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

本文关键字:对象 列表 工作 Distinct | 更新日期: 2023-09-27 18:23:37

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

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

引用类型的默认相等比较器是引用相等,仅当两个对象引用指向同一实例(即通过单个new语句创建(时,它才返回true。这与值类型不同,值类型测试值相等性,如果它们的所有数据字段都相等(如本例中的两个(,则会返回true。详细信息:相等比较(C# 编程指南(。

如果要更改此行为,则需要在类型上实现泛型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;
    }
}

从关于覆盖==!=运算符的准则(着重号是后加的(:

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

这是因为你没有在类中覆盖平等。现在,当您使用非重复时,它会检查引用相等性。要改变这一点,你需要覆盖一些东西: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等((,然后即可实现。