Currently the CLR does not support multiple inheritance. However, it is proved (look at the standard c++) compiler that a compiler may emulate multiple inheritance even though it only supports single inheritance.
Indeed, this is what the MC++ does.
Ideally, you would at least need to:
- be able to declare multiple inheritance
- allow the type system to understand it
- resolve ambiguities when overriding methods
- handle constructors and finalizers
Multiple Inheritance Emulation
Suppose you want to have the class A that inherits from the classes B1 and B2. Say the classes are:
public class B1
{
public void MethodDeclaredInB1();
}
public class B2
{
public void MethodDeclaredInB2();
}
Conceptually, what the compiler (a generic compiler can do) can do the following under the hood:
Create a new type, say, A1 which is a simple object with two fields. The code might look like this:
public sealed class A1
{
public B1 B1;
public B2 B2;
}
Then, at compile time, transparently transform the calls by accessing the fields:
your high-level code
A a = new A();
a.MethodDeclaredInB1();
a.MethodDeclaredInB2();
can be turned in (don't consider the constructor for now):
A1 a = new A1();
a.B1.MethodDeclaredInB1();
a.B2.MethodDeclaredInB2();
Type System Management
This is tough, as the compiler cannot use the standard rules of the language, but needs to use helper methods emitted at compile time to perform the type checking.
your high-level code
Object o = new A();
B1 b = o as B1;
b.MethodDeclaredInB1();
can be turned into
Object o = new A1();
B1 b = AsOperator(o, typeof(B1));
b.MethodDeclaredInB1();
where the AsOperator
method could be a general purpose method the does this in pseudocode:
method AsOperator: instance i1 , type t1 -> returns instance of type t1
t2 <- get the runtime type of instance i1
if t2 is not a compiler generated object (e.g. A1) then
use the standard type system checking (this is trivial and we skip it here)
else
for each child type c1 in t2->parent classes
if c1 is subtype of t1 or it is exactly the same as t1 then return the corresponding field (this is a trivial task too) and we are done
no match, return null
the AsOperator
also requires having the CastOperator
(which does the same, but instead of returning null, it throws InvalidCastException
).
these new operators must be spread across the code as the compiler cannot always use static analysis to determine what is the content of an object instance.
Resolve ambiguities when overriding methods
This is a pain in the neck as you have to resolve problems like the Diamond Problem. Fortunately, this is a well-known problem, and you can find a solution (at least sub optimal).
When invoking inherited and instance methods, the compiler needs to patch the this
pointer to target the correct base class.
Handle constructors and finalizers
Constructors are finalizers are particular virtual/inherited methods. In particular, in C++ the type of the object being constructed/destructed changes over time, stopping at the end of the hierarchy. You have to face virtual method calls while constructing/destroying objects even though it is not good practice.
Side Notes
The MC++ compiler emits a value type that uses these concepts and overrides the operators in order to have the required semantics.
Building a compiler that does what you ask is challengingm interesting, but truly hard as you first have to define a proper behaviour for all the new cases (think of the diamond problem for example), while the benefit might be limited.
Using interfaces instead of classes, might help in avoiding multiple inheritance.