I’m a big fan of anonymous objects ( the ASP.Net MVC framework seems to beckon for their use, especially when working with asynchronous server calls that return objects to serialize in JSON ).  However, one of the drawbacks is that they can only really be defined & used within the context of a single method.  Because of this, if you need to call another function with the contents of the object, you’ll either have to create a custom object or settle with passing delegates or lambda’s in to that function.

I’ve frequently lamented to my fellow co-workers at Magenic that life would be oh-so-sweet if we were able to inherit interfaces with an anonymous object.  While I just assumed that I was at the mercy of the compiler team to make that happen, I realized that I can do it myself by dynamically building a type at runtime and populating it with the values inside my anonymous object.

As a proof of concept, I wrote the following code that creates a dynamic implementation of an interface, and then assigns the values of an anonymous object to that dynamic implementation.

The code is as follows ( commented for clarity! ):

public interface IRandom
{
    int Number { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            IRandom random = ConvertAnonymousTypeToInterface<IRandom>(new { Number = 3 });
            Console.WriteLine("Value of random = " + random.Number.ToString());
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }

        Console.ReadLine();
    }

    static T ConvertAnonymousTypeToInterface<T>(object data) where T : class
    {
        // A little error checking
        if (!typeof(T).IsInterface)
            throw new InvalidOperationException("T must be an interface");

        if (!typeof(T).IsPublic)
            throw new InvalidOperationException("T must be a public interface");

        string typeName = typeof(T).Name;

        // We need an assembly to generate our type in
        AssemblyName assemblyName = new AssemblyName("AnonymousInterfaceAssembly");
        assemblyName.Version = new Version("1.0.0.0");

        // We need a few builders before we can build a type:
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly
            (assemblyName, AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule
            ("AnonymousInterfaceAssembly", "AnonymousInterfaceAssembly.dll");
        TypeBuilder typeBuilder = moduleBuilder.DefineType
            ("AnonymousInterfaceAssembly." + typeName, TypeAttributes.Public, typeof(object));

        // Now we can get on with building an object that implements the interface.
        // First we'll tell the runtime we want to implement the interface passed in
        typeBuilder.AddInterfaceImplementation(typeof(T));

        // We'll be fine with a default constructor
        typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

        // Implement interface members :o

        // Pull the properties & loop through them
        PropertyDescriptorCollection interfaceProperties = 
            TypeDescriptor.GetProperties(typeof(T));

        foreach (PropertyDescriptor interfaceProperty in interfaceProperties)
        {
            // First we'll define a new field to hold our property
            FieldBuilder propertyFieldBuilder = typeBuilder.DefineField
                ("_" + typeName, interfaceProperty.PropertyType, FieldAttributes.Private);

            // Our get & set methods need a few attributes
            MethodAttributes getSetMethodAttributes = 
                MethodAttributes.Public | MethodAttributes.Virtual;

            // implementing our inteface requires a get method named 'get_PROPERTYNAME' 
            // ( where PROPERTYNAME is really the property name. )
            MethodBuilder propertyGetMethod = typeBuilder.DefineMethod(
                "get_" + interfaceProperty.Name, getSetMethodAttributes, 
                interfaceProperty.PropertyType, Type.EmptyTypes);
            
            ILGenerator getMethodGenerator = propertyGetMethod.GetILGenerator();

            // our get consists of loading the field & returning
            getMethodGenerator.Emit(OpCodes.Ldarg_0);
            getMethodGenerator.Emit(OpCodes.Ldfld, propertyFieldBuilder);
            getMethodGenerator.Emit(OpCodes.Ret);

            // that's it for the get method, on to the set
            MethodBuilder propertySetMethod = typeBuilder.DefineMethod(
                "set_" + interfaceProperty.Name, getSetMethodAttributes, 
                null, new Type[] { interfaceProperty.PropertyType });

            ILGenerator setMethodGenerator = propertySetMethod.GetILGenerator();

            // The code is basically the same ( just in the other direction ), 
            // however we'll have an actual argument coming in ( to set the value )
            setMethodGenerator.Emit(OpCodes.Ldarg_0);
            setMethodGenerator.Emit(OpCodes.Ldarg_1);
            setMethodGenerator.Emit(OpCodes.Stfld, propertyFieldBuilder);
            setMethodGenerator.Emit(OpCodes.Ret);

            // And we're finished!
        }

        // Let's build our object

        Type anonymousInterfaceType = typeBuilder.CreateType();

        // set values

        T instance = Activator.CreateInstance(anonymousInterfaceType) as T;

        foreach (PropertyDescriptor interfaceProperty in interfaceProperties)
        {
            PropertyInfo dataProperty = data.GetType().GetProperty(interfaceProperty.Name);
            object value = dataProperty.GetValue(data, null);

            // Find the set method we just built:
            MethodInfo setterMethod = anonymousInterfaceType.GetMethod(
                "set_" + interfaceProperty.Name);

            setterMethod.Invoke(instance, new object[] { value });
        }

        return instance;
    }
}

A couple things to note:

  • I created this code only as a proof that the concept, and there’s quite a bit of error checking left out ( and potentially quite a few bugs left in )
  • What I’ve written above is basically ‘mocked’ the interface, so the same thing could be achieved through the use of any of the various mocking frameworks available.

- Colin