目前 CLR 不支持多重继承。然而,事实证明(查看标准 c++)编译器即使只支持单继承,编译器也可以模拟多重继承。
确实,这就是 MC++ 所做的。
理想情况下,您至少需要:
- 能够声明多重继承
- 让类型系统理解它
- 解决覆盖方法时的歧义
- 处理构造函数和终结器
多重继承仿真
假设您希望拥有从类 B1 和 B2 继承的类 A。说类是:
public class B1
{
public void MethodDeclaredInB1();
}
public class B2
{
public void MethodDeclaredInB2();
}
从概念上讲,编译器(通用编译器可以做的)可以在后台执行以下操作:
创建一个新类型,例如 A1,它是一个具有两个字段的简单对象。代码可能如下所示:
public sealed class A1
{
public B1 B1;
public B2 B2;
}
然后,在编译时,通过访问字段透明地转换调用:
你的高级代码
A a = new A();
a.MethodDeclaredInB1();
a.MethodDeclaredInB2();
可以上交(暂时不用考虑构造函数):
A1 a = new A1();
a.B1.MethodDeclaredInB1();
a.B2.MethodDeclaredInB2();
类型系统管理
这很难,因为编译器不能使用语言的标准规则,而是需要使用编译时发出的辅助方法来执行类型检查。
你的高级代码
Object o = new A();
B1 b = o as B1;
b.MethodDeclaredInB1();
可以变成
Object o = new A1();
B1 b = AsOperator(o, typeof(B1));
b.MethodDeclaredInB1();
AsOperator 方法可能是一种通用方法,它在伪代码中执行此操作:
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
AsOperator 也需要CastOperator(同样的,但不是返回 null,而是抛出 InvalidCastException)。
这些新运算符必须分布在代码中,因为编译器不能总是使用静态分析来确定对象实例的内容。
解决重写方法时的歧义
这是一个令人头疼的问题,因为您必须解决像Diamond Problem 这样的问题。幸运的是,这是一个众所周知的问题,您可以找到解决方案(至少是次优的)。
在调用继承方法和实例方法时,编译器需要修补 this 指针以定位正确的基类。
处理构造函数和终结器
构造函数是终结器,是特定的虚拟/继承方法。特别是,在 C++ 中,正在构造/销毁的对象的类型会随着时间而变化,在层次结构的末尾停止。在构造/销毁对象时,您必须面对虚拟方法调用,即使这不是好的做法。
附注
MC++ 编译器发出一个使用这些概念的值类型并覆盖运算符以获得所需的语义。
构建一个可以满足您要求的编译器具有挑战性,但确实很困难,因为您首先必须为所有新情况定义适当的行为(例如考虑菱形问题),而好处可能是有限的。
使用接口而不是类,可能有助于避免多重继承。