简体   繁体   English

C#,在重写GetHashCode和Equals时应考虑哪些类字段/成员?

[英]C#, Which class fields/members should be considered when overriding GetHashCode and Equals?

There is this excelent question and answer about this topic: Do I HAVE to override GetHashCode and Equals in new Classes? 关于此主题有一个出色的问答: 我是否必须重写新类中的GetHashCode和Equals?

As it mentions: 正如它提到的:

you only need to override them if you need value equality semantics. 仅在需要值相等语义时才需要覆盖它们。 The System.Object implementation isn't 'bad', it just only does a reference check (which is all an implementation at that level can do). System.Object实现不是“不好的”,它仅执行引用检查(该级别的所有实现都可以做到)。

In short: If you need some sort of value based equality (equality based on properties of the class), then yes, override away. 简而言之:如果您需要某种基于值的相等性(基于类的属性的相等性),则可以,请覆盖掉。 Otherwise, it should be more than fine already. 否则,应该已经足够了。

Let's suppose I have a class User: 假设我有一个User类:

public class User: IEquatable<User>
{
    private readonly string _firstName;
    private readonly string _lastName;
    private readonly string _address;

    public User (string firstName, string lastName, string address)
    {       
        this._firstName = firstName;
        this._lastName = lastName;
        this._address = address;
    }

    public FirstName {get; private set;}
    public LastName {get; private set;}
    public Address {get; private set;}


    //should I need to override this?
    public override bool Equals(object right)
    {
        if (object.ReferenceEquals(right, null))
            return false;

        if (object.ReferenceEquals(this, right))
            return true;

        if (this.GetType() != right.GetType())
            return false;

        return this.Equals(right as User);
    }

    #region IEquatable<User> Members
    public bool Equals(User user)
    {
        bool isEqual = (this._firstName != null && this._firstName.Equals(user.FirstName, StringComparison.InvariantCultureIgnoreCase)) || 
                      (this._lastName != null && this._lastName.Equals(user.LastName, StringComparison.InvariantCultureIgnoreCase)) ||
                      (this._address != null && this._address.Equals(user.Address, StringComparison.InvariantCultureIgnoreCase)) ||
                      (this._firstName == null && this._lastName == null && this._address == null);
        return isEqual; 
    }
    #endregion

}

User user1 = new User("John", "Wayne", "Collins Avenue");
User user2 = new User("John", "Wayne", "Collins Avenue");

//if I don't override these methods, reference equals will be:
user1 == user2 // false

//if I override GetHashCode and Equals methods, then:
user1 == user2 //true

IList<User> usersList1 = new List<User>();
IList<User> usersList2 = new List<User>();

usersList1.Add(user1);
usersList2.Add(user2);

IList<User> finalUsersList = usersList1.Union(usersList2);

//if I don't override these methods, count will be:
finalUsersList.Count() // 2
//if I do override these methods, count will be:
finalUsersList.Count() // 1 

