Why are hashcodes different when two objects of the same type have the same values?
Asked Answered
G

5

5

It is my understanding that GetHashCode will return the same value for two different instances which share the same values. The MSDN documentation is a bit fuzzy on this point.

A hash code is a numeric value that is used to identify an object during equality testing.

If I have two instances of the same type and the same values will the GetHashCode() return the same value?

Assuming all the values are the same, would the following test past or fail?

SecurityUser only has getters and setters;

    [TestMethod]
    public void GetHashCode_Equal_Test()
    {
        SecurityUser objA = new SecurityUser(EmployeeName, EmployeeNumber, LastLogOnDate, Status, UserName);
        SecurityUser objB = new SecurityUser(EmployeeName, EmployeeNumber, LastLogOnDate, Status, UserName);

        int hashcodeA = objA.GetHashCode();
        int hashcodeB = objB.GetHashCode();

        Assert.AreEqual<int>(hashcodeA, hashcodeB);
    }


/// <summary>
/// This class represents a SecurityUser entity in AppSecurity.
/// </summary>
public sealed class SecurityUser
{
    #region [Constructor]

    /// <summary>
    /// Initializes a new instance of the <see cref="SecurityUser"/> class using the
    /// parameters passed.
    /// </summary>
    /// <param name="employeeName">The employee name to initialize with.</param>
    /// <param name="employeeNumber">The employee id number to initialize with.</param>
    /// <param name="lastLogOnDate">The last logon date to initialize with.</param>
    /// <param name="status">The <see cref="SecurityStatus"/> to initialize with.</param>
    /// <param name="userName">The userName to initialize with.</param>        
    public SecurityUser(
        string employeeName,
        int employeeNumber,            
        DateTime? lastLogOnDate,
        SecurityStatus status,
        string userName)
    {
        if (employeeName == null)
            throw new ArgumentNullException("employeeName");

        if (userName == null)
            throw new ArgumentNullException("userName");

        this.EmployeeName = employeeName;
        this.EmployeeNumber = employeeNumber;
        this.LastLogOnDate = lastLogOnDate;
        this.Status = status;
        this.UserName = userName;
    }

    #endregion

    #region [Properties]

    /// <summary>
    /// Gets the employee name of the current instance.
    /// </summary>
    public string EmployeeName { get; private set; }

    /// <summary>
    /// Gets the employee id number of the current instance.
    /// </summary>
    public int EmployeeNumber { get; private set; }

    /// <summary>
    /// Gets the last logon date of the current instance.
    /// </summary>
    public DateTime? LastLogOnDate { get; private set; }

    /// <summary>
    /// Gets the userName of the current instance.
    /// </summary>
    public string UserName { get; private set; }

    /// <summary>
    /// Gets the <see cref="SecurityStatus"/> of the current instance.
    /// </summary>
    public SecurityStatus Status { get; private set; }

    #endregion
}
Guarneri answered 4/4, 2013 at 17:14 Comment(2)
Where does the SecurityUser class come from? GetHashCode can be overridden in derived classes to return anything. There's no guarantee that its implementation is correct.Revile
@RobertHarvey My bad, I should have specified, it does not. SecurityUser only has getters and setters.Guarneri
R
6

Hash codes calculated by the framework for your custom objects aren't guaranteed to be identical.

I believe this is due to the framework not walking all your fields and such and calculating their hashcodes, it would be such a time-expensive thing to do for every object (I may be wrong).

This is why it is recommended that you override the Equals() and GetHashCode() methods on your own types.

See: Overriding GetHashCode

Roulette answered 4/4, 2013 at 17:16 Comment(4)
The hash code generated when you don't override GetHashCode is object.GetHashCode, which simply returns a CLR determined number based on the reference.Lido
@JasonWatkins I did believe that was the case and it defaulted down to System.Object, but I wasn't certain enough to state as such :PRoulette
@JasonWatkins Ok, that makes sense. If create two empty objects of the same type, they will (always, for the most part) return different values.Guarneri
@ChuckConway You can't know for sure, it could be identical, it could be different, so it's always best to override GetHashCode, this is of course if you're relying on the stock 'Equals()' '==' '!=' functionality or using them in hashed collections, if you're doing complex matching tests that need additional parameters then I'd recommend implementing a custom 'Match()' method.Roulette
W
4

From MSDN:

The default implementation of the GetHashCode method does not guarantee unique return values for different objects. Furthermore, the .NET Framework does not guarantee the default implementation of the GetHashCode method, and the value it returns will be the same between different versions of the .NET Framework. Consequently, the default implementation of this method must not be used as a unique object identifier for hashing purposes.

The GetHashCode method can be overridden by a derived type. Value types must override this method to provide a hash function that is appropriate for that type and to provide a useful distribution in a hash table. For uniqueness, the hash code must be based on the value of an instance field or property instead of a static field or property.

This means that you should override GetHashCode in your class.

Welkin answered 4/4, 2013 at 17:20 Comment(0)
A
3

They might be different if the class SecurityUser stores an ID that increases for each user you create. If the class uses this to calculate its HashCode, they will probably be different. You should not rely on GetHashCode to test for equality between two objects.

The only requirement for GetHashCode is that if objA.Equals(objB), then objA.GetHashCode() == objB.GetHashCode().

See this link (section "Notes to Implementers") for details on the implementation of GetHashCode(), especially this paragraph:

  • If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.

If GetHashCode() is not overridden in SecurityUser, the two HashCodes will be different as the two objects objA and objB are references to different objects in memory (as indicated by the new-Keyword).

Agulhas answered 4/4, 2013 at 17:18 Comment(0)
S
2

HashCodes in C# are not as straightforward as they might appear. By default a class will not return the same hashcode for two identical instances, you have to create that behavior yourself. Hash codes are used in specific scenarios to optimize lookup, but at least one of the founding developers has since said that if they had a chance to go back and start over, GetHashCode() would not have been one of the base object methods.

Silvey answered 4/4, 2013 at 17:21 Comment(1)
I'd forgotten that the GetHashCode() is actually object.GetHashCode()Guarneri
E
2

On a value type, GetHashCode() would return the same hash for two objects of the same value. However SecurityUser is a reference type and as such, its default GetHashCode() method (inherited from System.Object as others have mentioned) returns a hash based on the reference of the object. Since two distinct instances of SecurityUser do not share the same reference, they do not share the same hash code.

You can override this behavior by overriding the GetHashCode() method in SecurityUser so as to compute a hash off the members of your class rather than from the class itself. Make sure you also override Equals(), since these two methods go hand in hand. You might also consider overriding the == equality operator.

See the accepted answer in this post for a good example of an implementation of GetHashCode(): What is the best algorithm for an overridden System.Object.GetHashCode?

Emmittemmons answered 7/4, 2013 at 16:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.