【问题标题】:GetHashCode() override coliding way to oftenGetHashCode() 经常覆盖折叠方式
【发布时间】:2018-08-14 03:04:33
【问题描述】:

我正在使用 unity,而 unity 中没有元组,所以我创建了自己的元组类来工作,因为我的字典需要它。

Dictionary <Tuple<int,int>, Tile>

我创建的 Tile 类与解决这个问题并不真正相关(至少我认为它不会有帮助)。

但问题是我在元组中同时使用了负整数和正整数,当我将当前的GetHashCode()Tuples 一起使用时,有时我会得到相同的HashCode,例如Tuple&lt;-10, 8&gt; 和@当我返回哈希码时,987654325@ 都给出 -172。

有什么好的 GetHashCode 不会让我发生冲突吗? 老实说,我只使用运算符==,因为我需要检查两个元组内部是否有相同的整数,如果我能得到一个运算符==,它只会在两个整数相同并且在同样的顺序,它会解决我的问题。

其他一些小问题,我无法理解 Equals 覆盖,因为它正在工作,但我不知道它的工作情况如何,因为我改变了每一件事,直到它工作。

public class Tuple<T1, T2>
{
    public T1 First { get; private set; }
    public T2 Second { get; private set; }

    public Tuple(T1 _First, T2 _Second)
    {
        First = _First;
        Second = _Second;
    }
    public override int GetHashCode()
    {
        int hash = 0;

        hash = First.GetHashCode() * 17 + Second.GetHashCode() + First.GetHashCode();

        return hash;
    }
    public static bool operator==(Tuple<T1, T2> obj1, Tuple<T1, T2> obj2)
    {
        if (ReferenceEquals(null, obj2))
            return false;
        return (obj1.GetHashCode() == obj2.GetHashCode());
    }
    public static bool operator!=(Tuple<T1, T2> obj1, Tuple<T1, T2> obj2)
    {
        if (ReferenceEquals(null, obj2))
            return true;
        return !(obj1.GetHashCode() == obj2.GetHashCode());
    }
    public bool Equals(Tuple<T1, T2> other)
    {
        if (other == null)
            return false;

        if (GetHashCode() == other.GetHashCode())
            return true;
        else
            return false;

    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        Tuple<T1, T2> other = obj as Tuple<T1, T2>;

        return obj.GetType() == GetType() && Equals(other);
    }
}
public static class Tuple
{
    public static Tuple<T1, T2> New<T1, T2>(T1 first, T2 second)
    {
        var tuple = new Tuple<T1, T2>(first, second);
        return tuple;
    }
}

