Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a constructor call using Reflection Emit that passes a Func<> as a parameter

I'm hoping someone can point me in the right direction with the following problem.

I am working on a project where the types are generated using Reflection.Emit, all has been working fine until a requirement arose where I needed to pass a Func<> into a constructor of a new object as below.

public class SearchTerm : IEntity
{
   private readonly NavigationProperty<Item> _item;

   public SearchTerm()
   {
       _item = new NavigationProperty<Item>(() => ItemIds);
   }
   public string[] ItemIds { get; set; }
}

Using Linqpad I can see the IL output is as follows:

SearchTerm.<.ctor>b__0:
IL_0000:  ldarg.0     
IL_0001:  call        UserQuery+SearchTerm.get_ItemIds
IL_0006:  stloc.0     // CS$1$0000
IL_0007:  br.s        IL_0009
IL_0009:  ldloc.0     // CS$1$0000
IL_000A:  ret         

SearchTerm..ctor:
IL_0000:  ldnull      
IL_0001:  stloc.0     
IL_0002:  ldarg.0     
IL_0003:  call        System.Object..ctor
IL_0008:  nop         
IL_0009:  nop         
IL_000A:  ldarg.0     
IL_000B:  ldloc.0     
IL_000C:  brtrue.s    IL_001D
IL_000E:  ldarg.0     
IL_000F:  ldftn       UserQuery+SearchTerm.<.ctor>b__0
IL_0015:  newobj      System.Func<System.Collections.Generic.IEnumerable<System.String>>..ctor
IL_001A:  stloc.0     
IL_001B:  br.s        IL_001D
IL_001D:  ldloc.0     
IL_001E:  newobj      UserQuery<UserQuery+Item>+NavigationProperty`1..ctor
IL_0023:  stfld       UserQuery+SearchTerm._item
IL_0028:  nop         
IL_0029:  ret  

The problem I have is that I'm not sure how to define the delegate within IL.

I have tried the following

var method = typeBuilder.DefineMethod("func", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(IEnumerable<string>), Type.EmptyTypes);

var methodIl = method.GetILGenerator();
methodIl.Emit(OpCodes.Ldarg_0);
methodIl.Emit(OpCodes.Call, dictionary["get_ItemIds"]);
methodIl.Emit(OpCodes.Ret);

Then trying to create the delegate the following throws "Not Supported Exception" with the error "Derived classes must provide an implementation."

var funcType = typeof(Func<,>).MakeGenericType(typeBuilder, typeof(IEnumerable<string>));
method.CreateDelegate(funcType);

I have also tried using Delegate.CreateDelegate and DynamicMethod which both require the type to have been created before using them.

Any suggestion on what I might be doing wrong are greatly appreciated.

like image 491
SCB Avatar asked Nov 02 '22 22:11

SCB


1 Answers

If you want to invoke your code, you have to work with Types and MethodInfos, not TypeBuilders and MethodBuilders.

So, you need to CreateType() and then use that Type and its method:

var generatedType = typeBuilder.CreateType();

var funcType = typeof(Func<,>).MakeGenericType(
    generatedType, typeof(IEnumerable<string>));
var d = generatedType.GetMethod("func").CreateDelegate(funcType);
like image 123
svick Avatar answered Nov 09 '22 06:11

svick