2011-01-03 15 views
6

Dies ist eine Lernübung. Ich habe eine Methode erstellt, die ein Foo und eine Zeichenfolge verwendet und die A-Eigenschaft festlegt. Ich habe die Reflektordemontage verwendet, um den folgenden Code zu emittieren. Die Demontage wie folgt aussieht:DynamicMethod erstellen, um einer Eigenschaft einen Wert zuzuweisen?

.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 modellierte ich meine emit Code danach:

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

ich diese Ausnahme erhalten:

JIT Compiler encountered an internal limitation. 

Was die krunk tut, gemein und wie repariere ich es?

EDIT:

Ich dachte, vielleicht ist es, weil die Zielmethode ist privat, aber ich bin mir nicht so sicher. Von der DynamicMethod MSDN page:

Das folgende Codebeispiel erstellt eine DynamicMethod, die logisch einem Typ zugeordnet ist. Diese Zuordnung ermöglicht den Zugriff auf die privaten Member dieses Typs.

+0

Sie greifen auf die Methode "get_" zu; Ist das nur ein Tippfehler in deinem Beitrag? Beachten Sie, dass Sie auf die Eigenschaft nach Name zugreifen können und die GetSetMethod() -Methode für das PropertyInfo-Objekt verwenden können. Auf diese Weise verlassen Sie sich nicht auf die C# -Konvention von "get_" und "set_". –

+0

Ich korrigierte gerade einen Tippfehler. Die IL enthielt AStr anstelle von A, weil ich die Eigenschaft für den Zweck dieses Beitrags umbenannt hatte. – Amy

+0

Ah, ich habe den ersten Satz Ihres Kommentars falsch interpretiert. Du hast absolut recht. – Amy

Antwort

2

Nun, ich habe es herausgefunden. Ändern

IL.Emit(OpCodes.Callvirt, setMethod); 

zu

IL.Emit(OpCodes.Call, setMethod); 

fixiert es. Ich weiß nicht, warum die Demontage CallVirt zeigte, aber eh.

+0

Welche Version von .NET? In .NET4 gibt es eine Sicherheitsausnahme, um eine Methode aufzurufen, die sich auf einer Instanz befindet. –

1

In welcher .NET-Version muss das ausgeführt werden? Das Kompilieren eines Delegaten aus einem Ausdrucksbaum wäre viel einfacher.

+0

In .Net 3.5 können Sie keine Zuweisung in Ausdrucksbäumen vornehmen. – Amy

+0

Eh, da er nur eine Setter-Methode aufruft, könnte man es auch in .NET 3.5 machen. Gehen Sie einfach auf Expression.Call (target, setMethod, valueExpression). Es erscheint als 'target.set_Property (value)' im Tostring, aber es funktioniert. –

4

Interessantes Problem. Zuerst gibt es einige Dinge, die Sie tun können. Wenn Sie nur einen Delegaten für den Setter möchten, können Sie Delegate.CreateDelegate verwenden.

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

Wenn Sie .NET verwenden 3.5+ Sie Ausdrucksbäume verwenden kann, und ich empfehle sie über Dynamic lernen, haben sie Verwendung in .NET 3.5 begrenzt und fast keine .NET 4. (Typebuilder ist immer noch sehr nützlich obwohl).

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 können Sie schreiben, es mithilfe Expression.Assign leicht bisschen besser sein, aber es ist nicht viel besser.

Schließlich, wenn Sie es wirklich in IL tun wollen, funktioniert das.

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

Ich denke, das Problem ist, dass Sie zur Zeit die eingestellte Methode aufrufen tatsächlich die getMethod ist der Fehler auf dieser Linie ist :: MethodInfo setMethod = typeof(Foo).GetMethod("get_" + propName);

Ich habe noch nie Parameter Attribute zugeordnet sind, sondern dass auch sein ein Problem auch.

Verwandte Themen