Skip to main content

Duck Typing with Anonymous Types

If you can create an anonymous method and an anonymous type, why not an 'anonymous class'? Wouldn't it be nice to be able to plug properties and methods into and object and then consume that object like any other class or interface?

What we're talking about is essentially duck typing (with strongly-typed ducks). There are examples all over of how to do this, especially with Castle Dynamic Proxy. This is my implementation of the same.

First let's look at a test:

public interface IDuck
{
    string Color { get; }
    Direction Fly();
    Direction Fly(Direction direction);
    string Quack();
}

[Test]
public void CanStubColorProperty()
{
    var rubberDuck = new { Color = "yellow" };
    var typedRubberDuck = rubberDuck.As<IDuck>();

    Assert.That(typedRubberDuck.Color == "yellow");
}

That As<IDuck> part is an extension method that does nothing right now, so this test will fail. What we want that extensions method to do is to return a proxy that implements the IDuck interface and forwards the method/property calls on to our anonymous type instance. Using Castle Dynamic Proxy, we can start creating this:

internal class DuckTypingInterceptor : IInterceptor
{
    private readonly object anonymous;

    public DuckTypingInterceptor(object anonymous)
    {
        this.anonymous = anonymous;
    }

    public void Intercept(IInvocation invocation)
    {
        if (invocation.IsGetter())
        {
            var property = invocation.PropertyName();
            invocation.ReturnValue = anonymous.GetPropertyValue(property);
        }
    }
}

There are some extension methods in there to help with reading method names and reflection, but it's pretty simple - when someone tries to get a property value from the proxy, we give back the value of the property on the anonymous object. Now we can implement that As<TDuck> extensions method:

public static TDuck As<TDuck>(this object anonymous) where TDuck : class
{
    var proxyGenerator = new ProxyGenerator();
    var objectImplementingDuckInterface =
        proxyGenerator.CreateInterfaceProxyWithoutTarget(typeof(TDuck), new DuckTypingInterceptor(anonymous));

    return objectImplementingDuckInterface as TDuck;
}

Now the test passes! Great, now what if that color property had a setter? Let's whip up some code to set the property on that anonymous type instance too:

[ ... some code that attempts to forward a setter invocation to the anonymous object via reflection ... ]
[Test]
public void CanSetColorProperty()
{
    var lameDuck = new { Color = "whocares" };
    var typedLameDuck = lameDuck.As<IDuck>();

    typedLameDuck.Color = "blue";

    Assert.That("blue" == typedLameDuck.Color);
}

The problem is, anonymous types don't have setters on the their properties. So we'll just set the backing field, right? Nope... anonymous type properties have readonly backing fields. They are immutable. We'd really like our proxy to behave like the interface it supposedly implements, so we hack around a bit and come up with this:

internal class DuckTypingInterceptor : IInterceptor
{
    private readonly object anonymous;
    private readonly IDictionary<string, object> properties = new Dictionary<string, object>();

    public DuckTypingInterceptor(object anonymous)
    {
        this.anonymous = anonymous;
    }

    public void Intercept(IInvocation invocation)
    {
        if (invocation.IsGetter())
        {
            var property = invocation.PropertyName();
            invocation.ReturnValue = GetPropertyValue(property);
        }

        if (invocation.IsSetter())
        {
            properties[invocation.PropertyName()] = invocation.Arguments[0];
        }
    }

    private object GetPropertyValue(string property)
    {
        if (properties.ContainsKey(property))
        {
            return properties[property];
        }

        return anonymous.GetPropertyValue(property);
    }
}

Now we're mixing in a dictionary of property values with the mutable anonymous object instance to give the feel of a single object with IDuck capabilities.

So what about methods? Well it would be nice if we could construct anonymous types with lambas, like

var duck = new
{
    Color = "white",
    Quack = () => "Quack!"
};

but if we try that, the compiler will yell at us because we "Cannot assign lambda expression to anonymous type property". There is something we can do, however, and I'll cover that in my next post.

Comments

Popular posts from this blog

Enabling Globalization Invariant Mode for .NET Core App on Raspberry Pi Running LibreElec

I had an app I wanted to run on my Raspberry Pi 3 running LibreElec . In LibreElec you can install the dotnet core 2.2 runtime as an addon, and in Visual Studio you can compile for ARM processors with ‘Target Runtime’ set to ‘linux-arm’ in the publish profile. So, I published to a folder from VS using that profile, and I copied the output over to my RPi which had the dotnet runtime installed. I did a simple dotnet Whatever.dll to run the app (actually in this case, it was /storage/.kodi/addons/tools.dotnet-runtime/bin/dotnet Whatever.dll because of the way the addon is installed) and was met with this error: FailFast: Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. at System.Environment.FailFast(System.String) at System.Globalization.GlobalizationMode.GetGlobalizationInvariantMode() at System.Globalization.GlobalizationMode..cctor() at Syste

Migrating Hg Repos with hg-fast-export and Windows Subsystem for Linux

Introduction I prefer Mercurial (hg) to git . I don’t really have any reason for this preference - they both do the same thing, and the user experience for 90% of the use cases is the same. It probably comes from the conditions of the DVCS landscape when I started using these systems. Some of this may have been perception only, but it looked like this: GitHub didn’t have free private repos BitBucket did have free private repos BitBucket was very hg-friendly Joel Spolsky had an amazing tutorial that served as both a how-to for hg as well as a general intro to DVCS hg was much more Windows-friendly than git Since hg was written in python, I felt like extending it would be easier than doing so for git if I ever needed to (admittedly, this is a pretty ridiculous reason) hg felt like a more unified, “coherent” system than the very linux-y feeling git and its extensions (also pretty ridiculous) Where they differed, I liked the verbs hg used better than git’s counterparts

Serializing Anonymous Methods

I’m taking some time off from not blogging to do a little blogging. I hope this doesn’t inconvenience absolutely nobody. I was doing some [binary] serialization work recently when I came across a problem – I wanted to serialize objects with delegate fields that were populated with anonymous methods at runtime. To wit, I had types like this: public delegate void MakeMove(); public class AdrianPeterson { public int GameOneYards { get; set; } public Football Ball { get; set; } public MakeMove Move { get; set; } } Populated like this: var explicitDirections = new List<string> { "left", "right", "left" }; var ap = new AdrianPeterson(); var apName = ap.GetType().Name; ap.GameOneYards = 87; ap.Move = () => Moves.Weave(apName, explicitDirections); So, I pop a [ Serializable ] on AdrianPeterson (and Football ), and I’m set, right? Wrong. Wrong like getting away from running AP in the second half when the only receiving threat you have is bei