代码之家  ›  专栏  ›  技术社区  ›  Prasad Telkikar

HashSet<T>如何在C#中工作?

  •  0
  • Prasad Telkikar  · 技术社区  · 6 年前

    GetHashCode method

    是否有一个标准的算法或最佳实践如何实现 方法

    0 回复  |  直到 7 年前
        1
  •  1664
  •   Jalal    5 年前

    我通常会使用Josh Bloch的实现 极好的 Effective Java . 它的速度很快,并且创建了一个很好的散列,不太可能引起冲突。选择两个不同的素数,例如17和23,然后执行以下操作:

    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            // Suitable nullity checks etc, of course :)
            hash = hash * 23 + field1.GetHashCode();
            hash = hash * 23 + field2.GetHashCode();
            hash = hash * 23 + field3.GetHashCode();
            return hash;
        }
    }
    

    正如在注释中所指出的,您可能会发现最好选择一个大素数来相乘。显然486187739很好。。。尽管我看到的大多数小数字的例子都倾向于使用素数,但至少有类似的算法经常使用非素数。在不完全- FNV 不过,这是最好的。我不知道这有多重要。)

    这比一般的 XOR 散列代码有两个主要原因。假设我们有一个有两个的类型 int 领域:

    XorHash(x, x) == XorHash(y, y) == 0 for all x, y
    XorHash(x, y) == XorHash(y, x) for all x, y
    

    This page 提供了很多选择。我认为在大多数情况下,以上这些都是“足够好”的,而且非常容易记住和纠正。这个 FNV alternative同样简单,但使用不同的常量和 而不是 ADD 作为一种组合操作。它看起来 某物 与下面的代码类似,但是普通的FNV算法对单个字节进行操作,因此这需要修改以对每个字节执行一次迭代,而不是对每个32位哈希值执行一次迭代。FNV也是为可变长度的数据而设计的,而我们在这里使用它的方式总是针对相同数量的字段值。对这个答案的评论表明,这里的代码实际上并不像上面的加法方法那样有效(在测试的示例案例中)。

    // Note: Not quite FNV!
    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = (int) 2166136261;
            // Suitable nullity checks etc, of course :)
            hash = (hash * 16777619) ^ field1.GetHashCode();
            hash = (hash * 16777619) ^ field2.GetHashCode();
            hash = (hash * 16777619) ^ field3.GetHashCode();
            return hash;
        }
    }
    

    根据 documentation :

    • 当可变对象包含在依赖其哈希代码的集合中时,可以确保该对象的哈希代码不会更改。

    链接到 FNV Eternally Confuzzled - The Art of Hashing

        2
  •  470
  •   Rick Love    6 年前

    ValueTuple-C#7的更新

    正如@cactualoid在注释中提到的,可以使用值元组。这节省了一些击键,更重要的是纯粹在堆栈上执行(没有垃圾):

    (PropA, PropB, PropC, PropD).GetHashCode();
    

    (注意:最初使用匿名类型的技术似乎是在堆上创建一个对象,即垃圾,因为匿名类型是作为类实现的,尽管编译器可能会对此进行优化。对这些选项进行基准测试是很有趣的,但是tuple选项应该更好。)

    匿名类型(原始答案)

    new { PropA, PropB, PropC, PropD }.GetHashCode();
    

    这将适用于任何数量的属性。它不使用拳击。它只使用已经在匿名类型框架中实现的算法。

        3
  •  106
  •   casperOne    15 年前

    这是我的hashcode助手。
    它的优点是使用泛型类型参数,因此不会导致装箱:

    public static class HashHelper
    {
        public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
        {
             unchecked
             {
                 return 31 * arg1.GetHashCode() + arg2.GetHashCode();
             }
        }
    
        public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
        {
            unchecked
            {
                int hash = arg1.GetHashCode();
                hash = 31 * hash + arg2.GetHashCode();
                return 31 * hash + arg3.GetHashCode();
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, 
            T4 arg4)
        {
            unchecked
            {
                int hash = arg1.GetHashCode();
                hash = 31 * hash + arg2.GetHashCode();
                hash = 31 * hash + arg3.GetHashCode();
                return 31 * hash + arg4.GetHashCode();
            }
        }
    
        public static int GetHashCode<T>(T[] list)
        {
            unchecked
            {
                int hash = 0;
                foreach (var item in list)
                {
                    hash = 31 * hash + item.GetHashCode();
                }
                return hash;
            }
        }
    
        public static int GetHashCode<T>(IEnumerable<T> list)
        {
            unchecked
            {
                int hash = 0;
                foreach (var item in list)
                {
                    hash = 31 * hash + item.GetHashCode();
                }
                return hash;
            }
        }
    
        /// <summary>
        /// Gets a hashcode for a collection for that the order of items 
        /// does not matter.
        /// So {1, 2, 3} and {3, 2, 1} will get same hash code.
        /// </summary>
        public static int GetHashCodeForOrderNoMatterCollection<T>(
            IEnumerable<T> list)
        {
            unchecked
            {
                int hash = 0;
                int count = 0;
                foreach (var item in list)
                {
                    hash += item.GetHashCode();
                    count++;
                }
                return 31 * hash + count.GetHashCode();
            }
        }
    
        /// <summary>
        /// Alternative way to get a hashcode is to use a fluent 
        /// interface like this:<br />
        /// return 0.CombineHashCode(field1).CombineHashCode(field2).
        ///     CombineHashCode(field3);
        /// </summary>
        public static int CombineHashCode<T>(this int hashCode, T arg)
        {
            unchecked
            {
                return 31 * hashCode + arg.GetHashCode();   
            }
        }
    

    public override int GetHashCode()
    {
        return HashHelper.GetHashCode(Manufacturer, PartN, Quantity);
    }
    

    public override int GetHashCode()
    {
        return 0.CombineHashCode(Manufacturer)
            .CombineHashCode(PartN)
            .CombineHashCode(Quantity);
    }
    
        4
  •  71
  •   Muhammad Rehan Saeed    6 年前

    .NET Standard 2.1及更高版本

    System.HashCode 结构。有两种使用方法:

    哈希码。合并

    这个 Combine

    public override int GetHashCode() => HashCode.Combine(this.object1, this.object2);
    

    哈希代码。添加

    这个 Add 方法帮助您处理集合:

    public override int GetHashCode()
    {
        var hashCode = new HashCode();
        hashCode.Add(this.object1);
        foreach (var item in this.collection)
        {
            hashCode.Add(item);
        }
        return hashCode.ToHashCode();
    }
    

    你可以阅读完整的博客文章 GetHashCode Made Easy '了解更多细节和评论。

    public class SuperHero
    {
        public int Age { get; set; }
        public string Name { get; set; }
        public List<string> Powers { get; set; }
    
        public override int GetHashCode() =>
            HashCode.Of(this.Name).And(this.Age).AndEach(this.Powers);
    }
    

    实施

    public struct HashCode : IEquatable<HashCode>
    {
        private const int EmptyCollectionPrimeNumber = 19;
        private readonly int value;
    
        private HashCode(int value) => this.value = value;
    
        public static implicit operator int(HashCode hashCode) => hashCode.value;
    
        public static bool operator ==(HashCode left, HashCode right) => left.Equals(right);
    
        public static bool operator !=(HashCode left, HashCode right) => !(left == right);
    
        public static HashCode Of<T>(T item) => new HashCode(GetHashCode(item));
    
        public static HashCode OfEach<T>(IEnumerable<T> items) =>
            items == null ? new HashCode(0) : new HashCode(GetHashCode(items, 0));
    
        public HashCode And<T>(T item) => 
            new HashCode(CombineHashCodes(this.value, GetHashCode(item)));
    
        public HashCode AndEach<T>(IEnumerable<T> items)
        {
            if (items == null)
            {
                return new HashCode(this.value);
            }
    
            return new HashCode(GetHashCode(items, this.value));
        }
    
        public bool Equals(HashCode other) => this.value.Equals(other.value);
    
        public override bool Equals(object obj)
        {
            if (obj is HashCode)
            {
                return this.Equals((HashCode)obj);
            }
    
            return false;
        }
    
        public override int GetHashCode() => this.value.GetHashCode();
    
        private static int CombineHashCodes(int h1, int h2)
        {
            unchecked
            {
                // Code copied from System.Tuple a good way to combine hashes.
                return ((h1 << 5) + h1) ^ h2;
            }
        }
    
        private static int GetHashCode<T>(T item) => item?.GetHashCode() ?? 0;
    
        private static int GetHashCode<T>(IEnumerable<T> items, int startHashCode)
        {
            var temp = startHashCode;
    
            var enumerator = items.GetEnumerator();
            if (enumerator.MoveNext())
            {
                temp = CombineHashCodes(temp, GetHashCode(enumerator.Current));
    
                while (enumerator.MoveNext())
                {
                    temp = CombineHashCodes(temp, GetHashCode(enumerator.Current));
                }
            }
            else
            {
                temp = CombineHashCodes(temp, EmptyCollectionPrimeNumber);
            }
    
            return temp;
        }
    }
    

    什么是好的算法?

    计算散列码的算法需要很快。一个简单的算法通常会更快。不分配额外内存的方法也会减少垃圾收集的需要,这反过来也会提高性能。

    确定性

    deterministic i、 给定相同的输入,它必须总是产生相同的输出。

    计算哈希代码的算法需要保留 hash collisions 一分钟。哈希冲突是两个调用 GetHashCode 在两个不同的对象上生成相同的哈希码。请注意,碰撞是允许的(有些人错误地认为它们不是),但它们应该保持在最低限度。

    阻止的DoS

    在.NETCore中,每次重新启动应用程序时,都会得到不同的哈希代码。这是防止拒绝服务攻击(DoS)的安全功能。对于.NET Framework 通过添加以下内容启用此功能应用程序配置文件:

    <?xml version ="1.0"?>  
    <configuration>  
       <runtime>  
          <UseRandomizedStringHashAlgorithm enabled="1" />  
       </runtime>  
    </configuration>
    

    由于此功能,哈希代码决不能在创建它们的应用程序域之外使用,决不能用作集合中的键字段,也决不能持久化。

    了解更多信息 here .

    加密安全?

    Cryptographic hash function

    • 找到两个具有相同哈希值的不同消息是不可行的
    • 对消息的一个小的更改应该广泛地更改散列值,使新的散列值看起来与旧的散列值不相关(雪崩效应)。
        5
  •  63
  •   Mike Chamberlain JaredPar    10 年前

    我在Helper库中有一个Hashing类,我将它用于此目的。

    /// <summary> 
    /// This is a simple hashing function from Robert Sedgwicks Hashing in C book.
    /// Also, some simple optimizations to the algorithm in order to speed up
    /// its hashing process have been added. from: www.partow.net
    /// </summary>
    /// <param name="input">array of objects, parameters combination that you need
    /// to get a unique hash code for them</param>
    /// <returns>Hash code</returns>
    public static int RSHash(params object[] input)
    {
        const int b = 378551;
        int a = 63689;
        int hash = 0;
    
        // If it overflows then just wrap around
        unchecked
        {
            for (int i = 0; i < input.Length; i++)
            {
                if (input[i] != null)
                {
                    hash = hash * a + input[i].GetHashCode();
                    a = a * b;
                }
            }
        }
    
        return hash;
    }
    

    public override int GetHashCode()
    {
        return Hashing.RSHash(_field1, _field2, _field3);
    }
    

    我没有评估它的性能,所以欢迎任何反馈。

        6
  •  59
  •   Şafak Gür    6 年前

    下面是我的助手类使用 Jon Skeet's implementation .

    public static class HashCode
    {
        public const int Start = 17;
    
        public static int Hash<T>(this int hash, T obj)
        {
            var h = EqualityComparer<T>.Default.GetHashCode(obj);
            return unchecked((hash * 31) + h);
        }
    }
    

    public override int GetHashCode()
    {
        return HashCode.Start
            .Hash(_field1)
            .Hash(_field2)
            .Hash(_field3);
    }
    

    如果要避免为编写扩展方法系统.Int32:

    public readonly struct HashCode
    {
        private readonly int _value;
    
        public HashCode(int value) => _value = value;
    
        public static HashCode Start { get; } = new HashCode(17);
    
        public static implicit operator int(HashCode hash) => hash._value;
    
        public HashCode Hash<T>(T obj)
        {
            var h = EqualityComparer<T>.Default.GetHashCode(obj);
            return unchecked(new HashCode((_value * 31) + h));
        }
    
        public override int GetHashCode() => _value;
    }
    

    它仍然避免了任何堆分配,使用方式完全相同:

    public override int GetHashCode()
    {
        // This time `HashCode.Start` is not an `Int32`, it's a `HashCode` instance.
        // And the result is implicitly converted to `Int32`.
        return HashCode.Start
            .Hash(_field1)
            .Hash(_field2)     
            .Hash(_field3);
    }
    

    编辑(2018年5月): EqualityComparer<T>.Default getter现在是JIT的一个内在特性 pull request 是斯蒂芬·图布在书中提到的 this blog post .

        7
  •  30
  •   Bert Huijben    17 年前

    没有分配 ,请)快点( 没有繁重的计算

    繁重的工作应该是Equals()方法的一部分;哈希应该是一个非常廉价的操作,以便能够对尽可能少的项调用Equals()。

    最后一点提示: 不要依赖GetHashCode()在多个应用程序运行中保持稳定 . 许多.Net类型不能保证它们的哈希代码在重新启动后保持不变,因此只能对内存中的数据结构使用GetHashCode()的值。

        8
  •  25
  •   Glenn Slayden    8 年前

    简直糟透了。因此,经过一些实验和研究,我开始用以下内容重新散列我的散列:

    public static int ReHash(int source)
    {
      unchecked
      {
        ulong c = 0xDEADBEEFDEADBEEF + (ulong)source;
        ulong d = 0xE2ADBEEFDEADBEEF ^ c;
        ulong a = d += c = c << 15 | c >> -15;
        ulong b = a += d = d << 52 | d >> -52;
        c ^= b += a = a << 26 | a >> -26;
        d ^= c += b = b << 51 | b >> -51;
        a ^= d += c = c << 28 | c >> -28;
        b ^= a += d = d << 9 | d >> -9;
        c ^= b += a = a << 47 | a >> -47;
        d ^= c += b << 54 | b >> -54;
        a ^= d += c << 32 | c >> 32;
        a += d << 25 | d >> -25;
        return (int)(a >> 1);
      }
    }
    

    然后我的两个哈希表的能力就没有了。

    不过,这让我很不安,因为上面的方法不应该奏效。或者更确切地说,它不应该工作,除非原来的 GetHashCode()

    重新混合一个hashcode并不能改进一个好的hashcode,因为唯一可能的效果是我们引入了更多的冲突。

    重新混合散列代码只能改进散列代码,至少在避免整个范围内的绝对冲突方面做得相当好(2) 可能的值),但在哈希表中实际使用时,在避免冲突方面做得很差。虽然两个表的幂的简单模使这一点更加明显,但它对更常见的素数表也有负面影响,只是没有那么明显(重新灰化的额外工作将超过好处,但好处仍然存在)。

    好吧,令人不安的是 string.GetHashCode() 中的实现 .NET (或研究) here )可以这样改进(由于更少的冲突,测试运行速度提高了20-30倍),并且更令人不安的是,我自己的哈希代码可以改进多少(远不止这些)。

    我在过去编写的所有GetHashCode()实现,实际上在这个站点上用作答案的基础,都比我想象的要糟糕得多 . 很多时候它对很多用途来说都“足够好”,但我想要更好的。

    最后我决定移植 SpookyHash 到.NET。实际上,上面的代码是使用SpookyHash从32位输入生成32位输出的快速路径版本。

    然后我把 那个 项目的一方,因为正如原来的项目产生了如何产生更好的哈希代码的问题,所以项目产生了如何产生更好的.NET内存的问题。

    decimal )变成散列码。

    它的速度很快,Bob Jenkins值得称赞,因为他最初移植的代码速度更快,尤其是在64位机器上,算法得到了优化。

    https://bitbucket.org/JonHanna/spookilysharp/src

    然而,由于它现在已经写好了,人们可以更容易地使用它:

    public override int GetHashCode()
    {
      var hash = new SpookyHash();
      hash.Update(field1);
      hash.Update(field2);
      hash.Update(field3);
      return hash.Final().GetHashCode();
    }
    

    它还接受种子值,因此如果您需要处理不受信任的输入并希望防止哈希DoS攻击,则可以根据正常运行时间或类似情况设置种子,并使攻击者无法预测结果:

    private static long hashSeed0 = Environment.TickCount;
    private static long hashSeed1 = DateTime.Now.Ticks;
    public override int GetHashCode()
    {
      //produce different hashes ever time this application is restarted
      //but remain consistent in each run, so attackers have a harder time
      //DoSing the hash tables.
      var hash = new SpookyHash(hashSeed0, hashSeed1);
      hash.Update(field1);
      hash.Update(field2);
      hash.Update(field3);
      return hash.Final().GetHashCode();
    }
    

    *一个很大的惊喜是,hand内联了一个返回的旋转方法 (x << n) | (x >> -n) 改进的东西。我本可以肯定抖动会为我内联,但分析显示不是这样。

    GetHashCode() Equals() 没有。两者都是有效的选择,但不能混为一谈。在实现自己的版本时,您需要选择一个或另一个,但我不知道您想要哪个。

    在32位上,比 string.GetHashCode() 在64位上,这比在32位上的SpookyHash快得多,尽管仍然足够快,是一个合理的选择。

        9
  •  15
  •   James Ko    8 年前

    https://github.com/dotnet/coreclr/pull/14863 ,有一种生成哈希代码的新方法,非常简单!写吧

    public override int GetHashCode()
        => HashCode.Combine(field1, field2, field3);
    

    这将生成高质量的哈希代码,而不必担心实现细节。

        10
  •  13
  •   Magnus    14 年前

    这是一个很好的例子:

    /// <summary>
    /// Helper class for generating hash codes suitable 
    /// for use in hashing algorithms and data structures like a hash table. 
    /// </summary>
    public static class HashCodeHelper
    {
        private static int GetHashCodeInternal(int key1, int key2)
        {
            unchecked
            {
               var num = 0x7e53a269;
               num = (-1521134295 * num) + key1;
               num += (num << 10);
               num ^= (num >> 6);
    
               num = ((-1521134295 * num) + key2);
               num += (num << 10);
               num ^= (num >> 6);
    
               return num;
            }
        }
    
        /// <summary>
        /// Returns a hash code for the specified objects
        /// </summary>
        /// <param name="arr">An array of objects used for generating the 
        /// hash code.</param>
        /// <returns>
        /// A hash code, suitable for use in hashing algorithms and data 
        /// structures like a hash table. 
        /// </returns>
        public static int GetHashCode(params object[] arr)
        {
            int hash = 0;
            foreach (var item in arr)
                hash = GetHashCodeInternal(hash, item.GetHashCode());
            return hash;
        }
    
        /// <summary>
        /// Returns a hash code for the specified objects
        /// </summary>
        /// <param name="obj1">The first object.</param>
        /// <param name="obj2">The second object.</param>
        /// <param name="obj3">The third object.</param>
        /// <param name="obj4">The fourth object.</param>
        /// <returns>
        /// A hash code, suitable for use in hashing algorithms and
        /// data structures like a hash table.
        /// </returns>
        public static int GetHashCode<T1, T2, T3, T4>(T1 obj1, T2 obj2, T3 obj3,
            T4 obj4)
        {
            return GetHashCode(obj1, GetHashCode(obj2, obj3, obj4));
        }
    
        /// <summary>
        /// Returns a hash code for the specified objects
        /// </summary>
        /// <param name="obj1">The first object.</param>
        /// <param name="obj2">The second object.</param>
        /// <param name="obj3">The third object.</param>
        /// <returns>
        /// A hash code, suitable for use in hashing algorithms and data 
        /// structures like a hash table. 
        /// </returns>
        public static int GetHashCode<T1, T2, T3>(T1 obj1, T2 obj2, T3 obj3)
        {
            return GetHashCode(obj1, GetHashCode(obj2, obj3));
        }
    
        /// <summary>
        /// Returns a hash code for the specified objects
        /// </summary>
        /// <param name="obj1">The first object.</param>
        /// <param name="obj2">The second object.</param>
        /// <returns>
        /// A hash code, suitable for use in hashing algorithms and data 
        /// structures like a hash table. 
        /// </returns>
        public static int GetHashCode<T1, T2>(T1 obj1, T2 obj2)
        {
            return GetHashCodeInternal(obj1.GetHashCode(), obj2.GetHashCode());
        }
    }
    

    下面是如何使用它:

    private struct Key
    {
        private Type _type;
        private string _field;
    
        public Type Type { get { return _type; } }
        public string Field { get { return _field; } }
    
        public Key(Type type, string field)
        {
            _type = type;
            _field = field;
        }
    
        public override int GetHashCode()
        {
            return HashCodeHelper.GetHashCode(_field, _type);
        }
    
        public override bool Equals(object obj)
        {
            if (!(obj is Key))
                return false;
            var tf = (Key)obj;
            return tf._field.Equals(_field) && tf._type.Equals(_type);
        }
    }
    
        11
  •  9
  •   Community Mohan Dere    9 年前

    下面是另一个流畅的 the algorithm posted above by Jon Skeet ,但不包括分配或装箱操作:

    public static class Hash
    {
        public const int Base = 17;
    
        public static int HashObject(this int hash, object obj)
        {
            unchecked { return hash * 23 + (obj == null ? 0 : obj.GetHashCode()); }
        }
    
        public static int HashValue<T>(this int hash, T value)
            where T : struct
        {
            unchecked { return hash * 23 + value.GetHashCode(); }
        }
    }
    

    public class MyType<T>
    {
        public string Name { get; set; }
    
        public string Description { get; set; }
    
        public int Value { get; set; }
    
        public IEnumerable<T> Children { get; set; }
    
        public override int GetHashCode()
        {
            return Hash.Base
                .HashObject(this.Name)
                .HashObject(this.Description)
                .HashValue(this.Value)
                .HashObject(this.Children);
        }
    }
    

    编译器将确保 HashValue HashObject

        12
  •  8
  •   bitbonk    13 年前

    这是我的简单方法。我使用的是经典的生成器模式。它是类型安全的(没有装箱/拆箱),并且与.NET2.0兼容(没有扩展方法等)。

    用法如下:

    public override int GetHashCode()
    {
        HashBuilder b = new HashBuilder();
        b.AddItems(this.member1, this.member2, this.member3);
        return b.Result;
    } 
    

    以下是acutal builder类:

    internal class HashBuilder
    {
        private const int Prime1 = 17;
        private const int Prime2 = 23;
        private int result = Prime1;
    
        public HashBuilder()
        {
        }
    
        public HashBuilder(int startHash)
        {
            this.result = startHash;
        }
    
        public int Result
        {
            get
            {
                return this.result;
            }
        }
    
        public void AddItem<T>(T item)
        {
            unchecked
            {
                this.result = this.result * Prime2 + item.GetHashCode();
            }
        }
    
        public void AddItems<T1, T2>(T1 item1, T2 item2)
        {
            this.AddItem(item1);
            this.AddItem(item2);
        }
    
        public void AddItems<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
        {
            this.AddItem(item1);
            this.AddItem(item2);
            this.AddItem(item3);
        }
    
        public void AddItems<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, 
            T4 item4)
        {
            this.AddItem(item1);
            this.AddItem(item2);
            this.AddItem(item3);
            this.AddItem(item4);
        }
    
        public void AddItems<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, 
            T4 item4, T5 item5)
        {
            this.AddItem(item1);
            this.AddItem(item2);
            this.AddItem(item3);
            this.AddItem(item4);
            this.AddItem(item5);
        }        
    
        public void AddItems<T>(params T[] items)
        {
            foreach (T item in items)
            {
                this.AddItem(item);
            }
        }
    }
    
        13
  •  6
  •   Timo    8 年前

    ValueTuple 是一个结构,似乎有一个实体 GetHashCode

    这意味着我们可以这么做:

    // Yay, no allocations and no custom implementations!
    public override int GetHashCode() => (this.PropA, this.PropB).GetHashCode();
    

    值元组 的 .

    这是从 ValueTuple :

        internal static int CombineHashCodes(int h1, int h2)
        {
            return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2);
        }
    
        internal static int CombineHashCodes(int h1, int h2, int h3)
        {
            return HashHelpers.Combine(CombineHashCodes(h1, h2), h3);
        }
    

    HashHelper

        public static readonly int RandomSeed = Guid.NewGuid().GetHashCode();
    
        public static int Combine(int h1, int h2)
        {
            unchecked
            {
                // RyuJIT optimizes this to use the ROL instruction
                // Related GitHub pull request: dotnet/coreclr#1830
                uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
                return ((int)rol5 + h1) ^ h2;
            }
        }
    

    英语:

    • 向左旋转(循环移位)h1 5个位置。
    • 将结果和h1相加。
    • 对于每个进一步的项目,对上一个结果和下一个项目(例如h2)执行操作。

    如果能更多地了解ROL-5散列码算法的特性就更好了。

    值元组 方法 This comment 在相关的讨论中说明了直接调用 HashHelpers.Combine 更有效。另一方面,这是内部的,所以我们必须复制代码,牺牲我们在这里获得的大部分。同时,我们也要负责先记住 Combine 随机种子。我不知道如果我们跳过那一步会有什么后果。

        14
  •  5
  •   Charles Burns    8 年前

    ReSharper 用户可以使用 ReSharper -> Edit -> Generate Code -> Equality Members

    // ReSharper's GetHashCode looks like this
    public override int GetHashCode() {
        unchecked {
            int hashCode = Id;
            hashCode = (hashCode * 397) ^ IntMember;
            hashCode = (hashCode * 397) ^ OtherIntMember;
            hashCode = (hashCode * 397) ^ (RefMember != null ? RefMember.GetHashCode() : 0);
            // ...
            return hashCode;
        }
    }
    
        15
  •  3
  •   Mark G    17 年前

    我的大部分工作都是通过数据库连接完成的,这意味着我的类都有一个来自数据库的唯一标识符。我总是使用数据库中的ID来生成hashcode。

    // Unique ID from database
    private int _id;
    
    ...    
    {
      return _id.GetHashCode();
    }
    
        16
  •  3
  •   Dbl    11 年前

    与nightcoder的解决方案非常相似,只是如果你想提高素数会更容易。

    /// <summary>
    /// Try not to look at the source code. It works. Just rely on it.
    /// </summary>
    public static class HashHelper
    {
        private const int PrimeOne = 17;
        private const int PrimeTwo = 23;
    
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
                hash = hash * PrimeTwo + arg5.GetHashCode();
                hash = hash * PrimeTwo + arg6.GetHashCode();
                hash = hash * PrimeTwo + arg7.GetHashCode();
                hash = hash * PrimeTwo + arg8.GetHashCode();
                hash = hash * PrimeTwo + arg9.GetHashCode();
                hash = hash * PrimeTwo + arg10.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
                hash = hash * PrimeTwo + arg5.GetHashCode();
                hash = hash * PrimeTwo + arg6.GetHashCode();
                hash = hash * PrimeTwo + arg7.GetHashCode();
                hash = hash * PrimeTwo + arg8.GetHashCode();
                hash = hash * PrimeTwo + arg9.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
                hash = hash * PrimeTwo + arg5.GetHashCode();
                hash = hash * PrimeTwo + arg6.GetHashCode();
                hash = hash * PrimeTwo + arg7.GetHashCode();
                hash = hash * PrimeTwo + arg8.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
                hash = hash * PrimeTwo + arg5.GetHashCode();
                hash = hash * PrimeTwo + arg6.GetHashCode();
                hash = hash * PrimeTwo + arg7.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
                hash = hash * PrimeTwo + arg5.GetHashCode();
                hash = hash * PrimeTwo + arg6.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
                hash = hash * PrimeTwo + arg5.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
                hash = hash * PrimeTwo + arg4.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
                hash = hash * PrimeTwo + arg3.GetHashCode();
    
                return hash;
            }
        }
    
        public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
        {
            unchecked
            {
                int hash = PrimeOne;
                hash = hash * PrimeTwo + arg1.GetHashCode();
                hash = hash * PrimeTwo + arg2.GetHashCode();
    
                return hash;
            }
        }
    }
    
        17
  •  2
  •   Sebastian Hofmann Hassan Faghihi    8 年前

    //for classes that contain a single int value
    return this.value;
    
    //for classes that contain multiple int value
    return x ^ y;
    
    //for classes that contain single number bigger than int    
    return ((int)value ^ (int)(value >> 32)); 
    
    //for classes that contain class instance fields which inherit from object
    return obj1.GetHashCode();
    
    //for classes that contain multiple class instance fields which inherit from object
    return obj1.GetHashCode() ^ obj2.GetHashCode() ^ obj3.GetHashCode(); 
    

    int a=((int)value1 ^ (int)(value1 >> 32));
    int b=((int)value2 ^ (int)(value2 >> 32));
    int c=((int)value3 ^ (int)(value3 >> 32));
    return a ^ b ^ c;
    

    对于多类型也一样:都首先转换为 int 使用 GetHashCode()

    您可以将多个值转换为哈希值,其中一些值是相同的,因此不要将其用作标识符。(也许有一天我会用到你的组件)

        18
  •  1
  •   HokieMike    12 年前

    此测试失败(浮动;哈希相同,即使我将2个值切换为负值):

            var obj1 = new { A = 100m, B = 100m, C = 100m, D = 100m};
            var obj2 = new { A = 100m, B = 100m, C = -100m, D = -100m};
            var hash1 = ComputeHash(obj1.A, obj1.B, obj1.C, obj1.D);
            var hash2 = ComputeHash(obj2.A, obj2.B, obj2.C, obj2.D);
            Assert.IsFalse(hash1 == hash2, string.Format("Hashcode values should be different   hash1:{0}  hash2:{1}",hash1,hash2));
    

    但是这个测试通过了(ints):

            var obj1 = new { A = 100m, B = 100m, C = 100, D = 100};
            var obj2 = new { A = 100m, B = 100m, C = -100, D = -100};
            var hash1 = ComputeHash(obj1.A, obj1.B, obj1.C, obj1.D);
            var hash2 = ComputeHash(obj2.A, obj2.B, obj2.C, obj2.D);
            Assert.IsFalse(hash1 == hash2, string.Format("Hashcode values should be different   hash1:{0}  hash2:{1}",hash1,hash2));
    

    我改变了我的实现,不使用GetHashCode来处理基元类型,它似乎工作得更好

        private static int InternalComputeHash(params object[] obj)
        {
            unchecked
            {
                var result = (int)SEED_VALUE_PRIME;
                for (uint i = 0; i < obj.Length; i++)
                {
                    var currval = result;
                    var nextval = DetermineNextValue(obj[i]);
                    result = (result * MULTIPLIER_VALUE_PRIME) + nextval;
    
                }
                return result;
            }
        }
    
    
    
        private static int DetermineNextValue(object value)
        {
            unchecked
            {
    
                    int hashCode;
                    if (value is short
                        || value is int
                        || value is byte
                        || value is sbyte
                        || value is uint
                        || value is ushort
                        || value is ulong
                        || value is long
                        || value is float
                        || value is double
                        || value is decimal)
                    {
                        return Convert.ToInt32(value);
                    }
                    else
                    {
                        return value != null ? value.GetHashCode() : 0;
                    }
            }
        }
    
        19
  •  1
  •   Steven Coco    7 年前

    这是一个实现Josh Bloch实现的静态助手类;它提供显式重载来“防止”装箱,还专门为长原语实现哈希。

    因为散列输出总是一个int,所以可以直接链接散列调用。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    
    namespace Sc.Util.System
    {
        /// <summary>
        /// Static methods that allow easy implementation of hashCode. Example usage:
        /// <code>
        /// public override int GetHashCode()
        ///     => HashCodeHelper.Seed
        ///         .Hash(primitiveField)
        ///         .Hsh(objectField)
        ///         .Hash(iEnumerableField);
        /// </code>
        /// </summary>
        public static class HashCodeHelper
        {
            /// <summary>
            /// An initial value for a hashCode, to which is added contributions from fields.
            /// Using a non-zero value decreases collisions of hashCode values.
            /// </summary>
            public const int Seed = 23;
    
            private const int oddPrimeNumber = 37;
    
    
            /// <summary>
            /// Rotates the seed against a prime number.
            /// </summary>
            /// <param name="aSeed">The hash's first term.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            private static int rotateFirstTerm(int aSeed)
            {
                unchecked {
                    return HashCodeHelper.oddPrimeNumber * aSeed;
                }
            }
    
    
            /// <summary>
            /// Contributes a boolean to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aBoolean">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, bool aBoolean)
            {
                unchecked {
                    return HashCodeHelper.rotateFirstTerm(aSeed)
                            + (aBoolean
                                    ? 1
                                    : 0);
                }
            }
    
            /// <summary>
            /// Contributes a char to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aChar">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, char aChar)
            {
                unchecked {
                    return HashCodeHelper.rotateFirstTerm(aSeed)
                            + aChar;
                }
            }
    
            /// <summary>
            /// Contributes an int to the developing HashCode seed.
            /// Note that byte and short are handled by this method, through implicit conversion.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aInt">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, int aInt)
            {
                unchecked {
                    return HashCodeHelper.rotateFirstTerm(aSeed)
                            + aInt;
                }
            }
    
            /// <summary>
            /// Contributes a long to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aLong">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, long aLong)
            {
                unchecked {
                    return HashCodeHelper.rotateFirstTerm(aSeed)
                            + (int)(aLong ^ (aLong >> 32));
                }
            }
    
            /// <summary>
            /// Contributes a float to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aFloat">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, float aFloat)
            {
                unchecked {
                    return HashCodeHelper.rotateFirstTerm(aSeed)
                            + Convert.ToInt32(aFloat);
                }
            }
    
            /// <summary>
            /// Contributes a double to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aDouble">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, double aDouble)
                => aSeed.Hash(Convert.ToInt64(aDouble));
    
            /// <summary>
            /// Contributes a string to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aString">The value to contribute.</param>
            /// <param name="stringComparison">Optional comparison that creates the hash.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(
                    this int aSeed,
                    string aString,
                    StringComparison stringComparison = StringComparison.Ordinal)
            {
                if (aString == null)
                    return aSeed.Hash(0);
                switch (stringComparison) {
                    case StringComparison.CurrentCulture :
                        return StringComparer.CurrentCulture.GetHashCode(aString);
                    case StringComparison.CurrentCultureIgnoreCase :
                        return StringComparer.CurrentCultureIgnoreCase.GetHashCode(aString);
                    case StringComparison.InvariantCulture :
                        return StringComparer.InvariantCulture.GetHashCode(aString);
                    case StringComparison.InvariantCultureIgnoreCase :
                        return StringComparer.InvariantCultureIgnoreCase.GetHashCode(aString);
                    case StringComparison.OrdinalIgnoreCase :
                        return StringComparer.OrdinalIgnoreCase.GetHashCode(aString);
                    default :
                        return StringComparer.Ordinal.GetHashCode(aString);
                }
            }
    
            /// <summary>
            /// Contributes a possibly-null array to the developing HashCode seed.
            /// Each element may be a primitive, a reference, or a possibly-null array.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aArray">CAN be null.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, IEnumerable aArray)
            {
                if (aArray == null)
                    return aSeed.Hash(0);
                int countPlusOne = 1; // So it differs from null
                foreach (object item in aArray) {
                    ++countPlusOne;
                    if (item is IEnumerable arrayItem) {
                        if (!object.ReferenceEquals(aArray, arrayItem))
                            aSeed = aSeed.Hash(arrayItem); // recursive call!
                    } else
                        aSeed = aSeed.Hash(item);
                }
                return aSeed.Hash(countPlusOne);
            }
    
            /// <summary>
            /// Contributes a possibly-null array to the developing HashCode seed.
            /// You must provide the hash function for each element.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aArray">CAN be null.</param>
            /// <param name="hashElement">Required: yields the hash for each element
            /// in <paramref name="aArray"/>.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash<T>(this int aSeed, IEnumerable<T> aArray, Func<T, int> hashElement)
            {
                if (aArray == null)
                    return aSeed.Hash(0);
                int countPlusOne = 1; // So it differs from null
                foreach (T item in aArray) {
                    ++countPlusOne;
                    aSeed = aSeed.Hash(hashElement(item));
                }
                return aSeed.Hash(countPlusOne);
            }
    
            /// <summary>
            /// Contributes a possibly-null object to the developing HashCode seed.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="aObject">CAN be null.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int Hash(this int aSeed, object aObject)
            {
                switch (aObject) {
                    case null :
                        return aSeed.Hash(0);
                    case bool b :
                        return aSeed.Hash(b);
                    case char c :
                        return aSeed.Hash(c);
                    case int i :
                        return aSeed.Hash(i);
                    case long l :
                        return aSeed.Hash(l);
                    case float f :
                        return aSeed.Hash(f);
                    case double d :
                        return aSeed.Hash(d);
                    case string s :
                        return aSeed.Hash(s);
                    case IEnumerable iEnumerable :
                        return aSeed.Hash(iEnumerable);
                }
                return aSeed.Hash(aObject.GetHashCode());
            }
    
    
            /// <summary>
            /// This utility method uses reflection to iterate all specified properties that are readable
            /// on the given object, excluding any property names given in the params arguments, and
            /// generates a hashcode.
            /// </summary>
            /// <param name="aSeed">The developing hash code, or the seed: if you have no seed, use
            /// the <see cref="Seed"/>.</param>
            /// <param name="aObject">CAN be null.</param>
            /// <param name="propertySelector"><see cref="BindingFlags"/> to select the properties to hash.</param>
            /// <param name="ignorePropertyNames">Optional.</param>
            /// <returns>A hash from the properties contributed to <c>aSeed</c>.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int HashAllProperties(
                    this int aSeed,
                    object aObject,
                    BindingFlags propertySelector
                            = BindingFlags.Instance
                            | BindingFlags.Public
                            | BindingFlags.GetProperty,
                    params string[] ignorePropertyNames)
            {
                if (aObject == null)
                    return aSeed.Hash(0);
                if ((ignorePropertyNames != null)
                        && (ignorePropertyNames.Length != 0)) {
                    foreach (PropertyInfo propertyInfo in aObject.GetType()
                            .GetProperties(propertySelector)) {
                        if (!propertyInfo.CanRead
                                || (Array.IndexOf(ignorePropertyNames, propertyInfo.Name) >= 0))
                            continue;
                        aSeed = aSeed.Hash(propertyInfo.GetValue(aObject));
                    }
                } else {
                    foreach (PropertyInfo propertyInfo in aObject.GetType()
                            .GetProperties(propertySelector)) {
                        if (propertyInfo.CanRead)
                            aSeed = aSeed.Hash(propertyInfo.GetValue(aObject));
                    }
                }
                return aSeed;
            }
    
    
            /// <summary>
            /// NOTICE: this method is provided to contribute a <see cref="KeyValuePair{TKey,TValue}"/> to
            /// the developing HashCode seed; by hashing the key and the value independently. HOWEVER,
            /// this method has a different name since it will not be automatically invoked by
            /// <see cref="Hash(int,object)"/>, <see cref="Hash(int,IEnumerable)"/>,
            /// or <see cref="HashAllProperties"/> --- you MUST NOT mix this method with those unless
            /// you are sure that no KeyValuePair instances will be passed to those methods; or otherwise
            /// the generated hash code will not be consistent. This method itself ALSO will not invoke
            /// this method on the Key or Value here if that itself is a KeyValuePair.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="keyValuePair">The value to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int HashKeyAndValue<TKey, TValue>(this int aSeed, KeyValuePair<TKey, TValue> keyValuePair)
                => aSeed.Hash(keyValuePair.Key)
                        .Hash(keyValuePair.Value);
    
            /// <summary>
            /// NOTICE: this method is provided to contribute a collection of <see cref="KeyValuePair{TKey,TValue}"/>
            /// to the developing HashCode seed; by hashing the key and the value independently. HOWEVER,
            /// this method has a different name since it will not be automatically invoked by
            /// <see cref="Hash(int,object)"/>, <see cref="Hash(int,IEnumerable)"/>,
            /// or <see cref="HashAllProperties"/> --- you MUST NOT mix this method with those unless
            /// you are sure that no KeyValuePair instances will be passed to those methods; or otherwise
            /// the generated hash code will not be consistent. This method itself ALSO will not invoke
            /// this method on a Key or Value here if that itself is a KeyValuePair or an Enumerable of
            /// KeyValuePair.
            /// </summary>
            /// <param name="aSeed">The developing HashCode value or seed.</param>
            /// <param name="keyValuePairs">The values to contribute.</param>
            /// <returns>The new hash code.</returns>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static int HashKeysAndValues<TKey, TValue>(
                    this int aSeed,
                    IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs)
            {
                if (keyValuePairs == null)
                    return aSeed.Hash(null);
                foreach (KeyValuePair<TKey, TValue> keyValuePair in keyValuePairs) {
                    aSeed = aSeed.HashKeyAndValue(keyValuePair);
                }
                return aSeed;
            }
        }
    }
    
        20
  •  0
  •   Ivan Sanz Carasa    6 年前

    如果你想填充 HashCode 从 netstandard2.1

    public static class HashCode
    {
        public static int Combine(params object[] instances)
        {
            int hash = 17;
    
            foreach (var i in instances)
            {
                hash = unchecked((hash * 31) + (i?.GetHashCode() ?? 0));
            }
    
            return hash;
        }
    }
    

    注意:如果与 struct

        21
  •  0
  •   ivan.ukr    5 年前

    可以尝试采用C++ Boost库的方法。像这样:

    class HashUtil
    {
      public static int HashCombine(int seed, int other)
      {
        unchecked
        {
          return other + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        }
      }
    }
    

    然后:

    class MyClass
    {
      private string _field1;
      private int _field2;
      private AnotherClass _field3;
      private YetAnotherClass _field4;
    
      public override int GetHashCode()
      {
        int result = HashUtil.HashCombine(_field1.GetHashCode(), _field2);
        result = HashUtil.HashCombine(result, _field3.GetHashCode());
        return HashUtil.HashCombine(result, _field4.GetHashCode());
      }
    }
    
        22
  •  0
  •   t0b4cc0    5 年前

    我想把我的最新发现添加到我经常回到的这个话题上。

    我当前的visualstudio/项目设置提供了自动将元组重构为结构的功能。这将生成一个GetHashCode函数,如下所示:

            public override int GetHashCode()
            {
                int hashCode = -2088324004;
                hashCode = hashCode * -1521134295 + AuftragGesperrt.GetHashCode();
                hashCode = hashCode * -1521134295 + Auftrag_gesperrt_von.GetHashCode();
                hashCode = hashCode * -1521134295 + Auftrag_gesperrt_am.GetHashCode();
                return hashCode;
            }