Bertrand Marron
Bertrand Marron

Reputation: 22210

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.

Upvotes: 3

Views: 970

Answers (3)

Bertrand Marron
Bertrand Marron

Reputation: 22210

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

Upvotes: 1

48klocs
48klocs

Reputation: 6103

There's yet another reason why your solution wouldn't be a great idea - Base isn't defined as an abstract class, so there's a chance someone could instantiate it and, even if your technique worked, run into a stack overflow if they tried to call its Foo() method.

If you take away Base's Foo() method, does Foo() not get called as expected (does calling an instance of class C's Foo() extension method end up reaching B's Foo() extension method)?

If it doesn't work, you can always be old-fashioned and create wrapper classes.

Upvotes: 0

Brian Genisio
Brian Genisio

Reputation: 48147

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

}

Upvotes: 5

Related Questions