Search code examples
c#.netiequalitycomparer

IEnumerable<T>.Contains is not using my own IEqualityComparer by default


I implemented IEqualityComparer and I was getting incorrect comparisons, not even watching the debugger passing through my Equals method when being used by IEnumerable.Contains method.

public struct CustomMailAddress : IEqualityComparer<CustomMailAddress>
{
    public string Address { get; private set; }

    public string DisplayName { get; private set; }

    public bool IsFullAddress { get; private set; }

    public CustomMailAddress(string address)
    {
        this.Address = address;
        this.DisplayName = String.Empty;
        this.IsFullAddress = address.IndexOf('@') >= 0;
    }

    public CustomMailAddress(MailAddress address)
    {
        this.Address = address.Address;
        this.DisplayName = address.DisplayName;
        this.IsFullAddress = true;
    }

    public bool Equals(CustomMailAddress x, CustomMailAddress y)
    {
        return x.IsFullAddress ? x.Address.EndsWith(y.Address) : y.Address.EndsWith(x.Address);
    }

    public int GetHashCode(CustomMailAddress obj)
    {
        return obj.Address.GetHashCode();
    }
}

Based on the MSDN documentation :

Elements are compared to the specified value by using the default equality comparer, Default.

Which leads to:

The default instance of the EqualityComparer class for type T.

From my understanding, they say my own comparer will be used. But this returns false:

bool isMatch = source.CollectedAddresses.Any(x => _validAddreses.Contains(x));

So, since it should return true and the debugger wasn't stopping at my Equals method I used the Contains overload, getting my desired true.

bool isMatch = source.CollectedAddresses.Any(x => _validAddreses.Contains(x, new CustomMailAddress()));

What am I missing? Wasn't it supposed to use the EqualityComparer my CustomMailAddress has "by default"?


Solution

  • From my understanding, they say my own comparer will be used.

    They never said that. You have misunderstood. If your understanding were correct, they would have said something like, "if T implements IEqualityComparer<T>, an instance of T is returned".

    But think about it, how can IEqualityComparer<T>.Default know how to return an instance of T? You haven't guaranteed it any parameterless constructors or anything like that!

    The "default equality comparer" actually calls the Equals method of IEquatable<T>, so I think you probably have mixed up the two interfaces. Your struct should implement IEquatable<T>, not IEqualityComparer.