다음을 통해 공유


How to Emit Generics

Reflection.Emit can emit Generics! Checkout here:

Here is a code sample that tells you how to emit various Generics. It is created as dev unit test by Chris King who implemented Reflection.Emit on Generics.

using System;
using System.Reflection;
using System.Reflection.Emit;

public class G<T> // this class is going to be defined in instance()
{
public static T F;
public static bool B;

    public static void Foo()
{
bool b = B;

        // Case(peverify bug): typeDef G<T> -> methodDef G<T>.Foo()
// Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>.Foo()
Foo();

        // Case: typeDef G<T> -> typeSpec G<object> -> memberRef G<T>.Foo()
G<object>.Foo();

        // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>()
new G<T>();

        new G<object>();
}

    public static void M<S>()
{
T t = F;
S s = default(S);

        object boxVar = t;

        object boxMVar = s;

        // Case: typedef G<T> -> typeSpec G<object> -> memberRef G<object>.M<S>() -> methodSpec G<object>.M<string>()
G<object>.M<string>();

        // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>.M<S>() -> methodSpec G<T>.M<string>()
M<string>();

        // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>.M<S>() -> methodSpec G<T>.M<S>()
M<S>();

        // Case: typeDef G<T> -> typeSpec G<object> -> memberRef G<object>.M<S>() -> methodSpec G<T>.M<S>()
G<object>.M<S>();
}
}

public class Entry
{
public static int Main()
{
Instance();
Reference();
Constrained();

        return 100;
}

    public static void Instance()
{
AssemblyName name = new AssemblyName("Instance");
AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mod = asmbuild.DefineDynamicModule("Instance.exe");

        TypeBuilder G = mod.DefineType("G", TypeAttributes.Public);
Type T = G.DefineGenericParameters("T")[0];
Type GObj = G.MakeGenericType(new Type[] { typeof(object) });

        FieldBuilder F = G.DefineField("F", T, FieldAttributes.Public);
FieldBuilder B = G.DefineField("B", typeof(bool), FieldAttributes.Public | FieldAttributes.Static);

        ConstructorBuilder Ctor = G.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
{
ILGenerator il = Ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
il.Emit(OpCodes.Ret);
}

        MethodBuilder Foo = G.DefineMethod("Foo", MethodAttributes.Public);
{
// G<object>.Foo()
MethodInfo GObjFoo = TypeBuilder.GetMethod(GObj, Foo);

            // new G<object>()
ConstructorInfo GObjCtor = TypeBuilder.GetConstructor(GObj, Ctor);

            ILGenerator il = Foo.GetILGenerator();
CompileButDontRunMe(il, B);

            il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, F);
il.Emit(OpCodes.Box, T);
il.Emit(OpCodes.Pop);

            il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Call, Foo); // G<T>.Foo()
il.Emit(OpCodes.Ldnull);
il.EmitCall(OpCodes.Call, Foo, null);

            il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Call, GObjFoo); // G<object>.Foo()
il.Emit(OpCodes.Ldnull);
il.EmitCall(OpCodes.Call, GObjFoo, null);

            il.Emit(OpCodes.Newobj, Ctor); // new G<T>()
il.Emit(OpCodes.Pop);

            il.Emit(OpCodes.Newobj, GObjCtor); // new G<object>()
il.Emit(OpCodes.Pop);

            il.Emit(OpCodes.Ret);
}

        MethodBuilder M = G.DefineMethod("M", MethodAttributes.Public);
{
Type S = M.DefineGenericParameters("S")[0];

            // G<object>.M<S>
MethodInfo GObjM = TypeBuilder.GetMethod(GObj, M);

            // G<object>.M<string>()
MethodInfo GObjMStr = GObjM.MakeGenericMethod(typeof(string));

            // G<T>.M<string>()
MethodInfo GMStr = M.MakeGenericMethod(typeof(string));

            ILGenerator il = M.GetILGenerator();
CompileButDontRunMe(il, B);
il.DeclareLocal(S);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Box, S);
il.Emit(OpCodes.Stloc, il.DeclareLocal(typeof(object)));
il.Emit(OpCodes.Pop);
il.DeclareLocal(T);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, F);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Call, GObjMStr); // G<object>.M<string>()
il.Emit(OpCodes.Ldnull);
il.EmitCall(OpCodes.Call, GObjMStr, null); // G<object>.M<string>()
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Call, GMStr); // G<T>.M<string>()
il.Emit(OpCodes.Ldnull);
il.EmitCall(OpCodes.Call, GMStr, null); // G<T>.M<string>()
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Call, M); // G<T>.M<S>()
il.Emit(OpCodes.Ldnull);
il.EmitCall(OpCodes.Call, M, null); // G<T>.M<S>()
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Call, GObjM); // G<object>.M<S>
il.Emit(OpCodes.Ldnull);
il.EmitCall(OpCodes.Call, GObjM, null); // G<object>.M<S>
il.Emit(OpCodes.Ret);
}

        Type rtG = G.CreateType();

        Console.WriteLine("Instance.exe");
asmbuild.Save("Instance.exe");

        object target = Activator.CreateInstance(rtG.MakeGenericType(typeof(object)), null);
