如何在CLR中使用多重继承?

时间:2019-03-07 00:06:19

标签: .net clr multiple-inheritance cil

我遇到了一些类似this的资源,它们声称CLR实际上支持多重继承(多种基本类型)(但C#和其他语言不支持)。基于本文描述的方法,似乎它比直接支持更像是一个技巧,但我仍然想知道它应该如何工作。

如果创建自定义VTable并在VTFixup中使用它实际上使实现多继承成为可能,那么如何在CIL中实际实现它并使用它呢?

1 个答案:

答案 0 :(得分:0)

当前,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 ++编译器发出一个使用这些概念的值类型,并覆盖运算符以具有所需的语义。

构建一个可以满足您要求的编译器很有挑战性,但确实非常困难,因为您首先必须为所有新情况定义适当的行为(例如,考虑钻石问题),而好处可能是有限的。 使用接口而不是类,可能有助于避免多重继承。