アクション<T>指示


1

からDynamicMethodを作成します。私はDynamicMethodで遊んでと次の操作を行うことを目指しています:

私はGetILAsByteArray()を使用してバイトとしてILコードを取得し、そこからアクションを持っています。このバイトから、私は動的メソッドを作成して実行したいと思います。ここで私が何をしようとしています何の例:

class Program 
{ 
    static void Main(string[] args) 
    { 
     //Create action and execute 
     Action<string> myAction = s => 
     { 
      Console.WriteLine("Hello " + s); 
     }; 
     myAction("World"); 
     //Get IL bytes 
     byte[] ilBytes = myAction.GetMethodInfo().GetMethodBody().GetILAsByteArray(); 
     DynamicMethod dynamicCallback = new DynamicMethod("myAction", typeof(void), new Type[] { typeof(string) }); 
     DynamicILInfo dynamicIlInfo = dynamicCallback.GetDynamicILInfo(); 
     dynamicIlInfo.SetCode(ilBytes, 100); 
     dynamicCallback.Invoke(null, new object[] { "World" }); 
    } 
} 

dynamicCallback.Invoke(null, new object[] { "World" })我々取得「スロー例外:がmscorlib.dllで 『System.BadImageFormatException』」を呼び出します。

私には分かりませんが、SetCode()の2番目の引数として使用する必要があるものは、 'maxStackSize'として使用する必要がありますか?最初のアクションと同じ値を設定するにはどうすればよいですか?しかし、これは例外の理由ではないと思います。

ILバイトから動的メソッドを正しく作成するにはどうすればよいですか?


ソリューションここ

私はデュディ東駅で提供完全なソリューションを要約したいと思います:

static void Main(string[] args) 
{ 
    Action<string> myAction = s => 
    { 
     Console.WriteLine("Hello " + s); 
    }; 
    MethodInfo method = myAction.GetMethodInfo(); 
    object target = myAction.Target; 

    DynamicMethod dm = new DynamicMethod(
     method.Name, 
     method.ReturnType, 
     new[] {method.DeclaringType}. 
      Concat(method.GetParameters(). 
       Select(pi => pi.ParameterType)).ToArray(), 
     method.DeclaringType, 
     skipVisibility: true); 

    DynamicILInfo ilInfo = dm.GetDynamicILInfo(); 
    var body = method.GetMethodBody(); 
    SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper(); 
    foreach (LocalVariableInfo lvi in body.LocalVariables) 
    { 
     sig.AddArgument(lvi.LocalType, lvi.IsPinned); 
    } 
    ilInfo.SetLocalSignature(sig.GetSignature()); 
    byte[] code = body.GetILAsByteArray(); 
    ILReader reader = new ILReader(method); 
    DynamicMethodHelper.ILInfoGetTokenVisitor visitor = new DynamicMethodHelper.ILInfoGetTokenVisitor(ilInfo, code); 
    reader.Accept(visitor); 
    ilInfo.SetCode(code, body.MaxStackSize); 

    dm.Invoke(target, new object[] { target, "World" }); 

    Console.ReadLine(); //Just to see the result 
} 

注:DynamicMethodHelperはクラスblog postにハイボ羅によって開発され、説明されているが、直接hereにダウンロードすることもできます。あなたはこのようにそれを行うことができます

  0

私は反射を使ってmaxStackSize値を取得できないと思います。しかし、確かに、それはここで問題ではありません。問題は 'Console.WriteLine'呼び出しがメタデータトークン(おそらくMethodRef)としてエンコードされ、メタデータトークンがそれを宣言するモジュールのスコープ内でのみ有効であることです。 'DynamicILInfo.GetTokenFor'関数を見てみましょう。これらは' DynamicMethod'に有効な他のメタデータ項目と作成トークンをインポートします。 20 10月. 162016-10-20 09:22:28

  0

@thehennyy私は成功していない、私の編集を見てみました。 20 10月. 162016-10-20 09:36:14

  0

ILバイト配列の古いトークンを、新しく作成した 'GetTokenFor'メソッドが返すトークンに置き換える必要があります。 20 10月. 162016-10-20 09:42:02

  0

私はすでにコード化したものが意味をなさないと思ったので、すぐには取得できませんでした。どうも。アクションで何が呼び出されたのかを知る必要なしにこれを行う一般的な方法はありますか?例えば。最後にこれを動的に行う方法はありますか? 20 10月. 162016-10-20 09:45:45

  0

はい、メソッド本体のバイト配列を解析し、 'Module.Resolvexxx'メソッドを使ってすべてのトークンを解決することができます。 20 10月. 162016-10-20 09:55:57

  0

なぜこれをやろうとしていますか? Thehennyによって説明されているように、メタデータトークンのために単純なアプローチは機能しません。たぶんあなたがしようとしていることを達成するための他の方法があります。 20 10月. 162016-10-20 14:15:30

  0

質問は好奇心の中から出されました。これで何ができるのかは、メソッドのシリアライゼーションです。これは面白いことです。しかし、私はこれが実際のアプリケーションでは賢明ではないかもしれないことを知っています。 21 10月. 162016-10-21 07:27:24

