是否应该在引用类型上覆盖Equals始终意味着值相等?

Mat*_*ith 11 .net c#

如果没有为引用类型做任何特殊操作,则Equals()意味着引用相等(即相同的对象).如果我选择覆盖Equals()引用类型,它是否总是意味着两个对象的值是等价的?

考虑这个可变性 Person类:

class Person
{
    readonly int Id;

    string FirstName { get; set; }
    string LastName { get; set; }
    string Address { get; set; }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

表示完全相同的人的两个对象将始终具有相同的Id,但是其他字段可能随时间不同(即,在地址改变之前/之后).

对于这个对象,Equals可以定义为不同的东西:

  • 价值平等:所有字段都相等(代表同一个人但具有不同地址的两个对象将返回false)
  • 身份平等:Ids相等(代表同一个人但具有不同地址的两个对象将返回true)
  • 参考平等:即不实施等于.

问题:这个课程中哪些(如果有的话)更适合?(或许问题应该是,"这个班级的大多数客户如何期望Equals()表现?")

笔记:

  • 使用Value Equality使得在Hashsetor中使用这个类更加困难Dictionary
  • 使用Identity Equality使得Equals和=运算符之间的关系变得奇怪(即在检查两个Person对象(p1和p2)之后返回true)Equals(),您可能仍然希望更新引用以指向"较新的"Person对象,因为它是不等值).例如,以下代码读取奇怪 - 似乎它什么都不做,但它实际上是删除p1并添加p2:

    HashSet<Person> people = new HashSet<Person>();
    people.Add(p1);
    // ... p2 is an new object that has the same Id as p1 but different Address
    people.Remove(p2);
    people.Add(p2);
    
    Run Code Online (Sandbox Code Playgroud)

相关问题:

Mar*_*ell 13

是的,为此决定正确的规则是棘手的.这里没有单一的"正确"答案,它将在很大程度上取决于上下文和偏好.就个人而言,我很少考虑它,只是在大多数常规POCO类中默认引用相等:

  • 当你Person在哈希集中使用类似字典键/的东西的情况是最小的
    • 当您这样做时,您可以提供一个遵循您希望它遵循的实际规则的自定义比较器
    • 但大多数时候,无论如何,我只使用int Id字典(等)作为键
  • 使用参考相等的意思是x==y给出相同结果是否x/ yPersonobject,或实际上T在通用方法
  • 只要Equals并且GetHashCode兼容,大多数事情都会解决,一个简单的方法就是不要覆盖它们

但请注意,我总是建议相反的值类型,即显式覆盖Equals/ GetHashCode; 但是,写一个struct非常罕见的


Dus*_*gen 6

您可以提供多个IEqualityComparer(T)实现并让消费者决定.

例:

// Leave the class Equals as reference equality
class Person
{
    readonly int Id;

    string FirstName { get; set; }
    string LastName { get; set; }
    string Address { get; set; }
    // ...
}

class PersonIdentityEqualityComparer : IEqualityComparer<Person>
{
    public bool Equals(Person p1, Person p2)
    {
        if(p1 == null || p2 == null) return false;

        return p1.Id == p2.Id;
    }

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

class PersonValueEqualityComparer : IEqualityComparer<Person>
{
    public bool Equals(Person p1, Person p2)
    {
        if(p1 == null || p2 == null) return false;

        return p1.Id == p2.Id &&
               p1.FirstName == p2.FirstName; // etc
    }

    public int GetHashCode(Person p)
    {
        int hash = 17;

        hash = hash * 23 + p.Id.GetHashCode();
        hash = hash * 23 + p.FirstName.GetHashCode();
        // etc

        return hash;
    }
}
Run Code Online (Sandbox Code Playgroud)

另请参阅:重写的System.Object.GetHashCode的最佳算法是什么?

用法:

var personIdentityComparer = new PersonIdentityEqualityComparer();
var personValueComparer = new PersonValueEqualityComparer();

var joseph = new Person { Id = 1, FirstName = "Joseph" }

var persons = new List<Person>
{
   new Person { Id = 1, FirstName = "Joe" },
   new Person { Id = 2, FirstName = "Mary" },
   joseph
};

var personsIdentity = new HashSet<Person>(persons, personIdentityComparer);
var personsValue = new HashSet<Person>(persons, personValueComparer);

var containsJoseph = personsIdentity.Contains(joseph);
Console.WriteLine(containsJoseph); // false;

containsJoseph = personsValue.Contains(joseph);
Console.WriteLine(containsJoseph); // true;
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

1348 次

最近记录:

12 年,5 月 前