The Lame Duck
When C# 4.0 came out with the dynamic
keyword, I was pretty excited over the prospect of having Ruby-like features finally being baked in to the C# language itself, but as one of my Twitter followers and friends pointed out in one of their posts from a few years ago, C# 4.0 still lacks duck typing support, and the .NET BCL doesn't seem to have anything that could do something similar to the following code:
public interface ICanAdd
{
int Add(int a, int b);
}
public class SomethingThatAdds
{
private ICanAdd _adder;
public SomethingThatAdds(ICanAdd adder)
{
_adder = adder;
}
public int FirstNumber { get; set; }
public int SecondNumber { get; set; }
public int AddNumbers()
{
return _adder.Add(FirstNumber, SecondNumber);
}
}
There has to be some way to construct an object at runtime and map it to an ICanAdd
interface, but the problem is that the current .NET Base Class Libraries don't seem to have a solution for this problem. As Dave Tchepak pointed out in his post, the following dynamic code will fail miserably at runtime:
public class Dynamic : DynamicObject
{
Dictionary<String, object> members = new Dictionary<string, object>();
public override bool TrySetMember(SetMemberBinder binder, object value)
{
members[binder.Name] = value;
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return members.TryGetValue(binder.Name, out result);
}
}
[Test]
public void CannotUseDynamicAdderForAnythingUseful()
{
dynamic adder = new Dynamic();
adder.Add = new Func<int, int, int>((first, second) => first + second);
var somethingThatCanAdd = new SomethingThatAdds(adder);
somethingThatCanAdd.FirstNumber = 10;
somethingThatCanAdd.SecondNumber = 20;
Assert.That(somethingThatCanAdd.AddNumbers(), Is.EqualTo(30));
}
The problem is that the runtime isn't smart enough to figure out that there has to be a duck-typing cast to the ICanAdd
interface in order to use the SomethingThatCanAdd
class, and that's where LinFu's DynamicObject
comes in handy.
If It Walks and Quacks like a Duck, Then It's All Good
LinFu.DynamicObject
is flexible enough that it can let you build object instances at runtime and then 'strongly' duck type those object instances to any interface that matches the intended duck type. In this case, we need to find a way to build up something that can map to an ICanAdd
interface
instance so that it can be used by the SomethingThatAdds
class:
[Test]
public void CanCreateADynamicAdder()
{
var adder = new DynamicObject();
CustomDelegate addBody = delegate(object[] args)
{
int a = (int)args[0];
int b = (int)args[1];
return a + b;
};
var linfuDynamicObject = new DynamicObject(new object());
var returnType = typeof(int);
var parameterTypes = new Type[] { typeof(int), typeof(int) };
linfuDynamicObject.AddMethod("Add", addBody, returnType, parameterTypes);
Assert.IsTrue(linfuDynamicObject.LooksLike<ICanAdd>());
var somethingThatCanAdd = new SomethingThatAdds(adder.CreateDuck<ICanAdd>());
somethingThatCanAdd.FirstNumber = 10;
somethingThatCanAdd.SecondNumber = 20;
Assert.AreEqual(somethingThatCanAdd.AddNumbers(), 30);
}
The call to the DynamicObject.CreateDuck()
method does all the heavy lifting for you so you don't have to worry about the details of how to make the object behave like a duck. It just works, and that's the power that LinFu offers.
(Edit: You can grab the source code and examples for LinFu.DynamicObject
here at Github.)