13

我目前面临运营商的新问题。使用以下代码,我想制作与if ... else在 C# 中使用 pair 时相同的输出。

var method = new DynamicMethod("dummy", null, Type.EmptyTypes);
var g = method.GetILGenerator();

g.Emit(OpCodes.Ldstr, "string");
g.Emit(OpCodes.Ldstr, "string");
g.Emit(OpCodes.Call, typeof(String).GetMethod("op_Equality", new Type[]{typeof(string), typeof(string)}));
g.Emit(OpCodes.Ldc_I4, 0);
g.Emit(OpCodes.Ceq);
g.Emit(OpCodes.Brtrue_S, );

var action = (Action)method.CreateDelegate(typeof(Action));
action();

Console.Read();

我的问题是:

  1. 如何获取指令的地址以将其作为分支操作码的参数传递?
  2. BRBR_SBrtrueBrtrue_SBrfalseBrfalse_S类似的指令之间有什么区别吗?

谢谢。

4

3 回答 3

10
  1. 您可以使用DefineLabelMarkLabel方法的组合来确定分支的目标位置。为此,请声明您需要的标签 - 类似于 equaland notequal。然后,您可以在 IL 中标记应该存在标签的位置。完成此操作后,您可以将分支指令的目标设置为此标签。

    // Define labels
    Label equal = g.DefineLabel();
    Label notEqual = g.DefineLabel();
    Label endOfMethod = g.DefineLabel();
    // your logic here
    g.Emit(OpCodes.Brtrue_S, equal);
    g.MarkLabel(equal);
    // some code if they are equal
    g.MarkLabel(notEqual);
    // some code if they are not equal
    g.MarkLabel(endOfMethod); // this marks the return point
    g.Emit(OpCodes.Ret);
    
  2. BrBrtrueBrfalse和它们的对应物之间的区别在于_S 跳跃的长度。_S表示缩写形式;目标指令是距下一条指令的 1 字节带符号偏移量。在标准(非短)形式中,目标由 4 字节偏移量表示。

于 2012-06-21T13:38:37.307 回答
8

ILGenerator.ILOffset为您提供 IL 流中的当前偏移量,如果这是您想要的。正如 goric 建议的那样,您也可以使用DefineLabeland 。MarkLabel

brtrue.s和之间的唯一区别brtrue是. 使用 4 字节偏移量并使用 1 字节偏移量。这同样适用于and (and / )。 brtrue.sbrtruebrtruebrtrue.sbrfalsebrfalse.sbrbr.s

这些不是 IL 指令的唯一短版本,还有其他短指令,例如ldc.i4.0-ldc.i4.8用于加载整数。这些主要用于生成较小的二进制文件,但我认为其他方面没有太大区别。

于 2012-06-21T13:38:39.597 回答
4

这对我来说是新的,但是通过一些搜索显然你可以通过调用获得地址

Label targetInstruction = g.DefineLabel();

事先在某个时间点(例如在你的第一个Emit?),然后

g.MarkLabel(targetInstruction);

就在发出要分支的行之前。然后这Label是您的Br____操作码的参数。

BrBrtrueBrfalse之间的区别在于Br 总是分支,而另外两个从堆栈中弹出一个值,并且仅在分别为 true 或 false 时才分支。(所以是的,有很大的不同!) _S 表示“短格式”,但我不确定这是什么意思。

于 2012-06-21T13:39:51.093 回答