使用自定义比较器的LinQ distinct会留下重复项

9
我有以下几个类:

public class SupplierCategory : IEquatable<SupplierCategory>
{
    public string Name { get; set; }
    public string Parent { get; set; }

    #region IEquatable<SupplierCategory> Members

    public bool Equals(SupplierCategory other)
    {
        return this.Name == other.Name && this.Parent == other.Parent;
    }

    #endregion
}

public class CategoryPathComparer : IEqualityComparer<List<SupplierCategory>>
{
    #region IEqualityComparer<List<SupplierCategory>> Members

    public bool Equals(List<SupplierCategory> x, List<SupplierCategory> y)
    {
        return x.SequenceEqual(y);
    }

    public int GetHashCode(List<SupplierCategory> obj)
    {
        return obj.GetHashCode();
    }

    #endregion
}

我正在使用以下linq查询:

CategoryPathComparer comparer = new CategoryPathComparer();
List<List<SupplierCategory>> categoryPaths = (from i in infoList
                                                          select
                                                            new List<SupplierCategory>() { 
                                                             new SupplierCategory() { Name = i[3] },
                                                             new SupplierCategory() { Name = i[4], Parent = i[3] },
                                                             new SupplierCategory() { Name = i[5], Parent = i[4] }}).Distinct(comparer).ToList();

但是这个distinct并没有按照我的期望执行,以下代码演示了这种情况:
comp.Equals(categoryPaths[0], categoryPaths[1]); //returns True

我是否使用方法不当?为什么它们没有按照我的意图进行比较?

编辑: 为了证明比较器确实起作用,以下代码返回true,正如它应该做的:

List<SupplierCategory> list1 = new List<SupplierCategory>() {
    new SupplierCategory() { Name = "Cat1" },
    new SupplierCategory() { Name = "Cat2", Parent = "Cat1" },
    new SupplierCategory() { Name = "Cat3", Parent = "Cat2" }
};
List<SupplierCategory> list1 = new List<SupplierCategory>() {
    new SupplierCategory() { Name = "Cat1" },
    new SupplierCategory() { Name = "Cat2", Parent = "Cat1" },
    new SupplierCategory() { Name = "Cat3", Parent = "Cat2" }
};
CategoryPathComparer comp = new CategoryPathComparer();
Console.WriteLine(comp.Equals(list1, list2).ToString());

重新标记。没有C#3.5(请参见https://dev59.com/hnVC5IYBdhLWcg3wliKe) - Vaccano
2个回答

11

你的问题在于没有正确实现 IEqualityComparer 接口。

当你实现 IEqualityComparer<T> 接口时,必须要实现 GetHashCode 方法,以便任何两个相等的对象具有相同的哈希值。

否则,你会得到不正确的行为,就像你在这里看到的一样。

你应该按照以下方式实现 GetHashCode:(引用自这篇答案)

public int GetHashCode(List<SupplierCategory> obj) {
    int hash = 17;

    foreach(var value in obj)
        hash = hash * 23 + obj.GetHashCode();

    return hash;
}

你还需要覆盖 SupplierCategory 中的 GetHashCode 方法以保持一致。例如:

public override int GetHashCode() {
    int hash = 17;
    hash = hash * 23 + Name.GetHashCode();
    hash = hash * 23 + Parent.GetHashCode();
    return hash;
}

最后,虽然你不需要这样做,但你应该覆盖 SupplierCategory 中的 Equals 方法,并让它调用你为 IEquatable 实现的 Equals 方法。


4

不,我的意思是文档包含了VB和C#的代码示例,展示如何创建自己的比较器。它还展示了如何重写GetHashCode和Equals方法。 - Alexandra Rusina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接