Create a DynamicMethod to assign a value to a property?

This is a training exercise. I created a method that takes Foo and a string and sets property A. I used Reflector parsing to do the following emitting code. Disassembly is as follows:

.method private hidebysig static void Spork(class ConsoleTesting.Foo f, string 'value') cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: ldarg.1 L_0002: callvirt instance void ConsoleTesting.Foo::set_A(string) L_0007: ret } 

Ok, so I modeled my emit code after this:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Reflection.Emit; namespace ConsoleTesting { class Foo { public string A { get; set; } } class Program { static Action<Foo, string> GenMethodAssignment(string propName) { MethodInfo setMethod = typeof(Foo).GetMethod("get_" + propName); if (setMethod == null) throw new InvalidOperationException("no property setter available"); Type[] argTypes = new Type[] { typeof(Foo), typeof(String) }; DynamicMethod method = new DynamicMethod("__dynamicMethod_Set_" + propName, null, argTypes, typeof(Program)); ILGenerator IL = method.GetILGenerator(); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldarg_1); IL.Emit(OpCodes.Callvirt, setMethod); IL.Emit(OpCodes.Ret); method.DefineParameter(1, ParameterAttributes.In, "instance"); method.DefineParameter(2, ParameterAttributes.In, "value"); Action<Foo, string> retval = (Action<Foo, string>)method.CreateDelegate(typeof(Action<Foo, string>)); return retval; } static void Main(string[] args) { Foo f = new Foo(); var meth = GenMethodAssignment("A"); meth(f, "jason"); Console.ReadLine(); } } 

I get this exception:

 JIT Compiler encountered an internal limitation. 

What does this mean and how to fix it?

EDIT:

I thought maybe because the target method is private, but I'm not sure. On the DynamicMethod MSDN page:

The following code example creates DynamicMethod, which is logically associated with a type. This association gives him access to private members of this type.

+6
source share
3 answers

Ok, I figured it out. Change

 IL.Emit(OpCodes.Callvirt, setMethod); 

to

 IL.Emit(OpCodes.Call, setMethod); 

fixed it. I donโ€™t know why the showdown showed CallVirt, but yes.

+2
source share

An interesting problem. There are a few things you can do first. If you only need a delegate for the installer, you can use Delegate.CreateDelegate .

 Delegate.CreateDelegate(typeof(Action<Foo, string>),typeof(Foo).GetProperty("A").GetSetMethod()) as Action<Foo,String>; 

If you use .NET 3.5+, you can use expression trees, and I highly recommend exploring them with DynamicMethod, they have limited use in .NET 3.5 and almost no .NET 4. (TypeBuilder is still very useful).

 var targetExpression = Expression.Parameter(typeof(Foo),"target"); var valueExpression = Expression.Parameter(typeof(string),"value"); var expression = Expression.Lambda<Action<Foo,string>>( Expression.Call( targetExpression, typeof(Foo).GetProperty("A").GetSetMethod(), valueExpression ), targetExpression, valueExpression ); 

In .NET 4, you can write it a little prettier using Expression.Assign , but it is not much better.

Finally, if you really want to do this in IL, this will work.

  DynamicMethod method = new DynamicMethod("Setter", typeof(void), new[] { typeof(Foo), typeof(string) }, true); var ilgen = method.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Ldarg_1); ilgen.Emit(OpCodes.Callvirt, typeof(Foo).GetProperty("A").GetSetMethod()); ilgen.Emit(OpCodes.Ret); var action = method.CreateDelegate(typeof(Action<Foo,string>)) as Action<Foo,string>; 

I think your problem is that you are currently calling the set method actually getMethod, the error on this line is: MethodInfo setMethod = typeof(Foo).GetMethod("get_" + propName);

I have never assigned parameter attributes, but this can also be a problem.

+4
source share

What version of .NET do I need to run? Compiling a delegate from an expression tree will be much easier.

+1
source share

All Articles