我的简单 MSIL 有什么问题
本文关键字:什么 问题 MSIL 简单 我的 | 更新日期: 2023-09-27 18:19:56
我正在尝试生成以下类:
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();
但是当我在MyType
实例上调用MyMethod
时,我得到一个InvalidProgramException
:公共语言运行时检测到无效程序。
我尝试将返回类型更改为 void
并仅使用 EmitWriteLine
和 Emit(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
,然后在发出OpCodes.Br_S
时将该标签传递给 Emit
。
正如你所看到的,使用这么短的方法进行分支是没有意义的。
ilBuilder.Emit(OpCodes.Br_S);
您打算分支到哪里? br.s
需要一个有符号字节参数,该参数提供要跳转到的偏移量。