Is it right? 这样对吗?

  1. The first Equals override method commented is required? 需要注释的第一个Equals覆盖方法吗?
  2. In this case, which class members should I include in the GetHashCode override? 在这种情况下,应该在GetHashCode重写中包括哪些类成员? All the members involved in the Equals method? 所有参与平等方法的成员?

     public override int GetHashCode() { unchecked { // Hash -> primes int hash = 17; hash = hash * 23 + FirstName.GetHashCode(); hash = hash * 23 + LastName.GetHashCode(); hash = hash * 23 + Address.GetHashCode(); return hash; } } 

What happens if I only use FirstName for example? 例如,如果我仅使用名字,会发生什么?

The first Equals override method commented is required? 需要注释的第一个Equals覆盖方法吗?

Some comparisons use the generic version, some use the non-generic version. 有些比较使用通用版本,有些则使用非通用版本。 Since it's a fairly trivial implementation if you already have the generic version there's no harm in implementing it. 由于这是一个相当琐碎的实现,如果您已经拥有通用版本,则实现它不会有任何危害。

In this case, which class members should I include in the GetHashCode override? 在这种情况下,应该在GetHashCode重写中包括哪些类成员? All the members involved in the Equals method? 所有参与平等方法的成员?

The only requirement for GetHashCode is that two object that are "equal" must return the same hash code (the reverse is not true - two equal hash codes does not imply equal objects). GetHashCode的唯一要求是,两个“相等”的对象必须返回相同的哈希码(反之则不成立-两个相等的哈希码并不意味着相等的对象)。

So your GetHashCode can do anything from returning a constant (punt and use Equals to determine equality) to an elaborate function that returns as many distinct hash codes as possible. 因此,您的GetHashCode可以执行任何操作,从返回常量(平移并使用Equals确定相等性)到复杂的函数,该函数将返回尽可能多的不同哈希码。

For reasonable performance when using hash-based collections, you should design your GetHashCode logic to minimize collisions. 为了在使用基于哈希的集合时获得合理的性能 ,应设计GetHashCode逻辑以最大程度地减少冲突。 This is typically done by iteratively multiplying the hash code by a prime number as you are doing. 通常,这是通过在执行操作时将哈希码乘以质数来迭代完成的。

The other key is that hash codes cannot change , which means that the values that derive the hash code cannot change. 另一个关键是哈希码不能更改 ,这意味着派生哈希码的值不能更改。 If a hash code changed over the life of an object, it would be impossible to find the item in a dictionary, since it stores item based on their hash value. 如果哈希码在对象的生命周期内发生了变化,则不可能在字典中找到该项目,因为它会基于其哈希值存储项目。

If you want to define "equality" based on a value that can change, you would be better served doing that in a separate class that implements IEqualityComparer , with the caveat that the objects should not be modified if they are to be used to do hash-based lookups. 如果要基于可以更改的值定义“平等”,则最好在实现IEqualityComparer的单独类中进行此操作,并告诫不要修改对象(如果要使用它们进行哈希处理)基于的查找。

What happens if I only use FirstName for example? 例如,如果我仅使用FirstName ,会发生什么?

You may get more collisions than if you used all relevant fields, which just means that the system has to do more work when looking up an item in a hashed-based collection. 与使用所有相关字段相比,您可能会遇到更多的冲突,这仅意味着在基于散列的集合中查找项目时,系统必须做更多的工作。 It first finds all objects with the computed hash, then checks then against the original object using Equals . 它首先查找具有计算出的哈希值的所有对象,然后使用Equals对照原始对象进行检查。

Note that in your implementation you should do a null-check of FirstName , LastName , and Address : 请注意,在实现中,您应该对FirstNameLastNameAddress进行空检查:

    hash = hash * 23 + (FirstName == null ? 0 : FirstName.GetHashCode());
    hash = hash * 23 + (LastName  == null ? 0 : LastName.GetHashCode());
    hash = hash * 23 + (Address   == null ? 0 : Address.GetHashCode());

This will depend on how you intend to compare your users. 这将取决于您打算如何比较用户。 If you want equality comparisons to return true only when you are comparing two references to the same user object, then your equals override is not needed. 如果希望相等比较仅在将两个引用与同一个用户对象进行比较时返回true,则不需要equals覆盖。

However, if you want to compare users based on some other logic, then the answer as to which fields you should use in your equals and GetHashCode implementations depends on your specific context. 但是,如果要基于其他逻辑比较用户,则关于在equals和GetHashCode实现中应使用哪些字段的答案取决于您的特定上下文。 When doing equality comparisons would you consider two users equal if they have the same first name and last name? 在进行相等比较时,如果两个用户的名字和姓氏相同,您会认为两个用户相等吗? What about if they have the same first name and last name but not the same address? 如果他们的名字和姓氏相同但地址不同怎么办? Whichever fields you think define a unique user are the ones that you should use. 您认为应该使用哪个字段定义唯一用户。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 GetHashCode等于C#中的类的实现 - GetHashCode Equals implementation for a class in C# C#运算符重载==和pragma警告660和661在不相关时不覆盖Equals和GetHashCode - C# operator overloading == and pragma warnings 660 & 661 not overriding Equals and GetHashCode when irrelevant C#Equals和GetHashCode - C# Equals and GetHashCode 在C#中简化覆盖等于(),GetHashCode()以获得更好的可维护性 - Simplify Overriding Equals(), GetHashCode() in C# for Better Maintainability 覆盖一个类型的Equals和GetHashCode,它有&#39;dibs&#39;? - Overriding the Equals and GetHashCode of a type, which has 'dibs'? 所有c#类应该实现Equals和GetHashCode吗? - should all c# classes implement Equals and GetHashCode? 覆盖Equals和GetHashCode-派生类中的默认实现 - Overriding Equals and GetHashCode - default implementation in derived class 当类实现Equals和GetHashCode时,如何将对象引用用作C#字典中的键? - How do you use an object reference as a key in a C# dictionary when the class implements Equals and GetHashCode? C# 覆盖 GetHashCode 方法 - C# Overriding the GetHashCode method 覆盖C#中的Object.Equals()实例方法;现在代码分析/ FxCop警告CA2218:“还应该重新定义GetHashCode”。我该抑制吗? - Overriding Object.Equals() instance method in C#; now Code Analysis/FxCop warning CA2218: “should also redefine GetHashCode”. Should I suppress this?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM