![](/img/trans.png)
[英]IL Calling a method with params object[] arguments using Reflection.Emit
[英]IL Calling a method with 2 array arguments using Reflection.Emit
首先,我必須為IL成為菜鳥而言。 我無法生成IL代碼來調用具有此簽名的方法:
public void CallMethod2(string name, object[] args, object[] genericArgs)
我能夠調用一個具有單個數組的方法,如下所示:
public void CallMethod1(string name, object[] args)
使用以下IL工作:
ILGenerator ilgen = myMethod.GetILGenerator();
var il = ilgen;
MethodInfo invokerMethod = typeof(Proxy<T>).GetMethod("CallMethod1", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, method.Name);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg, 1);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, invokerMethod);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
但后來我使用以下IL嘗試使用此IL調用CallMethod2:
ILGenerator ilgen = myMethod.GetILGenerator();
var il = ilgen;
MethodInfo invokerMethod = typeof(Proxy<T>).GetMethod("CallMethod2", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, method.Name);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg, 1);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg, 1);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldloc_2);
il.Emit(OpCodes.Call, invokerMethod);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
此IL與附加對象[]我收到錯誤:
Common Language Runtime檢測到無效程序。
正如你所看到的,我所做的就是添加第二個塊來填充數組並調用方法,似乎通過使用StLoc_1它只會破壞它。
我編寫了相同的方法並正常調用它並查看了ILDasm,代碼似乎全部占用了。
謝謝
我認為問題是這些行就在方法調用之前:
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldloc_2);
應該
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldloc_1);
您的數組位於堆棧位置0和1,而不是1和2。
我很困惑......你明白了:因為你沒有真正分配任何本地人,所以代碼不應該工作 ; 例如,這里有一個寫得不好(因為它使用了不必要的本地人)乘以4的方法,它沒有聲明本地人:
var method = new DynamicMethod("MulBy4", typeof (int),
new Type[] {typeof (int)});
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_4);
il.Emit(OpCodes.Stloc_0); // this usage is
il.Emit(OpCodes.Ldloc_0); // deliberately silly
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Stloc_1); // this usage is
il.Emit(OpCodes.Ldloc_1); // deliberately silly
il.Emit(OpCodes.Ret);
var mulBy4= (Func<int,int>)method.CreateDelegate(typeof (Func<int, int>));
var twelve = mulBy4(3);
這會創建VerificationException
:
操作可能會破壞運行時的穩定性。
因為它無法驗證。 這是不好的IL! 如果我們將其更改為:
var method = new DynamicMethod("MulBy4", typeof (int),
new Type[] {typeof (int)});
var il = method.GetILGenerator();
il.DeclareLocal(typeof (int));
il.DeclareLocal(typeof(int));
...
然后它現在有效。 然后通過存儲和使用從DeclareLocal
返回的LocalBuilder
,這將導致記住數字的替代方法:
var method = new DynamicMethod("MulBy4", typeof (int),
new Type[] {typeof (int)});
var il = method.GetILGenerator();
var multiplier = il.DeclareLocal(typeof (int));
var result = il.DeclareLocal(typeof(int));
il.Emit(OpCodes.Ldc_I4_4);
il.Emit(OpCodes.Stloc, multiplier);
il.Emit(OpCodes.Ldloc, multiplier);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Stloc, result);
il.Emit(OpCodes.Ldloc, result);
il.Emit(OpCodes.Ret);
var mulBy4= (Func<int,int>)method.CreateDelegate(typeof (Func<int, int>));
var twelve = mulBy4(3);
如果您擔心這會使用較長的IL版本,那么您可以使用:
static void LoadLocal(this ILGenerator il, LocalBuilder local)
{
switch(local.LocalIndex)
{
case 0: il.Emit(OpCodes.Ldloc_0); break;
case 1: il.Emit(OpCodes.Ldloc_1); break;
case 2: il.Emit(OpCodes.Ldloc_2); break;
case 3: il.Emit(OpCodes.Ldloc_3); break;
default:
if(local.LocalIndex < 256)
{
il.Emit(OpCodes.Ldloc_S, (byte) local.LocalIndex);
} else
{
il.Emit(OpCodes.Ldloc, (ushort) local.LocalIndex);
}
break;
}
}
和il.LoadLocal(multiplier);
和il.LoadLocal(result);
(顯然類似於Stloc
)
我已經更新了@Mark的答案來覆蓋價值類型。 StoreLocal是獎金:)
public static void LoadLocalValue(this ILGenerator il, LocalBuilder local)
{
switch (local.LocalIndex)
{
case 0: il.Emit(OpCodes.Ldloc_0); break;
case 1: il.Emit(OpCodes.Ldloc_1); break;
case 2: il.Emit(OpCodes.Ldloc_2); break;
case 3: il.Emit(OpCodes.Ldloc_3); break;
default:
if (local.LocalIndex < 256)
{
il.Emit(OpCodes.Ldloc_S, (byte)local.LocalIndex);
}
else
{
il.Emit(OpCodes.Ldloc, (ushort)local.LocalIndex);
}
break;
}
}
public static void LoadLocalAddress(this ILGenerator il, LocalBuilder local)
{
if (local.LocalIndex < 256)
{
il.Emit(OpCodes.Ldloca_S, (byte)local.LocalIndex);
}
else
{
il.Emit(OpCodes.Ldloca, local);
}
}
public static void LoadLocalAuto(this ILGenerator il, LocalBuilder local)
{
if (local.LocalType?.IsValueType == true)
{
LoadLocalAddress(il, local);
return;
}
LoadLocalValue(il, local);
}
public static void StoreLocal(this ILGenerator il, LocalBuilder local)
{
switch (local.LocalIndex)
{
case 0: il.Emit(OpCodes.Stloc_0); break;
case 1: il.Emit(OpCodes.Stloc_1); break;
case 2: il.Emit(OpCodes.Stloc_2); break;
case 3: il.Emit(OpCodes.Stloc_3); break;
default:
if (local.LocalIndex < 256)
{
il.Emit(OpCodes.Stloc_S, (byte)local.LocalIndex);
}
else
{
il.Emit(OpCodes.Stloc, (ushort)local.LocalIndex);
}
break;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.