Devin Jenson's WebLog

That One Guy

Accessing Dynamic Generic Methods via Interfaces

It is possible to use the Reflection.Emit classes to emit generic methods on a dynamic type. Additionally, dynamic types can implement interfaces making it easy to access it's generic methods (or any of it's published members). Without interfaces, to call a generic method on a dynamic type, a call to MethodInfo.MakeGenericMethod() is required to bind the generic parameters to real type parameters. Only then can you call MethodInfo.Invoke() leading to code which can be difficult to read.

With interfaces you can simply make your method calls as you would with any interface. For example:


using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Threading; 
 
 
 
public interface MyInterface 
{ 
    Boolean MyEquals<T>(T x, T y); 
} 
 
 
 
class Program 
{ 
    private static void Main() 
    { 
        // Create a dynamic assembly 
        // --------------------------------------------------------------------- 
        AssemblyName assemblyName = new AssemblyName("DynamicAssembly"); 
 
        AssemblyBuilder assemblyBuilder = 
            AppDomain.CurrentDomain.DefineDynamicAssembly( 
            assemblyName, AssemblyBuilderAccess.RunAndSave); 
 
        ModuleBuilder moduleBuilder = 
            assemblyBuilder.DefineDynamicModule("DynamicAssembly.exe"); 
 
        // Create a type which inherits from MyInterface 
        // --------------------------------------------------------------------- 
        TypeBuilder typeBuilder = moduleBuilder.DefineType("MyType", 
            TypeAttributes.Public | TypeAttributes.Class); 
 
        typeBuilder.AddInterfaceImplementation(typeof(MyInterface)); 
 
        // Define the instance method: bool MyEquals<T>(T x, T, y) 
        // --------------------------------------------------------------------- 
        MethodBuilder methodBuilder = typeBuilder.DefineMethod( 
            "MyEquals", MethodAttributes.Public | MethodAttributes.Virtual, 
            CallingConventions.Standard); 
 
        GenericTypeParameterBuilder genericTypeParameterBuilder = 
            methodBuilder.DefineGenericParameters("T")[0]; 
 
        methodBuilder.SetReturnType(typeof(Boolean)); 
 
        methodBuilder.SetParameters(new Type[2] { 
            genericTypeParameterBuilder, genericTypeParameterBuilder } ); 
 
        // Generate a simple Object.Equals() wrapper 
        // ---------------------------------------------------------------------- 
        MethodInfo equalsMethodInfo = typeof(Object).GetMethod( 
            "Equals", BindingFlags.Public | BindingFlags.Static); 
 
        ILGenerator ilGenerator = methodBuilder.GetILGenerator(); 
 
        ilGenerator.Emit(OpCodes.Ldarg_1); 
        ilGenerator.Emit(OpCodes.Box, genericTypeParameterBuilder); 
        ilGenerator.Emit(OpCodes.Ldarg_2); 
        ilGenerator.Emit(OpCodes.Box, genericTypeParameterBuilder); 
        ilGenerator.Emit(OpCodes.Tailcall); 
        ilGenerator.EmitCall(OpCodes.Call, equalsMethodInfo, null); 
        ilGenerator.Emit(OpCodes.Ret); 
 
        // Create the type 
        // --------------------------------------------------------------------- 
        Type myType = typeBuilder.CreateType(); 
 
        MyInterface myInterface = (MyInterface)Activator.CreateInstance(myType); 
 
        // Verify the generated IL works (ugly code) 
        // --------------------------------------------------------------------- 
        MethodInfo methodInfo1 = myType.GetMethod("MyEquals"); 
 
        MethodInfo methodInfo2 = methodInfo1.MakeGenericMethod( 
            new Type[1] { typeof(Int32) } ); 
 
        Boolean result = (Boolean)methodInfo2.Invoke( 
            myInterface, new Object[2] { 5, 4 }); 
 
        result = (Boolean)methodInfo2.Invoke( 
            myInterface, new Object[2] { 5, 5 }); 
 
        // Verify the interface implementation (clean code) 
        // --------------------------------------------------------------------- 
        result = myInterface.MyEquals<int>(5, 4); 
        result = myInterface.MyEquals<int>(5, 5); 
 
        return; 
    } 
}

After saving the dynamic assembly, .NET Reflector shows us the code we generated:


.method public virtual instance bool MyEquals<T>(!!0, !!0) cil managed 
{ 
    // Code Size: 20 byte(s) 
    .maxstack 2 
    L_0000: ldarg.1 
    L_0001: box !!0 
    L_0006: ldarg.2 
    L_0007: box !!0 
    L_000c: tail 
    L_000e: call bool object::Equals(object, object) 
    L_0013: ret 
}

Or the C# view:


public override bool MyEquals<T>(T local1, T local2) 
{ 
    return object.Equals(local1, local2); 
}

Published Monday, July 11, 2005 8:34 PM by devinj

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Submit

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker