50

Let A be a class with some members as x, y, z:

Class A {
  int x;
  int y;
  String z;
  ...
}

A is an Object so it inherits the "Equals" functions defined in Object. What is the default behavior of this function? Does it check for the equality of members or does it check for reference equality?

Mostafa Mahdieh
  • 966
  • 3
  • 12
  • 23

3 Answers3

64

The default implementation of Equals supports reference equality for reference types, and bitwise equality for value types. Reference equality means the object references that are compared refer to the same object. Bitwise equality means the objects that are compared have the same binary representation.

http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

Greg
  • 16,540
  • 9
  • 51
  • 97
  • 3
    Things are actually more complicated for value types: it's not aways biwise equality comparison. See this question for details http://stackoverflow.com/q/8315656/129073 – Gebb Nov 29 '11 at 21:14
  • 1
    string is special. string.equals is bitwise equality, while string is reference type. – Will Yu Dec 30 '14 at 01:14
  • 5
    @WillYu Not bitwise equality. It tests if the characters are the same. Bitwise equality would mean that all .NET metadata (such as whether the string is interned or not) would be the same. It's also not special (in this regard), it just overrides Equals, and overloads the (in)equality operator. The default implementation for it would still be reference equality. – Aidiakapi Apr 19 '15 at 10:54
9

it checks for reference unless you override equals

UshaP
  • 1,271
  • 2
  • 18
  • 32
1

For "I want to know exactly how it works" kind of people below is the source code reference. I'm not sure though how the "loader trick" described in the comment in the first code fragment relates to the fact that in C++ code there is also handling for a ValueType in the same method.

https://source.dot.net/#System.Private.CoreLib/Object.cs,50

// Returns a boolean indicating if the passed in object obj is
// Equal to this.  Equality is defined as object equality for reference
// types and bitwise equality for value types using a loader trick to
// replace Equals with EqualsValue for value types).
public virtual bool Equals(object? obj)
{
    return RuntimeHelpers.Equals(this, obj);
}

https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,105

[MethodImpl(MethodImplOptions.InternalCall)]
public static extern new bool Equals(object? o1, object? o2);

Looking at https://github.com/dotnet/runtime/blob/603ebe97e2202bfa81d26cda146bddd53fde7f6b/src/coreclr/src/vm/ecalllist.h#L913 we can see it points to:

https://github.com/dotnet/runtime/blob/603ebe97e2202bfa81d26cda146bddd53fde7f6b/src/coreclr/src/classlibnative/bcltype/objectnative.cpp#L140

FCIMPL2(FC_BOOL_RET, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef)
{
    CONTRACTL
    {
        FCALL_CHECK;
        INJECT_FAULT(FCThrow(kOutOfMemoryException););
    }
    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->IsValueType())
        FC_RETURN_BOOL(FALSE);

    // Make sure they are the same type.
    if (pThisMT != pCompareRef->GetMethodTable())
        FC_RETURN_BOOL(FALSE);

    // Compare the contents (size - vtable - sync block index).
    DWORD dwBaseSize = pThisRef->GetMethodTable()->GetBaseSize();
    if(pThisRef->GetMethodTable() == g_pStringClass)
        dwBaseSize -= sizeof(WCHAR);
    BOOL ret = memcmp(
        (void *) (pThisRef+1),
        (void *) (pCompareRef+1),
        dwBaseSize - sizeof(Object) - sizeof(int)) == 0;

    FC_GC_POLL_RET();

    FC_RETURN_BOOL(ret);
}
FCIMPLEND
user1121956
  • 1,843
  • 2
  • 20
  • 34
  • 1
    it may be worth to note that on the other hand the == operator used in Object.ReferenceEquals is implemented by C# and if not overloaded (like it's not overloaded in Object type) emits CEQ instruction which compares two values (object references) https://stackoverflow.com/questions/5225273/difference-between-the-ceq-msil-command-and-object-internalequals https://stackoverflow.com/questions/34172634/where-is-operator-defined-in-class-object – user1121956 May 26 '20 at 02:16