How to use DuckTyping when implementations vary slightly?

420 views Asked by At

I'm making a Portable Class Library (PCL) in .NET and it happens that, when trying to abstract any behavior, I face the very common annoyance that .NET Framework is very possessive with its types and interfaces. It's very usual to find a type doesn't implement any interface, or when it does, the interface is internal.

When existing types have compatible methods (same name and signature) it's rather easy: I have been using ImpromptuInterface like this:

nakedInstanceTheDoesNotImplementAnything.ActAs<MyBeautifulInterface>();

and I get exactly what I want. Transparent and handy.

But, what to do when some methods are slightly different?

  • Different names
  • Different call site: one is a property getter and the other is a method
  • Some methods that are different, but easily adaptable between them with minor modifications.

Normally, a pure OOP approach is recommended and we are told to create and Adapter. But when you have to adapt a complex hierarchy of types, this can be really tedious and complex as well, even more when you have HUGE classes like UIElement, Control, FrameworkElement…

The question is: Can I make ImpromptuInterface overcome those variations in the types to dynamically create adapters?

1

There are 1 answers

0
jbtule On BEST ANSWER

So ImpromtuInterface uses the DLR, basically when you call ActLike() it emits and caches a proxy for that interface, and wraps it around your object.

public class Proxy:IMyInterface {

      dynamic target;

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

Since it's a dynamic invocation, you don't actually have the method on your target if it's and IDynamicMetaObjectProvider the most popular to customize being 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;
    }
}

But this is a lot of work, because it you have to handle the non modified calls as much as the modified calls.

There are several prefab DynamicObjects in ImpromptuInterface that I've moved to a separate library Dynamitey.

One in particular, BaseForwarder sounds like what you want, because rather than handing all the logic, forwarding the message to a target object is already implemented as the base functionality.

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

Then to use it would be new DynamicAdapter(myObject).ActLike<IMyInterface>()