开发者

Overridding Equals and GetHash

开发者 https://www.devze.com 2023-01-04 23:46 出处:网络
I have read that when you override Equals on an class/object you need to override GetHashCode. public class Person : IEquatable<Person>

I have read that when you override Equals on an class/object you need to override GetHashCode.

 public class Person : IEquatable<Person>
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Person(int personId, string firstName, string lastName)
        {
            PersonId = personId;
            FirstName = firstName;
            LastName = lastName;

        }

        public bool Equals(Person obj)
        {
            Person p = obj as Person;

            if (ReferenceEquals(null, p)) 
                return false;
            if (ReferenceEquals(this, p)) 
                return true;

            return Equals(p.FirstName, FirstName) && 
                   Equals(p.LastName, LastName);
        }


    }

Now given the following:

 public static Dictionary<Person, Person> ObjDic= new Dictionary<Person, Person>();
 public static 开发者_运维知识库Dictionary<int, Person> PKDic = new Dictionary<int, Person>();

Will not overridding the GetHashCode affect both of the Dictionary's above? What I am basically asking is how is GetHashCode generated? IF I still look for an object in PKDic will I be able to find it just based of the PK. If I wanted to override the GetHashCode how would one go about doing that?


You should always override GetHashCode.

A Dictionary<int, Person> will function without GetHashCode, but as soon as you call LINQ methods like Distinct or GroupBy, it will stop working.

Note, by the way, that you haven't actually overridden Equals either. The IEquatable.Equals method is not the same as the virtual bool Equals(object obj) inherited from Object. Although the default IEqualityComparer<T> will use the IEquatable<T> interface if the class implements it, you should still override Equals, because other code might not.

In your case, you should override Equals and GetHashCode like this:

public override bool Equals(object obj) { return Equals(obj as Person); }
public override int GetHashCode() {
    return FirstName.GetHashCode() ^ LastName.GetHashCode();
}


In your scenario, not overriding GetHashCode on your type will affect only the first dictionary, as the key is what's used for hashing, not the value.

When looking for the presence of a key, the Dictionary<TKey,TValue> will use the hash code to find out if any keys could be equal. It's important to note that a hash is a value that can determine if two things could be equal or very likely are equal. A hash, strictly speaking cannot determine if two items are equal.

Two equal objects are required to return the same hash code. However, two non-equal objects are not required to return different hash codes. In other words, if the hash codes don't match, you're guaranteed that the objects are not equal. If the hash codes do match, then the objects could be equal.

Because of this, the Dictionary will only call Equals on two objects if their hash codes match.

As to "how to override GetHashCode", that's a complicated question. Clasically, a hashing algorithm should provide a balance between even distribution of the codes over the set of values with a low collision rate (a collision is when two non-equal objects produce the same code). This is a simple thing to describe and a very difficult thing to accomplish. It's easy to do one or the other, but hard to balance them.

From a practical perspective (meaning disregarding performance), you could just XOR all of the characters of the first and last names (or even use their respective hash codes, as Joel suggests) as your hash code. This will give a low degree of collision, but won't result in a terribly even distribution. Unless you're dealing with very large sets or very frequent lookups, it won't be an issue.


Your GetHashCode() and Equals() methods should look like this:

public int GetHashCode()
{
    return (FirstName.GetHashCode()+1) ^ (LastName.GetHashCode()+2);
}


public bool Equals(Object obj)
{
    Person p = obj as Person;

    if (p == null) 
        return false;

    return this.Firstname == p.FirstName && this.LastName == p.Lastname;
}

The rule is that GetHashCode() must use exactly the fields used in determining equality for the .Equals() method.

As for the dictionary part of your question, .GetHashCode() is used for determining the key in a dictionary. However, this has a different impact for each of the dictionarys in your question.

The dictionary with the int key (presumably your person ID) will use the GetHashCode() for the integer, while the other dictionary (ObjDic) will use the GetHashCode() from your Person object. Therefore PKDic will always differentiate between two people with different IDs, while ObjDic might treat two people with different IDs but the same first and last names as the same record.


Here is how I would do it. Since it is common for two different people to have exactly the same name it makes more sense to use a unique identifier (which you already have).

public class Person : IEquatable<Person>
{
  public override int GetHashCode()
  {
    return PersonId.GetHashCode();
  }

  public override bool Equals(object obj)
  {
    var that = obj as Person;
    if (that != null)
    {
      return Equals(that);
    }
    return false;
  }

  public bool Equals(Person that)
  {
    return this.PersonId == that.PersonId;
  }
}

To answer your specific question: This only matters if you are using Person as a key in an IDictionary collection. For example, Dictionary<Person, string> or SortedDictionary<Person, Foo>, but not Dictionary<int, Person>.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号