2011-01-03 10 views
6

Questo è un esercizio di apprendimento. Ho creato un metodo che accetta un Foo e una stringa e imposta la proprietà A. Ho usato lo smontaggio di Reflector per creare il seguente codice di emissione. Lo smontaggio è simile al seguente:Creare DynamicMethod per assegnare un valore a una proprietà?

.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, così ho modellato il mio codice Emit dopo che:

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

sto ottenendo questa eccezione:

JIT Compiler encountered an internal limitation. 

Che krunk lo fa significa, e come lo aggiusto?

EDIT:

Ho pensato che forse è perché il metodo di destinazione è privato, ma non ne sono così sicuro. Da DynamicMethod MSDN page:

L'esempio di codice seguente crea un metodo DynamicMethod associato logicamente a un tipo. Questa associazione dà accesso ai membri privati ​​di quel tipo.

+0

Stai accedendo al metodo "get_"; è solo un refuso nel tuo post? Si noti che è possibile accedere alla proprietà per nome e utilizzare il metodo GetSetMethod() sull'oggetto PropertyInfo; in questo modo non stai facendo affidamento sulla convenzione C# di "get_" e "set_". –

+0

Ho fatto, tuttavia, correggere un errore di battitura proprio ora. L'IL conteneva AStr, invece di A, perché avevo rinominato la proprietà ai fini di questo post. – Amy

+0

Ah, ho erroneamente interpretato la prima frase del tuo commento. Hai assolutamente ragione. – Amy

risposta

2

Beh, l'ho capito. Cambiando

IL.Emit(OpCodes.Callvirt, setMethod); 

a

IL.Emit(OpCodes.Call, setMethod); 

riparato. Non so perché lo smontaggio abbia mostrato CallVirt, ma eh.

+0

Quale versione di .NET? In .NET4 fornisce un'eccezione di sicurezza per provare a chiamare un metodo che si trova su un'istanza. –

1

Quale versione di .NET deve eseguire su questo? Compilare un delegato da un albero di espressioni sarebbe molto più semplice.

+0

In .Net 3.5 non è possibile eseguire assegnazioni negli alberi di espressione. – Amy

+0

Eh dal momento che tutto ciò che sta facendo è chiamare un metodo setter che potresti fare anche in .NET 3.5. Basta andare su "Expression.Call (target, setMethod, valueExpression)". Apparirà come 'target.set_Property (value)' nel tostring ma funzionerà. –

4

Interessante. Innanzitutto ci sono poche cose che puoi fare. Se si desidera un delegato solo per il setter, è possibile utilizzare Delegate.CreateDelegate.

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

Se si utilizza .NET 3.5+ è possibile utilizzare alberi di espressione e lo consiglio vivamente loro apprendimento nel corso DynamicMethod, hanno limitato l'uso in .NET 3.5 e quasi nessuno .NET 4. (TypeBuilder è ancora molto utile anche se).

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 si può scrivere che sia un po 'più bella utilizzando Expression.Assign, ma non è molto meglio.

Infine, se si vuole davvero farlo in IL funziona.

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

Credo che il tuo problema è che si sta chiamando il metodo set è in realtà il getMethod l'errore è su questa linea :: MethodInfo setMethod = typeof(Foo).GetMethod("get_" + propName);

ho mai assegnato attributi dei parametri, ma che può anche essere anche un problema

Problemi correlati