rtG.MakeGenericType(typeof(object)).InvokeMember("Foo", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, target, null);
rtG.MakeGenericType(typeof(object)).GetMethod("M").MakeGenericMethod(typeof(string)).Invoke(target, null);
}

  
public static void Reference()
{
AssemblyName name = new AssemblyName("Reference");
AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mod = asmbuild.DefineDynamicModule("Reference.exe");

        TypeBuilder H = mod.DefineType("H", TypeAttributes.Public);
Type U = H.DefineGenericParameters("U")[0];
Type G = typeof(G<>);
Type GObj = typeof(G<object>);
Type GU = G.MakeGenericType(U);

        FieldBuilder B = H.DefineField("B", typeof(bool), FieldAttributes.Public | FieldAttributes.Static);

        MethodBuilder Baz = H.DefineMethod("Baz", MethodAttributes.Static | MethodAttributes.Public);
{
// G<object>.Foo()
MethodInfo GObjFoo = GObj.GetMethod("Foo");

            // G<object>.M<object>()
MethodInfo GObjMObj = GObj.GetMethod("M").MakeGenericMethod(typeof(object));

            // G<U>.Foo()
MethodInfo GFoo = G.GetMethod("Foo");
MethodInfo GUFoo = TypeBuilder.GetMethod(GU, GFoo);

            // G<U>.M<U>()
MethodInfo GM = G.GetMethod("M");
MethodInfo GUM = TypeBuilder.GetMethod(GU, GM);
MethodInfo GUMU = GUM.MakeGenericMethod(U);

            // G<object>.M<U>
MethodInfo GObjM = GObj.GetMethod("M");
MethodInfo GObjMU = GObjM.MakeGenericMethod(U);

            // new G<object>()
ConstructorInfo GObjCtor = GObj.GetConstructor(new Type[0]);

            // G<U>()
ConstructorInfo GCtor = G.GetConstructor(new Type[0]);
ConstructorInfo GUCtor = TypeBuilder.GetConstructor(GU, GCtor);

            // G<U>.F
FieldInfo GF = G.GetField("F");
FieldInfo GUF = TypeBuilder.GetField(GU, GF);

            // G<U>.B
FieldInfo GB = G.GetField("B");
FieldInfo GUB = TypeBuilder.GetField(GU, GB);

            // Nullable<int>()
Type Nullable = typeof(Nullable<>);
Type NullableInt = Nullable.MakeGenericType(typeof(int));
ConstructorInfo NullableIntCtor = NullableInt.GetConstructor(new Type[] { typeof(int) });

            ILGenerator il = Baz.GetILGenerator();
il.Emit(OpCodes.Ldloca, il.DeclareLocal(NullableInt));
il.Emit(OpCodes.Ldc_I4_3);
il.Emit(OpCodes.Call, NullableIntCtor);
CompileButDontRunMe(il, B);
il.Emit(OpCodes.Ldsfld, GUF); // G<U>.F
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldsfld, GUB); // G<U>.B
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Call, GObjFoo); // G<object>.Foo()
il.Emit(OpCodes.Call, GObjMU); // G<object>.M<U>()
il.Emit(OpCodes.Newobj, GObjCtor); // new G<object>()
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Newobj, GUCtor); // new G<U>()
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Call, GUFoo); // G<U>.Foo()
il.Emit(OpCodes.Call, GUMU); // G<U>.M<U>
il.Emit(OpCodes.Call, GObjMObj); // G<object>.M<object>()
il.Emit(OpCodes.Ret);
}

        Type rtH = H.CreateType();

        Console.WriteLine("Reference.exe");
asmbuild.Save("Reference.exe");

        rtH.MakeGenericType(typeof(object)).InvokeMember("Baz", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null);
}

    public static void Constrained()
{
AssemblyName name = new AssemblyName("Constrained");
AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mod = asmbuild.DefineDynamicModule("Constrained.exe");

        TypeBuilder IFoo = mod.DefineType("IFoo", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
MethodBuilder IFooM = IFoo.DefineMethod("IFooM", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract);

        TypeBuilder G = mod.DefineType("G", TypeAttributes.Public);
GenericTypeParameterBuilder T = G.DefineGenericParameters("T")[0];
T.SetInterfaceConstraints(IFoo);
Type GObj = G.MakeGenericType(new Type[] { typeof(object) });

        FieldBuilder F = G.DefineField("F", T, FieldAttributes.Public | FieldAttributes.Static);
FieldBuilder B = G.DefineField("B", typeof(bool), FieldAttributes.Public | FieldAttributes.Static);

        MethodBuilder Foo = G.DefineMethod("Foo", MethodAttributes.Static | MethodAttributes.Public);
{
ILGenerator il = Foo.GetILGenerator();
CompileButDontRunMe(il, B);
il.Emit(OpCodes.Ldsflda, F);
il.Emit(OpCodes.Constrained, T);
il.Emit(OpCodes.Callvirt, IFooM);
il.Emit(OpCodes.Ret);
}

        Type rtIFoo = IFoo.CreateType();
Type rtG = G.CreateType();

        Console.WriteLine("Constrained.exe");
asmbuild.Save("Constrained.exe");

        rtG.MakeGenericType(rtIFoo).InvokeMember("Foo", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null);
}

    public static void CompileButDontRunMe(ILGenerator il, FieldBuilder B)
{
il.Emit(OpCodes.Ldsfld, B);
Label label = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, label);
il.Emit(OpCodes.Ret);
il.MarkLabel(label);
}
}

Comments

  • Anonymous
    April 28, 2005
    I wounder - anybody was able to read this code sample entirely ?
  • Anonymous
    April 29, 2005
    Thanks for the comment.
    I am removing some part that are having similiar usages.
  • Anonymous
    July 27, 2005
    This is very useful - thanks.
  • Anonymous
    June 06, 2006
    I am trying to generate a proxy class on a Generic class... so the only missing part to your code is if i want something generated like this :

    public MyClass<T>
    {
      public MyClass() {}
    }

    Emitted class

    public MyEmittedClass<T> : MyClass<T>
    {
      public MyEmittedClass() : base() {}
    }

    Is it possible???
  • Anonymous
    November 11, 2006
    Sure you can do this. You may use the assemblyroundtrip tool I posted to see how this thing can get emitted. Sorry for the very late reply.