我在ILDASM和Reflector中进行了挖掘,发现:
- ==编译成"ceq" MSIL命令
- object.Equals保持不变
- object.Equals调用object.InternalEquals
这个问题向我展示了如何找到InternalEquals的实现方式,即在.cpp类(或其他地方,在CLR中)。
我的问题是:
ceq会变成什么?不同.cpp类中的另一个方法吗?也就是说,它们是完全不同的代码?因此,尽管==和Equals的默认行为似乎相同,但它们是不同的代码吗?
我在ILDASM和Reflector中进行了挖掘,发现:
这个问题向我展示了如何找到InternalEquals的实现方式,即在.cpp类(或其他地方,在CLR中)。
我的问题是:
ceq会变成什么?不同.cpp类中的另一个方法吗?也就是说,它们是完全不同的代码?因此,尽管==和Equals的默认行为似乎相同,但它们是不同的代码吗?
== 操作符并非总是被翻译为 ceq。某些类型可以通过 operator==() 重载该操作符。例如,System.Decimal 会重载所有的操作符,因为它们的实现不是简单的,而 jitter 对该类型没有特殊的了解(编译器有)。
在 Reflector 中,你可以找到 Decimal.op_Equality() 方法。这将引导你到一个被 MethodImplOptions.InternalCall 标记的方法 FCallCompare。这些方法很特殊,jitter 对它们有秘密的了解。你可以在 Rotor 的 clr/src/vm/ecall.cpp 源代码文件中找到它们的实现。它包含了所有内部调用函数的表格,jitter 通过方法名称查找表格条目。然后,将提供表格中对应 C++ 函数的地址编译到调用指令中。请注意,自 Rotor 发布以来,函数名称已经更改,搜索 FCallAdd,它是表格中的下一个条目。这将带你到 COMDecimal::Compare。再带你到 comdecimal.cpp 源代码文件。
x86 和 x64 jitter 知道如何直接将 ceq 操作码转换为机器码,无需使用辅助函数,它会内联生成本机机器指令。实际生成的代码取决于正在比较的值的类型和目标,x64 jitter 使用 SSE 指令,x86 使用 FPU 指令来比较浮点值。其他 jitter 将以不同的方式实现它们。
像 Object.InternalEquals() 这样的辅助函数也是内部方法,就像 FCallCompare 一样。你可以使用相同的策略找到实现。
ceq
,因为没有重载的==
- 它正在进行直接引用比较。为此,它只需在堆栈上直接比较两个数字;这几乎是它可以做的最快的事情。==
可以被重载,但不能被覆盖;这是静态分析和多态之间的重大区别。 - Marc Gravell是的,它们运行不同的代码。
两者都可以针对自定义类型进行重新定义。
==
不会编译为ceq
的异常:string
,System.Type
,请参见http://stackoverflow.com/questions/17634395/what-is-a-better-way-to-check-that-a-given-object-is-a-particular-value-type。 - colinfang