Как использовать 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>()