gpt4 book ai didi

c# - DynamicMethod 反射 发出对 Func 的调用

转载 作者:行者123 更新时间:2023-12-04 07:53:53 25 4
gpt4 key购买 nike

我正在为一些内部库找出 Reflection.Emit 并坚持调用作为参数传入的 Func。我的场景测试使用图片中的 Linqpad 传输到 IL 的代码
Code to 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使用 ILGenerator.DeclareLocal .我们可以使用 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 等。您会看到 SharpLab 在 Release模式下不显示这些,我们可以简单地删除它们。那 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 反射 发出对 Func<Task> 的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66804398/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com