views:

119

answers:

3

Hello,

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.

+5  A: 

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

}
Brian Genisio
Oh.. ok, thank you. That's neat, but I wanted something a little bit more dynamic.
Bertrand Marron
I don't think you will find a dynamic solution to extension methods. :( I could be proven wrong by someone more creative than me, though :)
Brian Genisio
Yes, you're right. But I realized that Base's Foo() is enough since it will be available for every other type that somehow inherits from Base.
Bertrand Marron
A: 

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.

48klocs
Well, my bad, you're right, `Base` is an abstract class, I left it out when I simplified my example. Actually I really want `Base`'s `Foo()` method since it's the one that will do the dispatch.
Bertrand Marron
A: 

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