Как использовать DuckTyping, когда реализации немного различаются?

Я делаю Portable Class Library (PCL) в.NET, и случается, что, пытаясь абстрагировать любое поведение, я сталкиваюсь с очень распространенным раздражением, что.NET Framework очень притягателен с его типами и интерфейсами. Обычно бывает, что тип не реализует никакого интерфейса, или когда он это делает, интерфейс является внутренним.

Когда у существующих типов есть совместимые методы (то же имя и подпись), это довольно просто: я использую ImpromptuInterface следующим образом:

nakedInstanceTheDoesNotImplementAnything.ActAs<MyBeautifulInterface>();

и я получаю именно то, что хочу. Прозрачный и удобный.

Но что делать, если некоторые методы немного отличаются?

  • Разные имена
  • Другой сайт вызова: один - метод получения собственности, а другой - метод
  • Некоторые методы отличаются, но легко адаптируются между ними с небольшими изменениями.

Обычно рекомендуется чистый ООП-подход, и нам предлагается создать и Adapter. Но когда вам нужно адаптировать сложную иерархию типов, это также может быть очень утомительным и сложным, даже больше, когда у вас есть ОГРОМНЫЕ классы, такие как UIElement, Control, FrameworkElement…

Вопрос в том, могу ли я заставить ImpromptuInterface преодолеть эти различия в типах для динамического создания адаптеров?

1 ответ

Решение

Так ImpromtuInterface использует DLR, в основном, когда вы вызываете ActLike(), он генерирует и кэширует прокси для этого интерфейса и оборачивает его вокруг вашего объекта.

public class Proxy:IMyInterface {

      dynamic target;

      public int Foo(){
           return (int)target.Foo()
      } 
}

Поскольку это динамический вызов, у вас фактически нет метода на вашей цели, если он и IDynamicMetaObjectProvider наиболее популярны для настройки System.Dynamic.DynamicObject,

public class RoughDynamicAdapter:DynamicObject{

    public override bool TryInvokeMember(InvokeMemberBinder binder,
                                        Object[] args,
                                        out Object result){

          if(binder.Name == "Foo"){
            result = /* do your own logic */
            return true;
          }
          result = null;
          return false;
    }
}

Но это большая работа, потому что вы должны обрабатывать неизмененные вызовы столько же, сколько измененные вызовы.

Есть несколько сборных DynamicObject в ImpromptuInterface что я перешел в отдельную библиотеку динамитов.

Один в частности, BaseForwarder звучит как то, что вы хотите, потому что вместо передачи всей логики, пересылка сообщения целевому объекту уже реализована в качестве базовой функциональности.

public class DynamicAdapter:Dynamitey.DynamicObjects.BaseForwarder {

     public DynamicAdapter(object target):base(target){
     }

     public override bool TryInvokeMember(InvokeMemberBinder binder,
                                         Object[] args,
                                        out Object result){
          var newName = binder.Name;
          if(newName == "Foo"){
             result = Dynamic.InvokeMember(CallTarget, "Bar", args)
             return true;
          }
          //else pass them method on as it was called
          return base.TryInvokeMember(binder, args, out result)
    }
}

Тогда использовать его было бы new DynamicAdapter(myObject).ActLike<IMyInterface>()

Другие вопросы по тегам