我正在尝试生成以下类:

public class MyType
{
    public string MyMethod() { return "Hi"; }
}

我的发射代码如下:
var assemblyBuilder = GetAssemblyBuilder("MyAssembly");
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule");
var typeBuilder = moduleBuilder.DefineType("MyType", TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod("MyMethod", MethodAttributes.Public, typeof(string), new Type[] { });
var ilBuilder = methodBuilder.GetILGenerator();
ilBuilder.Emit(OpCodes.Nop);
ilBuilder.Emit(OpCodes.Ldstr, "Hi");
ilBuilder.Emit(OpCodes.Stloc_0);
ilBuilder.Emit(OpCodes.Br_S);
ilBuilder.Emit(OpCodes.Ldloc_0);
ilBuilder.Emit(OpCodes.Ret);
var type = typeBuilder.CreateType();

但是当我在 MyMethod 的一个实例上调用 MyType 时,我得到一个 InvalidProgramException :公共(public)语言运行时检测到一个无效的程序。

我尝试将返回类型更改为 void 并仅使用 EmitWriteLineEmit(OpCodes.Ret) ,它们运行良好,因此它必须是我在这里编写的 IL。

我在这里遗漏了一些明显的东西吗?一个明确的解释会很有帮助,因为我刚刚开始使用 Emit。

来自评论 的附加信息:

“原始”IL 取自 LINQ-pad 中的 IL 代。

最佳答案

除了删除 @Kendall 指出的 OpCodes.Br_S 之外,您还需要声明要存储 "Hi" 的局部变量:

ilBuilder.DeclareLocal(typeof(string));
ilBuilder.Emit(OpCodes.Nop);
ilBuilder.Emit(OpCodes.Ldstr, "Hi");
ilBuilder.Emit(OpCodes.Stloc_0);
ilBuilder.Emit(OpCodes.Ldloc_0);
ilBuilder.Emit(OpCodes.Ret);

此外,我认为整个事情可以缩短为(如果您使用编译器优化构建类,这就是您得到的):
ilBuilder.Emit(OpCodes.Ldstr, "Hi");
ilBuilder.Emit(OpCodes.Ret);

(只需加载字符串,将其放在计算堆栈的顶部,然后返回字符串)。

如果您想在关闭编译器优化的情况下复制版本,我认为这就是您最初尝试做的:
Label returnLabel = ilBuilder.DefineLabel();

ilBuilder.DeclareLocal(typeof(string));
ilBuilder.Emit(OpCodes.Nop);
/* Load the string "HI" and put it on the evaluation stack. */
ilBuilder.Emit(OpCodes.Ldstr, "Hi");

/* Store "Hi" in the local variable we declared. */
ilBuilder.Emit(OpCodes.Stloc_0);

/* Jump to the return label, which is, err the next line anyway: */
ilBuilder.Emit(OpCodes.Br_S, returnLabel);

/* Mark "returnLabel" here so we can jump to it: */
ilBuilder.MarkLabel(returnLabel);

/* Load the contents of our local variable, put it on the evaluation stack: */
ilBuilder.Emit(OpCodes.Ldloc_0);

/* Return "Hi", which is now back on top of the evaluation stack: */
ilBuilder.Emit(OpCodes.Ret);

您需要定义要分支到的标签(使用 ILGenerator.DefineLabel ,使用 MarkLabel ,然后在发出 Emit 时将该标签传递给 OpCodes.Br_S

但是,正如您所看到的,使用如此简短的方法进行分支是毫无意义的。

关于c# - 我的简单邮件有什么问题?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25551866/

10-12 03:02