【问题标题】:Why should I *not* override GetHashCode()?为什么我*不*重写 GetHashCode()?
【发布时间】:2013-08-06 15:55:27
【问题描述】:

我的search for a helper to correctly combine constituent hashcodes for GetHashCode() 似乎引起了一些敌意。我从 cmets 得到的印象是,一些 C# 开发人员认为您不应该经常覆盖 GetHashCode() - 当然,有些评论者似乎认为帮助获得正确行为的库是无用的。对于Java community to ask for it to be added to the JDK,这样的功能在Java 中被认为足够有用,它是now in JDK 7。

在 C# 中你不需要——或者绝对不应该——像在 Java 中那样频繁地覆盖 GetHashCode()(相应地,Equals())是否有一些根本原因?我发现自己经常使用 Java 执行此操作,例如,每当我创建一个我知道我想保留在 HashSet 中的类型或用作 HashMap 中的键(等效于 .net Dictionary)时。

【问题讨论】:

  • 在创建表示值的类型时应该重写 GetHashCode()。
  • @Slaks 例如int、double 和Point?
  • @HighCore:Dictionary<,> 是一种非常有用的类型……并不是说它是覆盖 Equals/GetHashCode 的唯一用途。能够创建一组不同的元素通常也很有用。正如您所暗示的那样,我认为没有任何理由建议 OP 过度使用字典。
  • 删除了我的答案,但有两个想法:在 .NET 中,您 需要 实现这一点的情况是一小部分的一小部分 - 所以“为什么不呢? "是“因为你不需要”。但它似乎也可能与可变性/不变性密切相关; .NET 对象通常是可变的,可变类型上的自定义 GetHashCode 实现可能很危险
  • @bacar:实际上,值类型对GetHashCode() 的实现非常糟糕。见msdn.microsoft.com/en-us/library/ms182276.aspx

标签: c# java .net hash


【解决方案1】:

C# 具有提供值相等性的内置值类型,而 Java 没有。 因此,用 Java 编写自己的哈希码可能是必要的,而在 C# 中这样做可能是过早的优化。

编写一个类型作为复合键在 Dictionary/HashMap 中使用是很常见的。通常在此类类型上您需要value equality (equivalence) as opposed to reference equality(identity),例如:

IDictionary<Person, IList<Movie> > moviesByActor; // e.g. initialised from DB
// elsewhere...
Person p = new Person("Chuck", "Norris");
IList<Movie> chuckNorrisMovies = moviesByActor[p];

在这里,如果我需要创建一个新的 Person 实例来进行查找,我需要 Person 来实现值相等,否则它将与 Dictionary 中的现有条目不匹配,因为它们具有不同的身份。

要获得相等的值,您需要覆盖两种语言的 Equals() 和 GetHashCode()。

C# 的结构(值类型)implement value equality 供您使用(尽管可能效率低下),并提供GetHashCode 的一致实现。这可能足以满足许多人的需求,除非性能问题另有说明,否则他们不会进一步实施自己的改进版本。

Java 没有这样的内置语言特性。如果要创建具有值相等语义的类型以用作复合键,则必须自己实现 equals() 和相应的 hashCode()。 (有第三方帮助程序和库可以帮助您执行此操作,但语言本身没有内置任何内容)。

我将 C# 值类型描述为在字典中使用“可能效率低下”,因为:

【讨论】:

    【解决方案2】:

    如果你的对象代表一个值或类型,那么你应该重写 GetHashCode() 和 Equals。我从不覆盖控制类的哈希码,比如“App”。虽然我看不出为什么在这些情况下甚至覆盖 GetHashCode() 也会成为问题,因为它们永远不会被置于干扰集合索引或比较的位置。

    例子:

    public class ePoint : eViewModel, IEquatable<ePoint>
    {
        public double X;
    
        public double Y;
    
        // Methods
    
        #region IEquatable Overrides
    
        public override bool Equals(object obj)
        {
            if (Object.ReferenceEquals(obj, null)) { return false; }
    
            if (Object.ReferenceEquals(this, obj)) { return true; }
    
            if (!(obj is ePoint)) { return false; }
    
            return Equals((ePoint)obj);
        }
    
        public bool Equals(ePoint other)
        {
            return X == other.X && Y == other.Y;
        }
    
        public override int GetHashCode()
        {
            return (int)Math.Pow(X,Y);
        }
    
        #endregion
    

    【讨论】:

    • 谢谢。这对于 Java 和 C# 一样适用。它没有解释是否有特定原因在 C# 与 Java 中较少需要这样做。
    • 如果一样,那么java在什么其他情况下需要呢?
    【解决方案3】:

    我编写了一个helper class 来实现GetHashCode()、Equals() 和CompareTo(),使用来自一组属性的值语义。

    【讨论】:

      猜你喜欢
      • 2019-02-02
      • 2022-07-27
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 2011-02-23
      相关资源
      最近更新 更多