当相同类型的两个对象具有相同的值时,为什么哈希码不同?

本文关键字:哈希码 为什么 对象 同类型 两个 | 更新日期: 2023-09-27 18:07:40

我的理解是,GetHashCode将为共享相同值的两个不同实例返回相同的值。MSDN文档在这一点上有点模糊。

哈希码是一个用于标识对象的数值在相等性测试期间。

如果我有两个相同类型和相同值的实例,GetHashCode()会返回相同的值吗?

假设所有的值都是相同的,下面的测试是通过还是失败?

SecurityUser只有getter和setter;

    [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
}

当相同类型的两个对象具有相同的值时,为什么哈希码不同?

框架为您的自定义对象计算的哈希码不能保证相同。

我相信这是由于框架没有遍历你所有的字段和计算它们的哈希码,这将是这样一个时间昂贵的事情为每个对象做(我可能是错的)。

这就是为什么建议您在自己的类型上重写Equals()GetHashCode()方法的原因。

参见:重写GetHashCode

From MSDN:

GetHashCode方法的默认实现没有保证不同对象的唯一返回值。此外,的默认实现GetHashCode方法,它返回的值在两者之间是相同的不同版本的。net框架。因此,默认的此方法的实现不能用作唯一对象用于散列的标识符。

GetHashCode方法可以被派生类型覆盖。价值类型必须覆盖此方法以提供散列函数为该类型提供了一个有用的分布哈希表。为了实现唯一性,哈希码必须基于该值的实例字段或属性,而不是静态字段或财产。

这意味着你应该在你的类中重写GetHashCode

如果SecurityUser类存储的ID随着您创建的每个用户的增加而增加,则它们可能会有所不同。如果类使用它来计算它的HashCode,它们可能是不同的。你不应该依赖GetHashCode来测试两个对象之间的相等性。

GetHashCode的唯一要求是如果objA.Equals(objB),则objA.GetHashCode() == objB.GetHashCode()

关于GetHashCode()实现的详细信息,请参阅此链接("对实现者的说明"一节),特别是这一段:

  • 如果两个对象比较为相等,则分别使用GetHashCode方法对象必须返回相同的值。但是,如果两个对象没有如果两个对象的GetHashCode方法相等,则不相等必须返回不同的值

如果GetHashCode() SecurityUser中没有被覆盖,则两个HashCodes将不同,因为objAobjB两个对象是对内存中不同对象的引用(由new -Keyword表示)。

c#中的HashCodes并不像看起来那么简单。默认情况下,一个类不会为两个相同的实例返回相同的哈希码,您必须自己创建该行为。哈希码在特定场景中用于优化查找,但至少有一个创始开发人员说,如果他们有机会重新开始,GetHashCode()不会成为基本对象方法之一。

对于值类型,GetHashCode()将为具有相同值的两个对象返回相同的哈希值。然而,SecurityUser是一个引用类型,因此,它的默认GetHashCode()方法(如前所述,继承自System.Object)返回基于对象引用的哈希值。由于SecurityUser的两个不同的实例不共享相同的引用,因此它们不共享相同的哈希码。

您可以通过重写SecurityUser中的GetHashCode()方法来重写此行为,以便从类的成员计算散列,而不是从类本身。确保您也覆盖Equals(),因为这两个方法是齐头并进的。还可以考虑重写==相等操作符。

查看这篇文章中关于GetHashCode()实现的一个很好的例子:覆盖System.Object.GetHashCode的最佳算法是什么?