我读完后来到这里 这个 我没有找到相关的答案 - 所以在你阅读整个问题之前,请不要将其标记为副本。
我一直在使用反射器并进行调查 Object.Equals
我看到的是:
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public virtual bool Equals(object obj)
{
return RuntimeHelpers.Equals(this, obj);
}
和 RuntimeHelpers.Equals
看起来像这样:
// System.Runtime.CompilerServices.RuntimeHelpers
/// <summary>Determines whether the specified <see cref="T:System.Object" /> instances are considered equal.</summary>
/// <returns>true if the <paramref name="o1" /> parameter is the same instance as the <paramref name="o2" /> parameter, or if both are null, or if o1.Equals(o2) returns true; otherwise, false.</returns>
/// <param name="o1">The first object to compare. </param>
/// <param name="o2">The second object to compare. </param>
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
public new static extern bool Equals(object o1, object o2);
现在我看不到实施了 RuntimeHelpers.Equals
但是通过描述,如果两个对象不是同一个实例而且不是null,它将调用 object.Equals
方法再次,我进入一个循环(我在谈论 纯物)。
当我说纯物体时我的意思是这样的:
object pureObj1 = new object();
object pureObj2 = new object();
bool areEql = pureObj1.Equals(pureObj2);
通过文档,这应该调用 Object.Equals
得到一个 recusive stackoverflow。我想也许文档是错误的,这检查 参考平等 对于基本对象 - 但我想确定。
底线:
比较两个纯对象(例如,不将字符串转换为对象)通过 Equals
电话 - 它如何确定它们是否相等? - 如果我不覆盖,会发生什么 Equals
方法和我打电话 Equals
两个对象?
附:无论如何,我可以看到 RuntimeHelpers.Equals
源代码?
MSDN的页面上 object.Equals(object)
详细介绍了这一点。具体而言,引用类型的默认实现是引用相等。 “继承人备注”部分中的表格是最直接的。
参考平等;相当于调用Object.ReferenceEquals。
MSDN的页面上 RuntimeHelpers.Equals(object,object)
确实这么说 Object.Equals(Object)
如果它的参数不是引用相等而且都不为null,则调用它。这显然是错误的;实际展现的行为是 RuntimeHelpers.Equals(object,object)
从不打电话 Object.Equals(Object)
。
例如,这个LINQPad脚本:
void Main()
{
object left = new Foo();
object right = new Foo();
left.Equals(right).Dump();
RuntimeHelpers.Equals( left, right ).Dump();
left = new Bar();
right = new Bar();
left.Equals(right).Dump();
RuntimeHelpers.Equals( left, right ).Dump();
left = new Baz();
right = new Baz();
left.Equals(right).Dump();
RuntimeHelpers.Equals( left, right ).Dump();
left = new Qux();
right = new Qux();
left.Equals(right).Dump();
RuntimeHelpers.Equals( left, right ).Dump();
}
private class Foo {}
private class Bar {
public override bool Equals(object obj) {
"Bar.Equals() called".Dump();
return base.Equals(obj);
}
}
private class Baz {
public override bool Equals(object obj) {
"Baz.Equals() called".Dump();
return RuntimeHelpers.Equals( this, obj );
}
}
private class Qux {
public override bool Equals(object obj) {
"Qux.Equals() called".Dump();
return true;
}
}
打印下面的输出:
假
假
Bar.Equals()调用
假
假
Baz.Equals()调用
假
假
Qux.Equals()调用
真正
假
所以我从中榨了一下 汉斯帕斯特给出的回答 Math.Pow()
...
这是来自\ clr \ src \ vm \ ecall.cpp的相关代码 SSCLI2.0
FCFuncStart(gObjectFuncs)
FCIntrinsic("GetType", ObjectNative::GetClass, CORINFO_INTRINSIC_Object_GetType)
FCFuncElement("InternalGetHashCode", ObjectNative::GetHashCode)
FCFuncElement("InternalEquals", ObjectNative::Equals)
FCFuncElement("MemberwiseClone", ObjectNative::Clone)
FCFuncEnd()
这是它映射到的\ clr \ src \ _vm \ comobject.cpp中的函数的代码:
FCIMPL2(FC_BOOL_RET, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef)
{
CONTRACTL
{
THROWS;
DISABLED(GC_NOTRIGGER);
INJECT_FAULT(FCThrow(kOutOfMemoryException););
MODE_COOPERATIVE;
SO_TOLERANT;
}
CONTRACTL_END;
if (pThisRef == pCompareRef)
FC_RETURN_BOOL(TRUE);
// Since we are in FCALL, we must handle NULL specially.
if (pThisRef == NULL || pCompareRef == NULL)
FC_RETURN_BOOL(FALSE);
MethodTable *pThisMT = pThisRef->GetMethodTable();
// If it's not a value class, don't compare by value
if (!pThisMT->IsValueClass())
FC_RETURN_BOOL(FALSE);
// Make sure they are the same type.
if (pThisMT != pCompareRef->GetMethodTable())
FC_RETURN_BOOL(FALSE);
// Compare the contents (size - vtable - sink block index).
BOOL ret = memcmp(
(void *) (pThisRef+1),
(void *) (pCompareRef+1),
pThisRef->GetMethodTable()->GetBaseSize() - sizeof(Object) - sizeof(int)) == 0;
FC_GC_POLL_RET();
FC_RETURN_BOOL(ret);
}
FCIMPLEND
我看到参考比较,空检查,值类型排除,类型匹配检查和按位相等比较。我不知道怎么样 Object.Equals(Object)
被称为。我相信文档 RuntimeHelpers.Equals(object,object)
简直不对。
Object.Equals
是 虚拟。类型覆盖它以具有不同的行为。
正如您所注意到的,默认实现调用了 MethodImplOptions.InternalCall
方法(即它是.NET运行时内部的一部分)。此方法通过直接查看引用来执行引用相等(实际上它执行C / C ++指针比较)。
没有递归。
NB。的文档 ReferenceHelper.Equals
说:
真正 如果o1参数与o2参数的实例相同,或两者都是 空值, 或者如果 o1.Equals(o2)
回报 真正; 除此以外, 假。
(来自消息来源。)
但这意味着 a.Equals(b)
哪里 Object.ReferenceEquals(a, b)
是假的,也不是 null
, 然后 Object.Equals(object)
电话 ReferenceHelper.Equals(object, object)
电话 Object.Equals(object)
,......这似乎是一个文档错误(对于不覆盖的类型,运行时行为不是递归的 Equals(object)
然后调用不同的对象导致a false
参考平等结果)。
我认为这个页面的其他地方有一些混乱。请注意 数字3和4之间有区别!。另一个容易被误解的观点是 base.Equals
实例方法(#1)调用 RuntimeHelpers.Equals
版本,和 不 它自己的静态方法 Object.ReferenceEquals
。
虚拟布尔 ((目的)这个)。等于(目的)
[链接到源]
[__DynamicallyInvokable]
public virtual bool Equals(object obj) => RuntimeHelpers.Equals(this, obj);
这是实例中的方法 Object
基类。如上所述,这可以通过调用来避免无限递归 RuntimeHelpers
无法覆盖的版本。
静态布尔 的Object.Equals(对象,对象)
[链接到源]
public static bool Equals(Object objA, Object objB)
{
if (objA == objB)
return true;
if (objA == null || objB == null)
return false;
return objA.Equals(objB);
}
静态布尔 Object.ReferenceEquals(对象,对象)
[链接到源]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[NonVersionable, __DynamicallyInvokable]
public static bool ReferenceEquals(Object objA, Object objB)
{
return objA == objB;
}
结果是最简单的运行时代码。通常最终内联两个引用类型的句柄值的简单CPU比较。不调用用户定义的 Equals
覆盖并且不试图以任何方式将非引用类型等同起来。也就是说,没有两种值类型,blittable原语,枚举等等等。
静态布尔 RuntimeHelpers.Equals(对象,对象)
[链接到源]
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public new static extern bool Equals(object o1, object o2);
请注意 extern
关键字:没有IL代码;这会直接跳转到CLR内部代码。还要注意这是一个 newslot
静态方法,所以你必须用“R̲u̲n̲t̲i̲m̲e̲H̲e̲l̲p̲e̲r̲s̲.Equals
“在任何一个电话网站或你会得到的 非常 实例方法的不同行为(#2) Object.Equals
。
覆盖bool ((值类型)这个)。等于(目的)
[链接到源]
(未显示)
无论如何,可能会受到JIT拦截。可能会结束 runtimecallablewrapper.cpp。
这里还有更多要讨论的内容。一个主要因素是很多行为受到特殊JIT处理的严重影响或拦截,其中一些可能取决于在运行时遇到的实例是否可能是值类型,或者JIT是否可以将其排除在外。我也不是这些问题的专家,所以请随意评论和/或纠正。让我知道是否有关于JIT结果的更多细节的兴趣,我可能会稍微扩展一下。