C#:具有非多态类的动态多态

时间:2010-07-08 11:54:55

标签: c# inheritance dynamic

我有一组我没写过的课程,而且它们只读。 让我们说,例如,那些类是以下类:

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

我想在所有这些类上添加方法Foo(),我正在使用扩展方法:

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

}

正如您所看到的,我正在尝试使用关键字dynamic,期望它知道我的对象的真实类型并调用其Foo()方法。但是...... dynobj.Foo()总是失败。

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

我知道我可以使用适配器模式,但我的课程确实太多了。

使用dynamic执行此操作是个好主意吗?是否有可能使这项工作?

谢谢。

3 个答案:

答案 0 :(得分:5)

这是因为Foo永远不会被添加为方法。扩展方法仍然只是静态类中的静态方法,实际上并不直接与所讨论的类相关联。它们不像混合物(红宝石)。编译器只是将对象上的调用转换为静态方法。

与致电相同:Extensions.Foo(thing)

我最好的建议是创建一个查找表(Dictionary),您可以注册不同版本的Foo。

这样的东西(未经测试),也许?

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

}

答案 1 :(得分:1)

我解决了我的问题,只保留Base {Foo()扩展方法,并使其他扩展方法采用常规静态方法,将BCD作为参数。

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

答案 2 :(得分:0)

还有另一个原因,你的解决方案不是一个好主意 - Base没有被定义为抽象类,所以有可能有人可以实例化它,即使你的技术有效,也会遇到如果他们试图调用其Foo()方法,则堆栈溢出。

如果你拿走Base的Foo()方法,Foo()是不是按预期调用的(调用C类的Foo()扩展方法的实例最终会达到B的Foo()扩展方法)?

如果它不起作用,您可以始终保持老式并创建包装类。