0

I want to build a dynamic type at run-time to compose multiple existing types in one dynamic type to simulate the following python scenario:

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

I got the below outputs:

C
D
A
DD
B

so the call stack respects code order before and after super() call, executing all codes before super() call then super call itself finally the last part of code that comes after super() call. I want to simulate that behavior in 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
  }
}

i think i can solve the problem with using of the System.Reflection.Emit but i don't know if that possible.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Henka Programmer
  • 727
  • 7
  • 25
  • As of this writing, multi-inheritance is not supported in C#. – Bigabdoul Sep 22 '19 at 11:37
  • Use `call` instead of `callvirt`. This is a hack though. – dymanoid Sep 22 '19 at 11:38
  • You kinda never showed us the calls producing that output. As I read it, Python uses the "first match" variant of Diamond Problem Resolution. D, A, DD I get - it is just D.Confirm(). But how do C and B end up on that list? Much less in those *exact* positions? – Christopher Sep 22 '19 at 12:05

2 Answers2

2

Your question could be rephrased as "How do I build a compiler that supports multiple inheritance and runs on the CLR". If that's really the path you want to go down through, I'd start here and try to read up on the VTable stuff Brumme mentions.

There is also this SO question which shows how, more or less, MC++ emulates multiple inheritance by simply holding base class instances as instance members of the derived type. Note, however, that in your case you would have to solve the diamond problem (inheriting from two or more base classes that have a common base class) differently, applying that solution blindly will cause A.Confirm() to be called thrice. To change that, your compiler would have to first flatten the inheritance hierarchy and then split all methods containing calls to base into multiple methods. So something like this:

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();
  }
}

Your hypothetical compiler would have to parse the inheritance hierarchy, find all methods accessible from your multiply-inheriting type, split them into N+1 methods, where N is the number of base calls within them and call them all in order in your overloaded method.

Things get infintely more complicated if you change your example, however. What if A had an additional method Foo, B.Confirm() was:

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

and C.Confirm() was

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

What do you expect of E.Confirm()? Will it call Foo once or twice? And what if there were multiple calls to Foo in B.Confirm() but not in C.Confirm()? I have little idea on how Python handles this, it's enough to say that simply changing the B class to

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

and calling E.Confirm results in

C
D
A
DD
B
C
D
A
DD

That looks just baffling to me, which is a part of why MI is so hard.

Either way, the final answer is that if you want Python-esque behaviour, you'd have to implement a compiler that looks for multiple inheritance one way or another (most likely using an attribute) and transforms the code to do exactly what Python does. A concrete solution is way beyond an answer to an SO question, but if you run into any specific trouble implementing this crazy idea then feel free to ask another one.

EDIT:

As a PSA, I think that I should mention: if this is not just a fun exercise/thought experiment that you're doing, but rather you're trying to solve a real problem - this is not the way. Redesign your system to not rely on MI, or, if you're fond of the Python MI, don't use C#.

V0ldek
  • 9,623
  • 1
  • 26
  • 57
  • put in consideration that all parent classes are inherit from same one A, i haven't the code of all E parents to recompile it, for that reason i am looking to create one type inherit from all classes, if it is possible could we talk more about my idea? – Henka Programmer Sep 22 '19 at 12:40
1

I rewrote the python a little bit

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()

and here a C# code with the same output

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
{
}

live view at .net fiddle

output of both codes

B.Confirm ENTER
C.Confirm ENTER
D.Confirm ENTER
A
D.Confirm EXIT
C.Confirm EXIT
B.Confirm EXIT
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73