我正在为一些内部库计算 Reflection.Emit,但在调用作为参数传入的 Func 时遇到了困难。我的场景测试使用的是图中用Linqpad传输到IL的代码 我在 DynamicMethod 中复制 IL 的代码如下
public class ScopeTest
{
public delegate Task WrapScope(Func<Task> value);
public (WrapScope scope,string id) WrapScopeInId()
{
var id = $"wrap~{Guid.NewGuid().ToString().Replace("-",string.Empty)}";
var mi = typeof(Func<Task>).GetMethod("Invoke");
var d = new DynamicMethod(id, typeof(Task), new[] { typeof(Func<Task>) });
var gen = d.GetILGenerator();
var lab = gen.DefineLabel();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Callvirt, mi);
gen.Emit(OpCodes.Stloc_0);
gen.Emit(OpCodes.Br_S, lab);
gen.MarkLabel(lab);
gen.Emit(OpCodes.Ldloc_0);
gen.Emit(OpCodes.Ret);
WrapScope del = (WrapScope)d.CreateDelegate(typeof(WrapScope));
return (del,id);
}
}
代码编译并返回,但是当您调用 WrapScope 委托(delegate)时 del(Func<Task>)
它抛出 System.InvalidProgramException:公共(public)语言运行时检测到无效程序。
运行此 DynamicMethod 可能出现什么问题?
谢谢
最佳答案
您的主要问题是您将变量存储在插槽 0 中,但您从未声明插槽 0。如果我们查看您的代码 on SharpLab ,我们可以看到 IL 声明了该方法使用的每个槽:
.locals init (
[0] class [System.Private.CoreLib]System.Threading.Tasks.Task
)
(这表示我们有 1 个插槽,索引 0,类型为 Task
)。
您可以使用 ILGenerator.DeclareLocal
使用 ILGenerator
来执行此操作。我们可以使用ldloc
/STLoc
并传入返回的LocalBuilder
,而不是使用编号的ldloc.0
>/STLoc.0
.
var taskLocal = gen.DeclareLocal(typeof(Task));
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Callvirt, mi);
gen.Emit(OpCodes.Stloc, taskLocal);
gen.Emit(OpCodes.Br_S, lab);
gen.MarkLabel(lab);
gen.Emit(OpCodes.Ldloc, taskLocal);
gen.Emit(OpCodes.Ret);
这一切都很好并且有效,但是它包含了很多不必要的指令。 Linqpad 为您提供 Debug模式下编译器的输出,它会发出大量不必要的 NOP 等。您将看到 Release模式下的 SharpLab 不会显示这些内容,我们可以简单地删除它们。那个 br.s
也是无关紧要的,因为它无条件跳转到下一条指令,所以我们也可以删除它:
var taskLocal = gen.DeclareLocal(typeof(Task));
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Callvirt, mi);
gen.Emit(OpCodes.Stloc, taskLocal);
gen.Emit(OpCodes.Ldloc, taskLocal);
gen.Emit(OpCodes.Ret);
现在 STLoc
/ldloc
看起来毫无意义:我们从堆栈中取出一个变量,将其移动到本地,然后立即将其从本地复制回来并入栈以便返回它。完全抛弃本地的:
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Callvirt, mi);
gen.Emit(OpCodes.Ret);
关于c# - DynamicMethod Reflection 发出对 Func<Task> 的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66804398/