Ответ 1
Страница MSDN на object.Equals(object)
подробно описывает это. В частности, реализация по умолчанию для ссылочных типов является ссылочным равенством. Таблица в разделе "Примечания для наследователей" является самой прямой.
Reference равенство; эквивалент вызова Object.ReferenceEquals.
Страница MSDN на RuntimeHelpers.Equals(object,object)
говорит, что object.Equals(object)
вызывается в случае, если его аргументы не являются ссылочными, и ни один из них не равен нулю. Это явно ложно; фактически проявленное поведение состоит в том, что RuntimeHelpers.Equals(object,object)
никогда не вызывает object.Equals(object)
.
Например, этот 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;
}
}
выводит результат ниже:
False
False
Bar.Equals() называется
False
False
Baz.Equals() называется
False
False
Qux.Equals() называется
True
False
Итак, я немного подрезал из ответ, который Ханс Пассант дал Math.Pow()
...
Это соответствующий код из \clr\src\vm\ecall.cpp в
Это код для функции в \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 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