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