【问题讨论】:

    标签: c# unity3d overriding hashcode gethashcode


    【解决方案1】:

    GetHashCode() 不应该是无冲突的。您应该使用它来确定两个事物是否可能是相同的对象,然后您必须真正彻底检查它们是否是。

    例如,你的 == 方法应该写成这样:

    public static bool operator==(Tuple<T1, T2> obj1, Tuple<T1, T2> obj2)
    {
        if (ReferenceEquals(null, obj2))
            return false;
    
        if (obj1.GetHashCode() != obj2.GetHashCode())
        {
            return false;
        }
        return DefaultComparer<T1>.Equals(obj1.First, obj2.First) && DefaultComparer<T2>.Equals(obj1.Second, obj2.Second);
    }
    

    另外,不要忘记考虑obj1obj2 都是null 的情况。

    如果您正在实现自己的Tuple,您可能会考虑从Reference Source 存储库中窃取微软的,或者至少将其用作您自己的基础。

    【讨论】:

    • 我已经尝试过 == 操作,它给了我一个错误,因为来自 obj1 的 T1 和 obj2 的 T1 都无法与 == 操作符进行比较,我试图为 T1 创建一个覆盖==在与元组相同的类中进行操作,不走运,我是否必须在名为 T1 的不同类中进行操作? T2 也一样?
    • 使用 EqualityComparer.Default.Equals 代替 ==
    【解决方案2】:

    我用的是unity,unity里面没有元组

    如果你有 Unity 2017 及更高版本,它支持 Tuple。

    转到编辑 --> 项目设置 --> 播放器 --> 其他设置 --> 配置 em> --> 脚本运行时版本 --> .NET 4.x 等效版本

    重新加载或重新启动 Visual Studio,您应该可以使用 Tuple。如果您没有使用 Unity 2017 及更高版本并且也不想更新,请参阅 John 的 answer

    【讨论】:

    • hmm 很酷,我对 tupes 进行了快速浅层搜索,我发现的第一批文章之一是 Tuples 不在 Unity 中,所以我失去了希望,这实际上很有帮助。一件奇怪的事情,我只是更改为 System 元组,它给了我大约 250 毫秒搜索的每个图块,而使用 my 元组它给了我 58Ticks 每个搜索的图块(我去通过 Tiles 的 2D 地图中的每个 Tile 并检查其中的每个相邻的 tile 以平滑 2D 地图)系统元组比我的元组慢近 5 倍是否正常?
    • 请注意,您当前的元组不正确,因此不一定是公平的比较。
    • 看看@MineR 说了什么。您应该检查源代码以了解标准元组的实际情况。
    【解决方案3】:

    这是 resharper 自动为您生成的。只需注意他们如何执行 GetHashCode() 和 Equals。

    private class Tuple<T1,T2> : IEquatable<Tuple<T1, T2>>
    {
        public T1 First {get;}
        public T2 Second {get;}
    
        public Tuple(T1 first, T2 second)
        {
            First = first;
            Second = second;
        }
    
        public bool Equals(Tuple<T1, T2> other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return EqualityComparer<T1>.Default.Equals(First, other.First) && EqualityComparer<T2>.Default.Equals(Second, other.Second);
        }
    
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((Tuple<T1, T2>) obj);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                return (EqualityComparer<T1>.Default.GetHashCode(First) * 397) ^ EqualityComparer<T2>.Default.GetHashCode(Second);
            }
        }
    
        public static bool operator ==(Tuple<T1, T2> left, Tuple<T1, T2> right)
        {
            return Equals(left, right);
        }
    
        public static bool operator !=(Tuple<T1, T2> left, Tuple<T1, T2> right)
        {
            return !Equals(left, right);
        }
    }
    

    【讨论】:

      【解决方案4】:

      我发现PropertyCompare (*) 在自动生成Equals 实现中很有用。它会自动比较所有公共属性(因此,如果您添加一个新的公共属性,您不需要更改任何内容除了 GetHashCode(即使是技术上可选) .

      它使用 Cache 来获得合理的性能 - 它需要一次性命中(每种类型)来生成适当的比较表达式。

      using System;
      using System.Linq.Expressions;
      
      namespace YourApp
      {
          public class Tuple<T1, T2>
          {
              public T1 First { get; private set; }
              public T2 Second { get; private set; }
      
              public Tuple(T1 _First, T2 _Second)
              {
                  First = _First;
                  Second = _Second;
              }
              public override int GetHashCode()
              {
                  var hash = 0;
      
                  // Implement this however you like
                  hash = First.GetHashCode() * 17 + Second.GetHashCode() + First.GetHashCode();
      
                  return hash;
              }
              public static bool operator ==(Tuple<T1, T2> x, Tuple<T1, T2> y)
              {
                  return PropertyCompare.Equal(x, y);
              }
              public static bool operator !=(Tuple<T1, T2> x, Tuple<T1, T2> y)
              {
                  return !PropertyCompare.Equal(x, y);
              }
              public bool Equals(Tuple<T1, T2> other)
              {
                  return PropertyCompare.Equal(this, other);
      
              }
              public override bool Equals(object obj)
              {
                  return PropertyCompare.Equal(this, obj);
              }
          }
          public static class Tuple
          {
              public static Tuple<T1, T2> New<T1, T2>(T1 first, T2 second)
              {
                  var tuple = new Tuple<T1, T2>(first, second);
                  return tuple;
              }
          }
      
          public class Program
          {
              public static void Main()
              {
                  var bob1 = Tuple.New("a", 1);
                  var bob2 = Tuple.New("a", 1);
      
                  Console.WriteLine(bob1 == bob2);
                  Console.ReadLine();
              }
          }
      
          public static class PropertyCompare
          {
              public static bool Equal<T>(T x, object y) where T : class
              {
                  return Cache<T>.Compare(x, y as T);
              }
      
              public static bool Equal<T>(T x, T y)
              {
                  if (x == null)
                  {
                      return y == null;
                  }
      
                  if (y == null)
                  {
                      return false;
                  }
      
                  return Cache<T>.Compare(x, y);
              }
      
              private static class Cache<T>
              {
                  internal static readonly Func<T, T, bool> Compare;
                  static Cache()
                  {
                      var props = typeof(T).GetProperties();
                      if (props.Length == 0)
                      {
                          Compare = delegate { return true; };
                          return;
                      }
                      var x = Expression.Parameter(typeof(T), "x");
                      var y = Expression.Parameter(typeof(T), "y");
      
                      Expression body = null;
                      for (var i = 0; i < props.Length; i++)
                      {
                          var propEqual = Expression.Equal(
                              Expression.Property(x, props[i]),
                              Expression.Property(y, props[i]));
                          if (body == null)
                          {
                              body = propEqual;
                          }
                          else
                          {
                              body = Expression.AndAlso(body, propEqual);
                          }
                      }
                      Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y)
                                    .Compile();
                  }
              }
          }
      }
      

      (*) 我在网上某个地方找到了它,可惜我不记得在哪里,Google 在这里失败了。

      【讨论】:

        猜你喜欢
        • 2012-07-13
        • 1970-01-01
        • 2014-11-25
        • 2011-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-22
        • 1970-01-01
        相关资源
        最近更新 更多