生成动态类型以模拟 python 多重继承



我想在运行时构建一个动态类型,在一个动态类型中组合多个现有类型,以模拟以下python场景:

class A(object):  
def Confirm(self):
print("A")
class B(A):  
def Confirm(self):
super().Confirm()
print("B")
class C(A):  
def Confirm(self):
print("C")
super().Confirm()
class D(A):  
def Confirm(self):
print("D")
super().Confirm()
print("DD")
class E(B,C,D):  
pass

我得到了以下输出:

C
D
A
DD
B

因此,调用堆栈遵循 super(( 调用前后的代码顺序,在 super(( 调用之前执行所有代码,然后 super 调用自己,最后是 super(( 调用后代码的最后一部分。 我想在 c# 中模拟这种行为。

public class A
{
public virtual void Confirm()
{
Console.Writeline("A");
}
}
public class B : A
{
public virtual void Confirm()
{
base.Confirm();
Console.Writeline("B");
}
}
public class C : A
{
public virtual void Confirm()
{
Console.Writeline("C");
base.Confirm();      
}
}
public class D : A
{
public virtual void Confirm()
{
Console.Writeline("D");
base.Confirm();
Console.Writeline("DD");      
}
}
public class E : A
{
public virtual void Confirm()
{
// BEFORE base
Console.Writeline("C"); // C.Confirm() without base, only code before base.Confirm()
Console.Writeline("D"); // D.Confirm() without base only code before base.Confirm()
// BASE
base.Confirm(); // base itself (A)
// AFTER base
Console.Writeline("DD"); // return to D.Confirm() only the code after base.Confirm()
Console.Writeline("B"); // B.Confirm() have only a code after base.Confirm() call
}
}

我想我可以解决使用System.Reflection.Emit的问题,但我不知道这是否可能。

您的问题可以改写为"如何构建支持多重继承并在 CLR 上运行的编译器"。如果这真的是你想要走的路,我会从这里开始,尝试阅读布鲁姆提到的 VTable 内容。

还有一个 SO 问题,它显示了 MC++ 如何或多或少地通过将基类实例保存为派生类型的实例成员来模拟多重继承。但是请注意,在您的情况下,您必须以不同的方式解决菱形问题(继承自具有公共基类的两个或多个基类(,盲目应用该解决方案将导致A.Confirm()被调用三次。若要更改此设置,编译器必须首先平展继承层次结构,然后将包含调用 base 的所有方法拆分为多个方法。所以像这样:

public class A
{
public virtual void Confirm()
{
Console.Writeline("A");
}
}
public class B : A
{
public virtual void Confirm()
{
base.Confirm();
Console.Writeline("B");
}
protected void Confirm_Before1()
{
}
protected void Confirm_After1()
{
Console.WriteLine("B");
}
}
public class C : A
{
public virtual void Confirm()
{
Console.Writeline("C");
base.Confirm();      
}
protected void Confirm_Before1()
{
Console.Writeline("C");
}
protected void Confirm_After1()
{
}
}
public class D : A
{
public virtual void Confirm()
{
Console.Writeline("D");
base.Confirm();
Console.Writeline("DD");      
}
protected void Confirm_Before1()
{
Console.Writeline("D");
}
protected void Confirm_After1()
{
Console.WriteLine("DD");
}
}
public class E : A
{
private readonly B _baseB = new B();
private readonly C _baseC = new C();
private readonly D _baseD = new D();
public virtual void Confirm()
{
_baseB.Confirm_Before1();
_baseC.Confirm_Before1();
_baseD.Confirm_Before1();
base.Confirm();
_baseB.Confirm_After1();
_baseC.Confirm_After1();
_baseD.Confirm_After1();
}
}

假设的编译器必须分析继承层次结构,找到可从乘法继承类型访问的所有方法,将它们拆分为 N+1 个方法,其中 N 是其中的base调用数,并在重载方法中按顺序调用它们。

但是,如果您更改示例,事情会变得无限复杂。如果A有一个额外的方法FooB.Confirm()是:

public virtual void Confirm()
{
base.Foo();
Confirm.WriteLine("B");
}

C.Confirm()

public virtual void Confirm()
{
Confirm.WriteLine("C");
base.Foo();
}

你对E.Confirm()有什么期望?它会打电话Foo一两次吗?如果在B.Confirm()中有多个呼叫Foo,但在C.Confirm()中没有怎么办?我对 Python 如何处理这个问题一无所知,只需将B类更改为

class B(A):  
def Confirm(self):
super().Confirm()
print("B")
super().Confirm()

并调用E.Confirm结果

C
D
A
DD
B
C
D
A
DD

这对我来说看起来很莫名其妙,这也是MI如此困难的部分原因。

无论哪种方式,最终的答案是,如果你想要Python式的行为,你必须实现一个编译器,以一种或另一种方式寻找多重继承(很可能使用属性(,并转换代码以完全执行Python所做的工作。一个具体的解决方案远远超出了对SO问题的回答,但是如果您在实施这个疯狂的想法时遇到任何特定的麻烦,请随时询问另一个问题。

编辑:

作为公益广告,我认为我应该提到:如果这不仅仅是你正在做的一个有趣的练习/思想实验,而是你试图解决一个真正的问题 - 这不是办法。重新设计你的系统,使其不依赖于 MI,或者,如果你喜欢 Python MI,不要使用 C#。

我稍微重写了一下python

class A(object):
def Confirm(self):
print("A")
class B(A):
def Confirm(self):
print("B.Confirm ENTER")
super().Confirm() # E forces that it will call C.Confirm()
print("B.Confirm EXIT")
class C(A):
def Confirm(self):
print("C.Confirm ENTER")
super().Confirm() # E forces that it will call D.Confirm()
print("C.Confirm EXIT")
class D(A):
def Confirm(self):
print("D.Confirm ENTER")
super().Confirm()
print("D.Confirm EXIT")
class E(B,C,D):
pass
e = E()
e.Confirm()

这里是一个具有相同输出的 C# 代码

using System;
public class Program
{
public static void Main()
{
var e = new E();
e.Confirm();
}
}
class A
{
public virtual void Confirm()
{
Console.WriteLine("A");
}
}
class B : C
{
public override void Confirm()
{
Console.WriteLine("B.Confirm ENTER");
base.Confirm();
Console.WriteLine("B.Confirm EXIT");
}
}
class C : D
{
public override void Confirm()
{
Console.WriteLine("C.Confirm ENTER");
base.Confirm();
Console.WriteLine("C.Confirm EXIT");
}
}
class D : A
{
public override void Confirm()
{
Console.WriteLine("D.Confirm ENTER");
base.Confirm();
Console.WriteLine("D.Confirm EXIT");
}
}
class E : B
{
}

.NET 小提琴的实时取景

两个代码的输出

B.确认输入 C.确认输入 D.确认输入 一个 D.确认退出 C.确认退出 B.确认退出

最新更新