DynamicDuck: Duck Typing in a Dynamic World

When dynamics came to C#, I hoped that we'd be able to use interfaces to bridge the gap between dynamic & static typing.

For example, the following really should just work:

    public interface IUser
{
string Username { get; set; }
}
static class Sample
{
public static void Main()
{
dynamic test = new ExpandoObject();
test.Username = "jsmith";
AddUser(test);
}
public static void AddUser(IUser user)
{
Console.WriteLine("Adding user {0}", user.Username);
}
}

It compiles just fine, but you get the following at runtime:

   Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:'The best overloaded method match for 'DynamicDuck.Sample.AddUser(DynamicDuck.IUser)' has some invalid arguments'

Well, this just makes me grumpy.  So either I'm missing something...which is always possible. (If so, please let me know!)  Or this just isn't possible in C# 4.0.  Working under the assumption that it isn't possible, I started looking into Duck Typing.  I wasn't able to find anyone who had created a Duck Type Library for dynamics, so I took the DuckTaper source and retrofitted it to work with dynamics.

So, when you apply the DynamicDuck this code works.

AddUser(DynamicDuck.AsIf<IUser>(test));

How does it work?  DynamicDuck creates a class that implements the interface and proxies the calls back to the dynamic object. The current duck typing code currently just handles properties, but could be enhanced to support methods in the future. 

This functionality works perfectly for DTOs and marshaling data between JSON, XML, or dynamic languages.

Hopefully, this will just be "included" with .NET in the future.  But until then...there is DynamicDuck!

[ Download the source ]

 

3 Comments

  • ExpandoObject is sealed too - so to implement something yourself letting you implement an interface would require you to inherit from DynamicObject. You would have some work to do.

    Is it possible to implement what you have done as an extension method, as:
    AddUser(test.AsIf());

    would be more readable compared to:
    AddUser(DynamicDuck.AsIf(test));

    In the first example kind of just reads that you are passing test "as if" it were an "IUser".

    You should be able to implement as an extension method of ExpandoObject?

    Regards,

    Dave

  • Dave,
    Using an extension method was my first idea too. However, the runtime system didn't recognize the method. I think the compiler didn't perform the normal extension method magic since it assumed this method was in the dynamic world. To get extension methods to work, I had to cast the object first which made it less readable.
    -Brian

  • Just an update to my previous comment and to let you know that I swapped Clay out for your code. Reason - your code can be Serialized easily with JsonConvert.SerializeObject. I think this is because your code works with the ExpandoObject and Clay works with a Castle Proxy. I probably could of got Clay working, but your code worked first time and did what I wanted mainly casting a dynamic to a interface.

    So thanks again for a great post and thank you to Lars for his comment above that fixes the problem with returning value types.

Comments have been disabled for this content.