如果实现略有不同,如何使用DuckTyping?

时间:2014-02-06 20:43:30

标签: c# .net adapter impromptu-interface

我正在.NET中创建一个可移植类库(PCL),当我尝试抽象任何行为时,我遇到了.NET Framework 非常普遍的烦恼,它的类型和接口非常占有欲即可。通常发现一个类型没有实现任何接口,或者当它发生时,接口是内部的。

当现有类型具有兼容的方法(相同的名称和签名)时,它很容易:我一直在使用ImpromptuInterface:

nakedInstanceTheDoesNotImplementAnything.ActAs<MyBeautifulInterface>();

我完全得到了我想要的东西。透明而且方便。

但是,当某些方法略有不同时该怎么办?

  • 不同的名字
  • 不同的呼叫站点:一个是属性getter,另一个是方法
  • 有些方法不同,但很容易在它们之间进行适应性修改。

通常,建议采用纯OOP方法,并告知我们创建和适配器。 但是当你必须适应复杂的类型层次结构时,这可能非常繁琐且复杂,当你拥有像UIElement,Control,FrameworkElement这样的巨大类时更是如此......

问题是:我是否可以使ImpromptuInterface克服类型中的变化以动态创建适配器?

1 个答案:

答案 0 :(得分:2)

所以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我已移至另一个库Dynamitey

特别是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>()