Introduction
In the first part
of this article, I showed how to effectively inherit public methods from
additional parent classes. However, in the conclusion to that article I said
there were a number of gaps in the implementation which I'd like to address in
this second part.
Gaps in the implementation
OK, what are the areas still to be covered?
1. Child isn't really a Mother (no pun intended!). For example, we can't do any
of these which would work if Child 'really' inherited from Mother:
A. Mother m = new Child();
B. Mother m = (Mother)new Child();
C. Mother m = new Child() as Mother;
D. If (c is Mother) Console.WriteLine("Child is
a Mother")
2. We can't inherit protected methods.
3. If Mother inherited some interfaces, then Child would not automatically do
so.
4. Having to change the name of the inherited version of Mother's MethodA to
MethodD is not very satisfactory.
5. There is no way to tell by looking at the header of the Child class that it
'multiply inherits' Mother.
Of these, 1(a) and 1(b) are easy to deal with. All we need to do is provide
overrides of the 'implicit' and 'explicit' operators in the Child class.
However, this won't help with 1(c) and 1(d) which ignore custom conversions.
Suppose instead we implement an interface (IMother say) which has no members but
is just a 'marker' interface. This would deal with 1(c) and 1(d) and also 5.
We can also deal with 2. quite easily because protected members are visible to
our private nested class NMother which can then expose them (via a public
member) to the Child class itself. Incidentally, this public member would not be
accessible to classes other than Child because its accessibility is limited by
the nested class itself being private.
3. can be addressed by including the interfaces in Child's implementation list -
the members themselves will already be implemented.
Finally, 5.could be dealt with by adding those members whose names are
duplicated to the IMother interface and then implementing them 'explicitly'.
They could then be accessed using their original names via an IMother reference.
So, this gives us the following code:
using
System;
interface
IFace
{
void MethodB();
}
class
Father
{
public void
MethodA()
{
Console.WriteLine("Hello
from Father's MethodA");
}
}
class
Mother : IFace
// implements IFace
{
public void
MethodA()
{
Console.WriteLine("Hello
from Mother's MethodA");
}
public void
MethodB() // implements IFace.MethodB|
{
Console.WriteLine("Hello
from Mother's MethodB");
}
public static
void MethodS() //
static method
{
Console.WriteLine("Hello
from Mother's static MethodS");
}
public virtual
void MethodV() //
virtual method
{
Console.WriteLine("Hello
from Mother's MethodV");
}
protected void
MethodP() // Protected method
{
Console.WriteLine("Hello
from Mother's protected MethodP");
}
}
partial
class Child :
Father, IMother,
IFace // implements
IMother and IFace
{
public void
MethodC()
{
Console.WriteLine("Hello
from Child's MethodC");
}
}
partial
class Child :
Father, IMother,
IFace // contains
Mother inheritance stuff
{
private NMother
base2; // contains NMother
reference
public Child()
{
base2 = new
NMother();
base2.Outer = this;
// set NMother's Outer property
}
// private nested class which inherits from Mother
private
class NMother :
Mother
{
// holds reference to outer Child object
public
Child Outer { get;
set; }
// overrides the virtual method Mother.MethodV
public
override void MethodV()
{
Console.WriteLine("Hello
from Child's MethodV");
}
// exposes protected MethodP to outer Child
public
void MethodP_Expose()
{
MethodP();
}
}
// wraps Mother.MethodB
public void
MethodB()
{
base2.MethodB();
}
// wraps static method Mother.MethodS
public
static void MethodS()
// static method
{
NMother.MethodS();
}
// wraps override of Mother.MethodV|
public
virtual void MethodV()
{
base2.MethodV();
}
// wraps protected method Mother.MethodP and exposes
it as public (not necessary)
public void
MethodP()
{
base2.MethodP_Expose();
}
// implicit conversion from Child to Mother
public
static implicit
operator Mother(Child
c)
{
return c.base2;
}
public static
explicit operator
Child(Mother
m)
{
if (m is
NMother)
{
return ((NMother)m).Outer;
}
else
{
string message =
"Unable to cast object of type '" + m.GetType()
+ "' to type 'Class' or a type derived therefrom.";
throw new
InvalidCastException(message);
}
}
void IMother.MethodA()
// explicitly implements IMother.MethodA
{
base2.MethodA();
}
}
class
GrandChild :
Child
{
// further overrides Mother.MethodV
public
override void MethodV()
{
Console.WriteLine("Hello
from GrandChild's MethodV");
}
}
interface
IMother
{
void MethodA(); //
method with duplicated name
}
class
Test
{
static void
Main()
{
Child c =
new Child();
c.MethodA(); // calls MethodA inherited from
Father
c.MethodB();
c.MethodC();
((IMother)c).MethodA();
// calls MethodA inherited from Mother
Child.MethodS();
c.MethodV();
c.MethodP();
c = new
GrandChild();
c.MethodV();
Mother m =
new Child();
// implicit conversion
m.MethodV(); // calls Child's version
of MethodV
Child c2 = (Child)m;
// explicit conversion
c2.MethodV(); // calls Child's version
of MethodV
IMother im =
new Child()
as IMother;
im.MethodA(); // calls Mother's MethodA
if (c is
IMother) ((IMother)c).MethodA();
// calls Mother's MethodA
((IFace)c).MethodB();//
calls Mother's MethodB via an IFace reference
Console.ReadKey();
}
}
When you build and run this code, the results should be:
Hello from Father's MethodA
Hello from Mother's MethodB
Hello from Child's MethodC
Hello from Mother's MethodA
Hello from Mother's static MethodS
Hello from Child's MethodV
Hello from Mother's protected MethodP
Hello from GrandChild's MethodV
Hello from Child's MethodV
Hello from Child's MethodV
Hello from Mother's MethodA
Hello from Mother's MethodA
Hello from Mother's MethodB
Conclusion
So we now have a reasonably complete implementation of multiple inheritance.
If we wanted another class (Child2 say) to 'multiply inherit' from Mother, then
all we need to do is to copy the partial class with the Mother stuff in it to a
partial class of Child2. Using partial classes in this way, enables us to keep
everything together and facilitate code reuse.