1

byte[] code = body.GetILAsByteArray(); 
ILReader reader = new ILReader(method); 
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code); 
reader.Accept(visitor); 
ilInfo.SetCode(code, body.MaxStackSize); 

ILReaderはあなたのためにハードワークを行うクラスです。 hereからコピーできます。

例:

MethodInfo method = ... 
DynamicMethod dm = new DynamicMethod(
    method.Name, 
    method.ReturnType, 
    method.GetParameters.Select(pi => pi.ParameterType).ToArray(), 
    method.DeclaringType, 
    skipVisibility: true\fasle - depends of your need); 

DynamicILInfo ilInfo = dm.GetDynamicILInfo(); 
var body = method.GetMethodBody(); 
SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper(); 
foreach(LocalVariableInfo lvi in body.LocalVariables) 
{ 
    sig.AddArgument(lvi.LocalType, lvi.IsPinned); 
} 
ilInfo.SetLocalSignature(sig.GetSignature()); 
byte[] code = body.GetILAsByteArray(); 
ILReader reader = new ILReader(method); 
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code); 
reader.Accept(visitor); 
ilInfo.SetCode(code, body.MaxStackSize); 

あなたの方法は、単純な方法(一般的な例外ハンドルがないわけではない)である場合は、THIDは動作するはずです。

あなたの方法は一般的なものである場合は、DynamicMethodコンストラクタに所有者のタイプを渡すためにこれを実行する必要があります。

var owner = method.DeclaringType.MakeGenericType(
      method.DeclaringType.GetGenericArguments()); 

もう一つ、そのはまだ働いていない、とあなたのメソッドがインスタンスである場合メソッドの場合は、DynamicMethodコンストラクタのparamters配列の最初のセルにメソッドのinstacne型を渡します。

更新

myActionは静的メソッドではありませんので、あなたはここにnulldm.Invoke(**null**, new object[] { "World" });を渡すことはできません。

myActionAction<string>)は、実際には、そのメソッドを保持する新しい生成クラスのメソッドです。

しかし、myAction.Targetまたはそのタイプの新しいインスタンスを渡しても、例外がスローされています。例外(CLRは無効なプログラムを拒否する)は、ILが正確に正しくないことを伝えています。私はあなたに今問題を正確に伝えることはできませんが、もしあなたにとって重要であれば、私は来週仕事に戻ることができます。

とにかくあなただけであるとして、あなたがあなたのコードを使用することができますアクションでDynamicIlInfo.SetCodeを参照してくださいが、ちょうどこのからメソッド情報変更する場合:これに

class Program 
{   
    static void Main(string[] args) 
    { 
     Action<string> myAction = s => 
     { 
      Console.WriteLine("Hello " + s); 
     }; 
     MethodInfo method = myAction.GetMethodInfo(); 

     //Rest of your code 
    } 
} 

class Program 
{ 
    static void M(string s) 
    { 
     Console.WriteLine("Hello " + s); 
    } 

    static void Main(string[] args) 
    { 
     MethodInfo method = typeof (Program).GetMethod("M", BindingFlags.Static | BindingFlags.NonPublic); 

     //Rest of your code 
    } 
} 

更新2:

明らかに私は昨日非常に疲れていました。私はあなたの間違いを認識しませんでした。

そのはまだ働いていない、とあなたのメソッドがインスタンスメソッドである場合、私は偶然にの最初のセルに方法のinstacneタイプを渡し、

もう一つは、私のオリジナルの答えに書いたようにDynamicMethodコンストラクタの配列。

だから、この実行する必要があります。

DynamicMethod dm = new DynamicMethod(
    method.Name, 
    method.ReturnType, 
    new[] {method.DeclaringType}. 
     Concat(method.GetParameters(). 
     Select(pi => pi.ParameterType)).ToArray(), 
    method.DeclaringType, 
    skipVisibility: true); 

をそして、このような動的なメソッド呼び出し:

dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" }); 

は、今では完璧な仕事だし。

  0

'ILInfoGetTokenVisitor'はどこで宣言されていますか? 26 10月. 162016-10-26 12:39:58

  0

私はすぐに「System.Reflection.TargetInvocationException」メッセージを表示します。「{」Bad binary signature。 (HRESULTからの例外:0x80131192) "}"動的メソッドを呼び出すとき。私は何か基本的なことを忘れていると思う。 26 10月. 162016-10-26 13:35:35

  0

@ Sjoerd222888私の更新例をご覧ください 26 10月. 162016-10-26 14:49:14

  0

私の更新を見てください。私はまだ 'TargetInvocationException'を取得します。どこが間違っていますか? 27 10月. 162016-10-27 12:05:32

  0

@ Sjoerd222888今すぐチェック 27 10月. 162016-10-27 23:40:20

+1

@ Sjoerd222888その仕事 28 10月. 162016-10-28 08:39:43

  0

詳細な説明をありがとうございます。私はちょうど 'DynamicMethod'で作業することを学んでいます。あなたの答えは大きな助けになります! 28 10月. 162016-10-28 09:14:50