我的简单邮件有什么问题?

时间:2014-08-28 14:52:58

标签: c# cil reflection.emit

我试图生成以下类:

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

我的Emit代码如下:

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:公共语言运行时检测到无效程序。

我已经尝试将返回类型更改为void并仅使用EmitWriteLineEmit(OpCodes.Ret),这样运行正常,所以它必须是IL我写的这里。

我错过了一些明显的东西吗?如果我刚开始使用Emit,那么明确的解释会很有帮助。

评论中的其他信息

"原作" IL取自LINQ-pad中的IL-generation。

2 个答案:

答案 0 :(得分:5)

除了删除@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);

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

如果您希望使用编译器优化 off 复制版本,我认为这是您最初尝试做的事情:

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

正如您所看到的,分支对于这种简短的方法来说毫无意义。

答案 1 :(得分:3)

ilBuilder.Emit(OpCodes.Br_S);

你打算去哪里? br.s需要一个带符号的字节参数,该参数使偏移量跳转到。