Distinct()如何在对象列表上工作?

时间:2013-07-17 09:32:22

标签: c# linq distinct

var people = new List<Person>
{
    new Person
    {
        Id = 1,
        Name = "Atish"
    },
    new Person
    {
        Id = 2,
        Name = "Dipongkor"
    },
    new Person
    {
        Id = 1,
        Name = "Atish"
    }
};

Console.WriteLine(people.Distinct().Count());

为什么输出 3

为什么不是 2

2 个答案:

答案 0 :(得分:6)

引用类型的默认相等比较器是引用相等性,如果两个对象引用指向相同的实例(即通过单个true创建),则仅返回new声明)。这与测试值相等的值类型不同,如果所有数据字段相等(如您的情况中为两个),则返回true。更多信息:Equality Comparisons (C# Programming Guide)

如果要更改此行为,则需要在类型上实现通用IEquatable<T>接口,以便比较实例的属性是否相等。 Distinct运算符随后将自动选择此实现并产生预期结果。

修改:以下是您班级IEquatable<Person>的示例实现:

public class Person : IEquatable<Person>
{
    public int Id { get; set; }
    public int Name { get; set; }

    public bool Equals(Person other)
    {
        if (other == null)
            return false;

        return Object.ReferenceEquals(this, other) ||
            this.Id == other.Id &&
            this.Name == other.Name;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Person);
    }

    public override int GetHashCode()
    {
        int hash = this.Id.GetHashCode();
        if (this.Name != null)
            hash ^= this.Name.GetHashCode();
        return hash;
    }
}

从覆盖==!=运营商的guidelines开始(强调添加):

  

默认情况下,运算符==通过确定两个引用是否指示同一对象来测试引用相等性。因此,引用类型不必实现运算符==以获得此功能。当一个类型是不可变的,也就是说,实例中包含的数据不能被更改时,重载运算符==来比较值的相等而不是引用相等可能是有用的,因为作为不可变对象,它们可以被认为是只要它们具有相同的值即可。 在非不可变类型中覆盖运算符==不是一个好主意。

答案 1 :(得分:3)

这是因为你没有在你的班级中重写平等。现在,当您使用distinct时,它会检查引用相等性。要更改此设置,您需要覆盖相当多的内容:operator==Equals(),并获得最佳结果GetHashCode()

我就是这样做的:

public static bool operator ==(Person one, Person two)
{
    return one.Id == two.Id && one.Name == two.Name;
}
public static override bool Equals(Person one, Person two)
{
    return one == two;
}
public override bool Equals(object obj)
{
    return obj is Person && ((Person)obj) == this;
}
public bool Equals(Person other)
{
    return other == this;
}
public override int GetHashCode()
{
    unchecked
    {
        return 17 * Id * 31 * Name.GetHashCode();
    }
}

此外,您可以实施IEquatable<T>界面(我上面已经完成,您只需要确保在类标题的末尾添加: IEquatable<Person>class Person等。))然后将实施。