"ceq" MSIL指令和object.InternalEquals之间的区别是什么?

3

我在ILDASM和Reflector中进行了挖掘,发现:

  1. ==编译成"ceq" MSIL命令
  2. object.Equals保持不变
  3. object.Equals调用object.InternalEquals

这个问题向我展示了如何找到InternalEquals的实现方式,即在.cpp类(或其他地方,在CLR中)。

我的问题是:

ceq会变成什么?不同.cpp类中的另一个方法吗?也就是说,它们是完全不同的代码?因此,尽管==和Equals的默认行为似乎相同,但它们是不同的代码吗?


一些==不会编译为ceq的异常:stringSystem.Type,请参见http://stackoverflow.com/questions/17634395/what-is-a-better-way-to-check-that-a-given-object-is-a-particular-value-type。 - colinfang
3个回答

20

== 操作符并非总是被翻译为 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 一样。你可以使用相同的策略找到实现。


1
现在这就是我想要的。非常具体明确,再次感谢。这会让我安静几天 :). - J M
我希望我能够点赞/接受两次。说真的,找到一个起点并且找不到答案让我疯狂。 - J M
奇怪,你在SO上似乎并没有太多问题得不到答案 :) - Hans Passant
1
是的,抱歉,我的意思不是说我在这里得不到答案,我非常感激所有的回复。我的意思是通常只有在经过数小时无法自己解决问题后,我才会在这里发帖求助。因此,当有人详细地、富有洞见和真正的知识来回答时,这对我来说意义重大。 - J M

4
您看到了ceq,因为没有重载的== - 它正在进行直接引用比较。为此,它只需在堆栈上直接比较两个数字;这几乎是它可以做的最快的事情。
object.Equals是有歧义的;有两个;
x.Equals(y)是一个虚拟方法,所以可能会被覆盖。根据类型,将发出虚拟调用、静态调用或约束调用,其中可能有自定义实现。
object.Equals(x,y)是一个静态方法,首先检查null;2个null = true,1个null = false,0个null - 调用x.Equals(y)。
但是为了集中讨论问题,它在本质上是针对本机整数的==;在大多数JITs中,我希望这仍然是针对两种整数类型(可能是指针)的==,但是JITs有所不同(甚至可能不存在 - MF是解释器)。

抱歉耽搁了,感谢您的耐心等待。我很感激您花时间解释。关于模糊性问题,抱歉,我指的是实例Equals方法。我的问题是想要理解为什么有两种不同的相等性检查方式,在不同的地方(在CLR内部?)实现,一种默认编译为msil指令,另一种默认保持为对外部方法的调用,这两种方式都可以被覆盖... - J M
@JM == 可以被重载,但不能被覆盖;这是静态分析和多态之间的重大区别。 - Marc Gravell
是的,那里有错误,可能是因为已经过了午夜十五分钟,但这并不是一个好借口 :). 有些尴尬。谢谢你指出来。我正在编辑我的评论 :) - J M
除非我似乎不能...哦,好吧。 - J M

2

是的,它们运行不同的代码。

  • Equals是一个实例方法。
  • ==是一个静态运算符。

两者都可以针对自定义类型进行重新定义。


感谢您抽出时间回答。我确实理解Equals是一个实例方法,而==是一个运算符。我不明白的是默认实现(即“ceq”调用和对“object.InternalEquals”的调用)之间的区别,以及为什么一开始就有两种方式来做这件事... - J M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接