I got here after reading this and I didn\'t find a relevant answer - So please don\'t mark this as a duplicate until you read the whole question.
I\'ve been using a refl
MSDN's page on object.Equals(object) covers this in some detail. Specifically, the default implementation for reference types is reference equality. The table in the section "Notes for Inheritors" is the most direct.
Reference equality; equivalent to calling Object.ReferenceEquals.
MSDN's page on RuntimeHelpers.Equals(object,object) does say that Object.Equals(Object)
is called in the case that its arguments are not reference equal and neither is null. This is demonstrably false; the behavior actually exhibited is that RuntimeHelpers.Equals(object,object)
never calls Object.Equals(Object)
.
For example, this LINQPad script:
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;
}
}
prints the output below:
False
False
Bar.Equals() called
False
False
Baz.Equals() called
False
False
Qux.Equals() called
True
False
So I cribbed a little from an answer Hans Passant gave about Math.Pow()...
This is the relevant code from \clr\src\vm\ecall.cpp in 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()
This is the code for the function in \clr\src\vm\comobject.cpp to which it is mapped:
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
I see the reference comparison, null checks, value type exclusion, type match check, and a bitwise equality comparison. I don't see how Object.Equals(Object)
is ever called. I believe that the documentation for RuntimeHelpers.Equals(object,object)
is simply incorrect.