为什么`OpCode.Value`有"错误的"字节序?

sta*_*ica 7 .net cil reflection.emit endianness opcode

事实:

  1. CIL指令rethrow的操作码的正确编码是双字节序列FE 1A.

  2. OpCodes.Rethrow.Value(有类型short)0xFE1A在我的little-endian机器上有价值.

  3. BitConverter 转换为/从字节序列转换时,尊重机器的字节顺序.

  4. 在我的little-endian机器上,BitConverter.GetBytes(OpCodes.Rethrow.Value)产生字节序列1A FE.

这意味着,OpCode.Value使用little-endian机器序列化BitConverter不会产生正确的操作码编码; 字节顺序颠倒过来.

问题:

  • 记录的字节顺序OpCode.Value(如果是,在哪里?),还是"实现细节"?

  • 上面的大端机器上的步骤4是否也会导致错误的字节排序?也就是说,将OpCodes.Rethrow.Value0x1AFE一个big-endian的机器上?

sta*_*ica 1

我得出的结论是,根据OpCode.Value属性序列化操作码表示,即:

\n\n
OpCode someOpCode = \xe2\x80\xa6;\nbyte[] someOpCodeEncoding = BitConverter.GetBytes(someOpCode.Value);\n
Run Code Online (Sandbox Code Playgroud)\n\n

是一个坏主意,但并不是因为使用了BitConverter.GetBytes(short),它的行为有详细记录。罪魁祸首是OpCode.Value财产,其文件在两个方面含糊不清:

\n\n
    \n
  1. 它指出该属性包含“立即操作数的值”,它可能指也可能不指操作码的编码;该术语没有出现在 CLI 规范中的任何地方。

  2. \n
  3. 即使我们假设它实际上包含操作码的编码,文档也没有提及字节顺序。byte[](在和之间转换时,字节顺序会发挥作用short。)

  4. \n
\n\n

为什么我的论点基于 MSDN 文档,而不是 CLI 标准?因为System.Reflection.Emit它不是 CLI 标准定义的反射库的一部分。因此,我认为可以相当肯定地说,该名称空间的 MSDN 参考文档与官方规范非常接近。(但与 @Hans Passant 的回答不同,我不会更进一步并声称参考源无论如何都是一种规范。)

\n\n

结论:

\n\n

有两种方法可以输出给定对象的操作码编码OpCode

\n\n
    \n
  • 注重System.Reflection.Emit功能和使用ILGenerator.Emit(someOpCode)。在某些情况下这可能过于严格。

  • \n
  • 在操作码编码(即byte[]序列)和各种OpCode对象之间创建您自己的映射。

  • \n
\n

  • 替代方案:如果不使用反射,就不要使用“OpCode”对象?如果您只是喜欢使用“OpCodes.Rethrow.Value”来提高可读性,则可以使用 [Mono Cecil 的“OpCodes”](https://github.com/jbevain/cecil/blob/master/Mono.Cecil) 来实现相同的目的。 Cil/OpCodes.cs) 类;它的 [`OpCode`](https://github.com/jbevain/cecil/blob/master/Mono.Cecil.Cil/OpCode.cs) 结构具有显式的 `Op1` 和 `Op2` 属性。 (3认同)