2012-03-12 5 views
3

理想的には、CreateObjectデリゲートを使用したいのですが、これらの動的メソッドを生成するコードは任意の型(少なくともプリミティブ、構造体、&クラスインスタンス)を処理できるデシリアライザ用です。しかし、CreateObjectデリゲートタイプの問題が発生しましたので、デバッグのためにCreateRectangleデリゲートを試してみることにしました。私はもう少し働いている解決策に近づいたが、他の何かが正しくない。どちらのケースでも私のコードに何が間違っていますか?つまり、動的メソッドをCreateObjectCreateRectangleの両方で動作させるにはどうすればよいですか?または、私の呼び出しコードが原因ですか?Reflection.Emit.DynamicMethodで放射しているILの何が問題ですか?

出力:

{X=0,Y=0,Width=0,Height=0} 

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type. 

Exception has been thrown by the target of an invocation. 
    Common Language Runtime detected an invalid program. 

コード:

using System; 
using System.Drawing; 
using System.Reflection.Emit; 

namespace Experiments { 
    public class Program { 
     private delegate object CreateObject(); 
     private delegate Rectangle CreateRectangle(); 

     static void Main() { 
      Console.WriteLine(new Rectangle()); 
      Console.WriteLine(); 
      var dm = BuildDynamicMethod(); 
      TryCreateDelegate<CreateObject>(dm); 
      Console.WriteLine(); 
      TryCreateDelegate<CreateRectangle>(dm); 
      Console.WriteLine(); 
      Console.ReadKey(); 
     } 

     private static void TryCreateDelegate<T>(DynamicMethod dm) { 
      try { 
       var co = dm.CreateDelegate(typeof (T)); 
       var value = co.DynamicInvoke(null); 
       Console.WriteLine(value); 
      } catch (Exception ex) { 
       Console.WriteLine(ex.Message); 
       var indent = 0; 
       while (ex.InnerException != null) { 
        indent++; 
        ex = ex.InnerException; 
        Console.WriteLine(new string('\t', indent) + ex.Message); 
       } 
      } 
     } 

     private static DynamicMethod BuildDynamicMethod() { 
      var tr = typeof(Rectangle); 
      var dm = new DynamicMethod("buildNewRectangle", tr, Type.EmptyTypes); 
      var il = dm.GetILGenerator(); 
      il.Emit(OpCodes.Ldloca_S, (byte)0); 
      il.Emit(OpCodes.Initobj, tr); 
      il.Emit(OpCodes.Ldloc_0); 
      il.Emit(OpCodes.Box, tr); 
      il.Emit(OpCodes.Ret); 
      return dm; 
     } 
    } 
} 

答えて

3
private delegate object CreateObject(); 
private delegate Rectangle CreateRectangle(); 

static void Main() 
{ 
    Console.WriteLine(new Rectangle()); 
    Console.WriteLine(); 
    TryCreateDelegate<CreateObject>(BuildDynamicMethod_Boxed()); 
    Console.WriteLine(); 
    TryCreateDelegate<CreateRectangle>(BuildDynamicMethod()); 
    Console.WriteLine(); 
} 

private static DynamicMethod BuildDynamicMethod_Boxed() 
{ 
    var TRect = typeof(Rectangle); 
    var dm = new DynamicMethod("buildNewRectangle", typeof(object), Type.EmptyTypes); 
    var il = dm.GetILGenerator(); 
    il.Emit(OpCodes.Ldloca_S, il.DeclareLocal(TRect)); 
    il.Emit(OpCodes.Initobj, TRect); 
    il.Emit(OpCodes.Ldloc_0);  
    il.Emit(OpCodes.Box, TRect); 
    il.Emit(OpCodes.Ret); 
    return dm; 
} 

private static DynamicMethod BuildDynamicMethod() 
{ 
    var TRect = typeof(Rectangle); 
    var dm = new DynamicMethod("buildNewRectangle", TRect, Type.EmptyTypes); 
    var il = dm.GetILGenerator(); 
    il.Emit(OpCodes.Ldloca_S, il.DeclareLocal(TRect)); 
    il.Emit(OpCodes.Initobj, TRect); 
    il.Emit(OpCodes.Ldloc_0); 
    il.Emit(OpCodes.Ret); 
    return dm; 
} 

private static void TryCreateDelegate<T>(DynamicMethod dm) 
{ 
    try 
    { 
    var co = dm.CreateDelegate(typeof(T)); 
    var value = co.DynamicInvoke(); 
    } 
    catch (Exception ex) 
    { 
    Console.WriteLine(ex.Message); 
    var indent = 0; 
    while (ex.InnerException != null) 
    { 
     indent++; 
     ex = ex.InnerException; 
     Console.WriteLine(new string('\t', indent) + ex.Message); 
    } 
    } 
} 
+0

+1、受け入れます。素晴らしい作品!どうもありがとう! –

3

有効ILCodeであるかを理解するための最も簡単な方法は、C#のコードを書くことから始め、それをコンパイルして、読んでみることです、私を見つけました逆コンパイルされたILCodeそれは非常に有益なことができます。

しかし、アドレス空間を宣言せずにローカル変数を参照しようとしているようです。 LdLoca_Sはローカル変数を参照しています。その前に、.DeclareLocal(tr)を呼び出す必要があります。それが唯一の問題であるかどうかは分かりませんが、最も明白な問題です。

編集:OK、私はそれを自分で実行しました。本当にそこに.DeclareLocal(tr)を追加しても機能しますが、メソッドのシグネチャにも問題がありました。

しかし、あなたは2つの異なるメソッドシグネチャでそれを呼び出そうとしています。オブジェクトを返す場合はボックスにする必要がありますが、Rectangleを返す場合はボックスに入れることはできません。しかし、戻り値の型はtypeof(Rectangle)にハードコードされています。したがって、Rectangleをボックスに入れて、四角形の構造体としてboxed rectangleを返します。ボックスに入れないでください。また、戻り値の型を変更してください。

+0

良い一般的なアドバイス。合意した – Noldorin

+0

答えをありがとう。それは理にかなっている。しかし、il.Emitの前にil.DeclareLocal(tr)を追加しました(OpCodes.Ldloca_S、(byte)0)。それでも同じ出力が得られます。私はまた、逆コンパイルし、このように見えるメソッドを調べました:public Object DoItUp(){return new Rectangle(); } –

関連する問題