How can overriding struct's Equals method improve performance in C#?

Viewed 203

I am reading C# in a Nutshell and I encounter this paragraph:

The default structural equality comparison algorithm for structs is relatively slow. Taking over this process by overriding Equals can improve performance by a factor of five. Overloading the == operator and implementing IEquatable<T> allows unboxed equality comparisons, and this can speed things up by a factor of five again.

I understand that defining == & != operators can increase performance by preventing unnecessary boxing, but I can't understand why overriding Equals can improve performance by a factor of 5. can anybody describe it to me?

Thanks in advance!

1 Answers

The default Equals implementation also uses boxing (and reflection):

public override bool Equals([NotNullWhen(true)] object? obj)
{
    if (null == obj)
    {
        return false;
    }
    Type thisType = this.GetType(); // <------------ Reflection
    Type thatType = obj.GetType();

    if (thatType != thisType)
    {
        return false;
    }

    object thisObj = (object)this;
    object? thisResult, thatResult; // <------------ Boxing

    // if there are no GC references in this object we can avoid reflection
    // and do a fast memcmp
    if (CanCompareBits(this))
        return FastEqualsCheck(thisObj, obj);

    FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

    for (int i = 0; i < thisFields.Length; i++)
    {
        thisResult = thisFields[i].GetValue(thisObj);
        thatResult = thisFields[i].GetValue(obj);

        if (thisResult == null)
        {
            if (thatResult != null)
                return false;
        }
        else
        if (!thisResult.Equals(thatResult))
        {
            return false;
        }
    }

    return true;
}

It's also worth mentioning that the GetHashCode implementation only hashes the first non-null field, so depending on the situation you might encounter performance issues due to hash collisions. Just override both.

Related