【问题标题】:Why are hashcodes different when two objects of the same type have the same values?当相同类型的两个对象具有相同的值时,为什么哈希码会不同?
【发布时间】:2013-03-26 21:39:34
【问题描述】:

据我了解,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
}

【问题讨论】:

  • SecurityUser 类从何而来? GetHashCode 可以在派生类中被覆盖以返回任何内容。不能保证它的实现是正确的。
  • @RobertHarvey 我的错,我应该指定,它没有。 SecurityUser 只有 getter 和 setter。

标签: c# .net gethashcode


【解决方案1】:

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

我认为这是由于框架没有遍历所有字段等并计算它们的哈希码,对每个对象执行此操作将是一件非常耗时的事情(我可能错了)。

这就是为什么建议您在自己的类型上覆盖 Equals()GetHashCode() 方法。

见:Overriding GetHashCode

【讨论】:

  • 不覆盖GetHashCode时生成的哈希码是object.GetHashCode,它只是根据引用返回一个CLR确定的数字。
  • @JasonWatkins 我确实相信是这种情况,它默认为 System.Object,但我不确定这样说:P
  • @JasonWatkins 好的,这是有道理的。如果创建两个相同类型的空对象,它们将(在大多数情况下总是)返回不同的值。
  • @ChuckConway 你不能确定,它可能是相同的,也可能是不同的,所以最好覆盖GetHashCode,这当然是如果你依赖于股票'Equals()' '==' '!=' 功能或在散列集合中使用它们,如果您正在执行需要额外参数的复杂匹配测试,那么我建议您实现自定义 'Match()' 方法。跨度>
【解决方案2】:

来自MSDN

GetHashCode 方法的默认实现没有 保证不同对象的唯一返回值。此外,该 .NET Framework 不保证默认实现 GetHashCode 方法,它返回的值在 .NET Framework 的不同版本。因此,默认 此方法的实现不得用作唯一对象 用于散列目的的标识符。

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

这意味着你应该在你的类中覆盖GetHashCode

【讨论】:

    【解决方案3】:

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

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

    有关GetHashCode() 的实现的详细信息,尤其是这一段:

    • 如果两个对象比较相等,则每个对象的 GetHashCode 方法 对象必须返回相同的值。但是,如果两个对象不 比较相等,两个对象的 GetHashCode 方法不 必须返回不同的值。

    如果GetHashCode()SecurityUser 中被覆盖,则两个哈希码将不同,因为两个对象objAobjB 是对内存中不同对象的引用(如由new-关键字)。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 我忘记了 GetHashCode() 实际上是 object.GetHashCode()
      【解决方案5】:

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

      您可以通过覆盖SecurityUser 中的GetHashCode() 方法来覆盖此行为,以便从您的类的成员 而非类本身计算散列。确保您还覆盖了Equals(),因为这两种方法是相辅相成的。您还可以考虑覆盖 == 相等运算符。

      有关GetHashCode() 实现的一个很好的示例,请参阅这篇文章中接受的答案:What is the best algorithm for an overridden System.Object.GetHashCode?

      【讨论】:

        猜你喜欢
        • 2012-06-27
        • 2013-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-23
        • 1970-01-01
        • 2013-06-28
        • 1970-01-01
        相关资源
        最近更新 更多