Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# : dynamic polymorphism with non polymorphic classes

I have a set of classes which I didn't write, and they're read only. Let's say, for the example, that those classes are the following ones:

public class Base { }
public class B : Base { }
public class C : B { }
public class D : Base { }

I want to add a method Foo() on all these classes, I am using extension methods:

public static class Extensions {

  public static void Foo(this Base obj) {
      dynamic dynobj = obj;
      try {
        dynobj.Foo();
      }
      catch (RuntimeBinderException ex) {
          Console.WriteLine(ex.Message);
      }
  }

  public static void Foo(this B b) {
      Console.WriteLine("Foo in B");
  }

  public static void Foo(this C c) {
      Console.WriteLine("Foo in C");
  }

}

As you can see, I'm trying to use the keyword dynamic, expecting it know the real type of my object and call its Foo() method. But... dynobj.Foo() always fails.

static void Main(string[] args) {
    List<Base> list = new List<Base>();
    list.Add(new B());
    list.Add(new C());
    list.Add(new D());

    list.ForEach(x => x.Foo());
}

I know I could use the Adaptor pattern, but I really have too many classes.

Is it a good idea to do that with dynamic? Is it possible to make this work?

Thank you.

like image 560
Bertrand Marron Avatar asked Dec 06 '25 00:12

Bertrand Marron


2 Answers

This is because Foo never gets added as a method. Extension methods are still just static methods in static classes, not actually associated directly with the class in question. They are not like mix-ins (ruby). The compiler just translates what looks like a call on your object to the static method.

Same as calling: Extensions.Foo(thing)

My best advice is to create a lookup table (Dictionary) that you can register the different versions of Foo.

Something like this (untested), perhaps?

public static class Extensions {

  private static Dictionary<Type, Action<Base>> callFoo = new Dictionary<Type, Action<Base>>
  {
    {typeof(B), b => (b as B).Foo()},
    {typeof(C), b => (b as C).Foo()}
  };

  public static void Foo(this Base obj) {
      try {
        callFoo[typeof(obj)](obj);
      }
      catch (RuntimeBinderException ex) {
          Console.WriteLine(ex.Message);
      }
  }

  public static void Foo(this B b) {
      Console.WriteLine("Foo in B");
  }

  public static void Foo(this C c) {
      Console.WriteLine("Foo in C");
  }

}
like image 169
Brian Genisio Avatar answered Dec 07 '25 14:12

Brian Genisio


I sort of resolved my problem by keeping only Base' Foo() extension method and making the other extensions methods regular static methods taking B, C or D as a parameter.

 public static void Foo(this Base obj) {
      try {
        Foo_(obj as dynamic);
      }
      catch (RuntimeBinderException ex) {
          Console.WriteLine(ex.Message);
      }
  }

  private static void Foo_(B b) {
      Console.WriteLine("Foo in B");
      b.ListOfBs.ForEach(x => x.Foo());
  }

  private static void Foo_(C c) {
      Console.WriteLine("C.Name = {0}", c.Name);
  }
like image 32
Bertrand Marron Avatar answered Dec 07 '25 13:12

Bertrand Marron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!