IEnumerable<T>.Contains 默认不使用我自己的 IEqualityComparer

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

我实现了 IEqualityComparer,但得到的比较不正确,甚至没有看到调试器在被 IEnumerable.Contains 方法使用时通过我的 Equals 方法。

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();
    }
}

基于 MSDN documentation :

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

这导致:

The default instance of the EqualityComparer class for type T.

据我了解,他们说将使用我自己的比较器。但是这个 returns false:

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

所以,因为它应该 return true 并且调试器没有在我的 Equals 方法处停止,所以我使用了 Contains overload,得到我想要的 true.

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

我错过了什么?不是应该使用 EqualityComparer 我的 CustomMailAddress 有 "by default" 吗?

请记住,实施 IEqualityComparer 意味着 class CustomMailAddress 可以比较类型 CustomMailAddress 的对象,例如public struct AppleComparer: IEqualityComparer<Apple> 表示 AppleComparer 可以比较苹果。

您真正想要的是覆盖 Equals 和 GetHashCode

public struct 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 override bool Equals(object obj)
    {
        if ((obj is CustomMailAddress y))
        {
            return this.IsFullAddress ? this.Address.EndsWith(y.Address) : y.Address.EndsWith(this.Address);
        }

        return false;
    }

    public override int GetHashCode()
    {
        return Address.GetHashCode();
    }
}

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

他们从来没有这么说过。你误会了。如果你的理解是正确的,他们会说类似 "if T implements IEqualityComparer<T>, an instance of T is returned".

但是想想看,IEqualityComparer<T>.Default怎么会知道return一个T的实例呢?你没有保证任何无参数构造函数或类似的东西!

"default equality comparer" 实际上调用了 IEquatable<T>Equals 方法,所以我想你可能混淆了这两个接口。你的结构应该实现 IEquatable<T>,而不是 IEqualityComparer.