Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetHashCode override of object containing generic array

I have a class that contains the following two properties:

public int Id      { get; private set; } public T[] Values  { get; private set; } 

I have made it IEquatable<T> and overriden the object.Equals like this:

public override bool Equals(object obj) {     return Equals(obj as SimpleTableRow<T>); }  public bool Equals(SimpleTableRow<T> other) {     // Check for null     if(ReferenceEquals(other, null))         return false;      // Check for same reference     if(ReferenceEquals(this, other))         return true;      // Check for same Id and same Values     return Id == other.Id && Values.SequenceEqual(other.Values); } 

When having override object.Equals I must also override GetHashCode of course. But what code should I implement? How do I create a hashcode out of a generic array? And how do I combine it with the Id integer?

public override int GetHashCode() {     return // What? } 
like image 859
Svish Avatar asked Mar 12 '09 14:03

Svish


People also ask

When should we override the GetHashCode () method?

It's my understanding that the original GetHashCode() returns the memory address of the object, so it's essential to override it if you wish to compare two different objects. EDITED: That was incorrect, the original GetHashCode() method cannot assure the equality of 2 values.

What is the purpose of GetHashCode?

The GetHashCode method provides this hash code for algorithms that need quick checks of object equality. For information about how hash codes are used in hash tables and for some additional hash code algorithms, see the Hash Function entry in Wikipedia. Two objects that are equal return hash codes that are equal.

How does GetHashCode work C#?

The GetHashCode method provides this hash code for algorithms that need quick checks of object equality. Syntax: public virtual int GetHashCode (); Return Value: This method returns a 32-bit signed integer hash code for the current object.


2 Answers

Because of the problems raised in this thread, I'm posting another reply showing what happens if you get it wrong... mainly, that you can't use the array's GetHashCode(); the correct behaviour is that no warnings are printed when you run it... switch the comments to fix it:

using System; using System.Collections.Generic; using System.Linq; static class Program {     static void Main()     {         // first and second are logically equivalent         SimpleTableRow<int> first = new SimpleTableRow<int>(1, 2, 3, 4, 5, 6),             second = new SimpleTableRow<int>(1, 2, 3, 4, 5, 6);          if (first.Equals(second) && first.GetHashCode() != second.GetHashCode())         { // proven Equals, but GetHashCode() disagrees             Console.WriteLine("We have a problem");         }         HashSet<SimpleTableRow<int>> set = new HashSet<SimpleTableRow<int>>();         set.Add(first);         set.Add(second);         // which confuses anything that uses hash algorithms         if (set.Count != 1) Console.WriteLine("Yup, very bad indeed");     } } class SimpleTableRow<T> : IEquatable<SimpleTableRow<T>> {      public SimpleTableRow(int id, params T[] values) {         this.Id = id;         this.Values = values;     }     public int Id { get; private set; }     public T[] Values { get; private set; }      public override int GetHashCode() // wrong     {         return Id.GetHashCode() ^ Values.GetHashCode();     }     /*     public override int GetHashCode() // right     {         int hash = Id;         if (Values != null)         {             hash = (hash * 17) + Values.Length;             foreach (T t in Values)             {                 hash *= 17;                 if (t != null) hash = hash + t.GetHashCode();             }         }         return hash;     }     */     public override bool Equals(object obj)     {         return Equals(obj as SimpleTableRow<T>);     }     public bool Equals(SimpleTableRow<T> other)     {         // Check for null         if (ReferenceEquals(other, null))             return false;          // Check for same reference         if (ReferenceEquals(this, other))             return true;          // Check for same Id and same Values         return Id == other.Id && Values.SequenceEqual(other.Values);     } } 
like image 110
Marc Gravell Avatar answered Oct 25 '22 19:10

Marc Gravell


FWIW, it's very dangerous to use the contents of the Values in your hash code. You should only do this if you can guarantee that it will never change. However, since it is exposed, I don't think guaranteeing it is possible. The hashcode of an object should never change. Otherwise, it loses its value as a key in a Hashtable or Dictionary. Consider the hard-to-find bug of using an object as a key in a Hashtable, its hashcode changes because of an outside influence and you can no longer find it in the Hashtable!

like image 36
Dustin Campbell Avatar answered Oct 25 '22 21:10

Dustin Campbell