【问题标题】:IEqualityComparer<T> custom implementation and set-operationsIEqualityComparer<T> 自定义实现和设置操作
【发布时间】:2015-03-03 13:30:09
【问题描述】:

我需要在 linq 中执行简单的集合操作(​​例如 Union、Except 和 Intersect)

class Person {
        public int Id { get; set; }
        public string Name { get; set; }

        public Person() { }

        public Person(int id, string name) {
            Id = id; Name = name;
        }

    }

比较器实现:

class PersonComparer : IEqualityComparer<Person> {
        public bool Equals(Person x, Person y) {
            return x.Id == y.Id;
        }

        public int GetHashCode(Person p) {
            return p.GetHashCode();
        }
    }

填充列表:

var list1 = new List<Person>();
        list1.Add(new Person(1, "John"));
        list1.Add(new Person(2, "Peter"));
        list1.Add(new Person(3, "Mike"));

        var list2 = new List<Person>();
        list2.Add(new Person(2, "Peter"));
        list2.Add(new Person(3, "Mike"));
        list2.Add(new Person(4, "Fred"));

    var comparer = new PersonComparer();

    var list3 = list1.Intersect(list2, comparer).ToList(); // **Empty List**
    var list4 = list1.Except(list2, comparer).ToList(); // **"John", "Peter", "Mike"**

我的比较器似乎不起作用。为什么?

【问题讨论】:

    标签: c# linq iequalitycomparer set-operations


    【解决方案1】:

    问题在于您对GetHashCode(Person p) 的实现。如MSDN中所述:

    需要实现以确保如果Equals 方法 为xy 两个对象返回true,然后返回值 xGetHashCode 方法必须等于 y 的返回值。

    在您的情况下,p.GetHashCode() 可能会为内存中的每个 p 返回不同的值,即使它们具有相同的 Id - 也就是说,Person 的两个不同实例可能具有相同的 Id但不同的哈希码——因此这不足以满足上述正确实现GetHashCode(Person p) 的要求。相反,使用这样的东西:

    public int GetHashCode(Person p) {
        return p.Id;
    }
    

    【讨论】:

    • @Ivan 请记住,如果 Person 是保存到 DB 的实体,通常解决 GetHashCode/Equals 是相当复杂的:在保存 Id 之前是 0,而在保存 Id 之后是 != 0,所以哈希码已更改。因此,如果您有 HashSet 或 Dictionary 则哈希集/字典是 ko,因为它们在内部“缓存”哈希码。
    • 正如我理解的那样,我可以避免 IEqualityComparer 的实现,方法是在我的 Person 类中重写方法: ToString() 、 GetHashCode() 、 Equals() 。并且 set-operations 方法会在默认重载的情况下正常工作(没有 IEqualityComparer 参数)?
    • @IvanStelmakh 理论上是的,但您应该注意 xanatos 的谨慎。如果Id 可以在对象的生命周期内更改(它可以,因为设置器是公共的),那么该对象在任何依赖哈希码的集合中都不会表现良好。除非您知道自己在做什么,否则我建议不要修改类的默认行为。 IEqualityComparer 路由通常更好,因为它在所有其他情况下保留了 C# 类的预期行为。
    猜你喜欢
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多