Reflection.Emitを使用した奇妙なパラメーターシーケンス
-
22-07-2019 - |
質問
最近、Reflection.Emitを見てきました。同じパラメーターで別のメソッドを単純に呼び出すDynamicMethodを生成する単純なプログラムを作成しました
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Test();
}
public delegate void TestHandler(int a, int b, int c, int d, int e, int f);
public void Test()
{
DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));
MethodInfo method1 = typeof(Program).GetMethod("Question",BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,null,new Type[]{typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32)},null);
MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_S, 0);
gen.Emit(OpCodes.Ldarg_S, 1);
gen.Emit(OpCodes.Ldarg_S, 2);
gen.Emit(OpCodes.Ldarg_S, 3);
gen.Emit(OpCodes.Ldarg_S, 4);
gen.Emit(OpCodes.Ldarg_S, 5);
gen.Emit(OpCodes.Ldarg_S, 6);
gen.Emit(OpCodes.Call, method1);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Call, method2);
gen.Emit(OpCodes.Call, method3);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret);
TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
handler(1, 2, 3, 4, 5, 6);
}
public void Question(int a, int b, int c, int d, int e, int f)
{
Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
}
この例を実行すると、1,2,3,4,5,6が出力されるはずですが、2,3,4,5,6,1が出力されます
理由はよくわかりません... Reflection.Emitを使用するための優れたリソースを知っているなら、その方向を教えてください。 Emit AddInでReflectorを使用しています。
乾杯
ロハン
解決
問題は、静的メソッドではなく動的メソッドを呼び出していることです。生成されたメソッドには、Programクラスのインスタンスへの参照がありません。
また、6パラメーターメソッドの場合、7つのパラメーターをスタックにプッシュしていることに注意してください。 最初のパラメーターは、メソッドを呼び出しているオブジェクトへの参照である必要があります。
表示されている奇妙な動作は、インデックス6のパラメーターが存在せず、パラメーター配列の先頭に戻っているためである可能性があります。
「方法:動的メソッドを定義および実行する」を参照してください。 VSヘルプで。
メソッド呼び出しでオブジェクトパラメータを受け入れるか、静的にすることで機能させることができます:
public delegate void TestHandler(オブジェクトインスタンス、int a、int b、int c、int d、int e、int f);
public void Test()
{
DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(object), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));
MethodInfo method1 = typeof(Program).GetMethod("Question", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, null);
MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_S, 0);
gen.Emit(OpCodes.Ldarg_S, 1);
gen.Emit(OpCodes.Ldarg_S, 2);
gen.Emit(OpCodes.Ldarg_S, 3);
gen.Emit(OpCodes.Ldarg_S, 4);
gen.Emit(OpCodes.Ldarg_S, 5);
gen.Emit(OpCodes.Ldarg_S, 6);
gen.Emit(OpCodes.Call, method1);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Call, method2);
gen.Emit(OpCodes.Call, method3);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret);
TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
handler(this, 1, 2, 3, 4, 5, 6);
}
public void Question(int a, int b, int c, int d, int e, int f)
{
Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
他のヒント
動作させるには
-
Question
をstatic
メソッドにする - コメント
gen.Emit(OpCodes.Ldarg_S、6);
- modify
MethodInfo method1 = ...
適宜
1つの「臭い」 Question
メソッドでデバッグを停止すると、 this
参照を評価できなくなります。そして、これはインスタンスメソッド用ではありません...;-)
編集:操作。私は、ロバート・ワグナーからの答えを見たところです。必要に応じて投稿をキャンセルする準備ができました...:-)