C #: динамический полиморфизм с неполиморфными классами - PullRequest
3 голосов
/ 08 июля 2010

У меня есть набор классов, которые я не писал, и они только для чтения.Скажем, для примера, что эти классы следующие:

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 ]

5 голосов
/ 08 июля 2010

Это потому, что Foo никогда не добавляется как метод.Методы расширения все еще являются просто статическими методами в статических классах, фактически не связанными непосредственно с рассматриваемым классом.Они не похожи на смешанные модули (рубин).Компилятор просто переводит то, что выглядит как вызов вашего объекта, в статический метод.

То же, что и вызов: Extensions.Foo(thing)

Мой лучший совет - создать таблицу поиска (Словарь), которуюможно зарегистрировать разные версии 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 голос
/ 08 июля 2010

Я вроде решил мою проблему, оставив только базовый метод расширения Foo() и сделав другие методы расширений обычными статическими методами, принимая B, C или D в качестве параметра.

 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);
  }
0 голосов
/ 08 июля 2010

Есть еще одна причина, по которой ваше решение не будет хорошей идеей - Base не определен как абстрактный класс, поэтому есть шанс, что кто-то может создать его экземпляр и, даже если ваша техника сработает, столкнуться со стекомпереполнение, если они пытались вызвать его метод Foo().

Если убрать метод Base Foo (), Foo () не вызывается должным образом (вызывает экземпляр метода расширения Foo () класса C)в конечном итоге вы достигнете метода расширения Foo () в B)?

Если это не сработает, вы всегда можете быть старомодным и создавать классы-обертки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...