【问题标题】:How to use multiple inheritance in the CLR?如何在 CLR 中使用多重继承?
【发布时间】:2019-07-28 18:26:04
【问题描述】:

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

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

【问题讨论】:

  • @Joey Ref 类在 C++/CLI 中不允许多重继承,普通 C++ 类没有任何基类(它们是值类型)。
  • 我想这是可能的,您必须对 C++/CLI 编译器如何为使用 /clr 编译的本机 C++ 类进行逆向工程。元数据没用,你只得到了 MSIL 作为指南。
  • @HansPassant Native C++ 类被编译为值类型,并通过ldindcalli 从C++ vtable 加载正确的函数。

标签: .net clr multiple-inheritance cil


【解决方案1】:

目前 CLR 不支持多重继承。然而,事实证明(查看标准 c++)编译器即使只支持单继承,编译器也可以模拟多重继承。 确实,这就是 MC++ 所做的。

理想情况下,您至少需要:

  • 能够声明多重继承
  • 让类型系统理解它
  • 解决覆盖方法时的歧义
  • 处理构造函数和终结器

多重继承仿真

假设您希望拥有从类 B1B2 继承的类 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++ 编译器发出一个使用这些概念的值类型并覆盖运算符以获得所需的语义。

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

【讨论】:

  • 我知道有一些方法可以模拟多重继承,但这不是我所追求的。我对文章中概述的方法或其他涉及更多 CLR 功能的方法感兴趣。
  • 您提到的文章存在问题,例如在程序集验证期间。 C++ 团队使用了你已经知道的技术(我不知道你已经知道,我的错)这一事实对我来说很重要,因为即使是内部 MS 团队也选择了更安全的技术。考虑到 CLR 在技术上并非旨在提供该功能,因此朝该方向做任何事情都可能会在将来破坏您的应用程序。
猜你喜欢
  • 2020-03-20
  • 2016-07-09
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 2021-02-10
  • 1970-01-01
  • 1970-01-01
  • 2019-01-04
相关资源
最近更新 更多