简体   繁体   中英

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.

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

}

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM