Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Duck Typing with LinFu & C# 4.0's Dynamic Keyword

0.00/5 (No votes)
4 May 2011 1  
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.

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

// ..and the test would look like:
[Test]
public void CannotUseDynamicAdderForAnythingUseful()
{
    dynamic adder = new Dynamic();
    adder.Add = new Func<int, int, int>((first, second) => first + second);
    var somethingThatCanAdd = new SomethingThatAdds(adder); /* Fails here at runtime */
    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;
    };

    // Map LinFu's DynamicObject to an ICanAdd interface
    var linfuDynamicObject = new DynamicObject(new object());
    var returnType = typeof(int);
    var parameterTypes = new Type[] { typeof(int), typeof(int) };
    linfuDynamicObject.AddMethod("Add", addBody, returnType, parameterTypes);

    // If it looks like a duck...
    Assert.IsTrue(linfuDynamicObject.LooksLike<ICanAdd>());

    // ...then it must be a duck, right?
    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.)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here