Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Reflection.Emit to instantiate Generic Type with Generic Parameters

my goal is to use reflection emit to construct generic type with the generic parameters of the created generic method so the end result of the created generic method is similar to

void DoSomeThing<T>(T arg){ 
    var list=new List<T>();
}

so what I need is the code used to emit this fragment of code

    new List<T>

and this is my try

        var _assemblyName = "asm.dll";
        var _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(_assemblyName), System.Reflection.Emit.AssemblyBuilderAccess.RunAndSave);
        // ApplyReflectionPermission(asm); 

        var _moduleBuilder = _assemblyBuilder.DefineDynamicModule("module", _assemblyName, true);


        var type = _moduleBuilder.DefineType("type");

        var method = type.DefineMethod("DoSomeThing", MethodAttributes.Public | MethodAttributes.Static);
        var genericPrms = method.DefineGenericParameters("T");
        method.SetParameters(genericPrms);
        method.SetReturnType(typeof(void));


        var il = method.GetILGenerator();
        var listType = typeof(List<>);
        var list_of_T = listType.MakeGenericType(genericPrms);


        il.DeclareLocal(list_of_T);
        var c = list_of_T.GetConstructor(new Type[0]);


        il.Emit(OpCodes.Newobj, c);
        il.Emit(OpCodes.Stloc, 0);
        il.Emit(OpCodes.Ret);
        type.CreateType();
        _assemblyBuilder.Save(_assemblyName);

the exception is in this line of code

var c = list_of_T.GetConstructor(new Type[0]);

and it is caused by this line of code

var list_of_T = listType.MakeGenericType(genericPrms);

the exception is

System.NotSupportedException: Specified method is not supported.
   at System.Reflection.Emit.TypeBuilderInstantiation.GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) 
   at System.Type.GetConstructor(BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)    
   at System.Type.GetConstructor(Type[] types)

and by digging in (MakeGenericType) method, it returns a new instanse of TypeBuilderInstantiation if any of the parameter types isn't a (RuntimeType)

the type TypeBuilderInstantiation is nothing but an empty implementation of the abstract type 'TypeInfo' [whis is an abstract impl. of the type 'Type'], which all of its method throwing not supported exception

my goal is not to create a method to return new List, it's more complicated than that, but my obstacle is the same as doing so.

thanx for helping.

like image 231
Ala'a .H Avatar asked Dec 24 '22 00:12

Ala'a .H


1 Answers

Yeah, there's definitely a trick to this. Indeed you cannot call any methods on the TypeBuilderInstantiation. Instead, TypeBuilder will let you get constructors for dependent types.

The GetConstructor method provides a way to get a ConstructorInfo object that represents a constructor of a constructed generic type whose generic type definition is represented by a TypeBuilder object.

https://msdn.microsoft.com/en-us/library/ms145822(v=vs.110).aspx

You first need the generic ConstructorInfo, gotten from typeof(List<>) in the usual way...

var listDefaultConstructor = listType.GetConstructor(new Type[0]);

and then instantiate it to your particular generic implementation:

var c = TypeBuilder.GetConstructor(list_of_T, listDefaultConstructor);

Whenever you would like to call a method on an instance of Type that represents an unconstructed/dependent type, instead look for a method with the same name in the Reflection.Emit hierarchy.


Among other things, this design pattern of passing in the generic version of the MethodInfo allows you to distinguish between calls to overloaded Class<T=int>.Foo(T) and Class<T=int>.Foo(int).

like image 169
Ben Voigt Avatar answered Feb 16 '23 02:02

Ben Voigt