重写 GetHashCode 的最佳算法是什么?
在 .NET 中,GetHashCode
方法< /a> 在 .NET 基类库的很多地方都使用。 正确实现它对于在集合中快速查找项目或确定相等性时尤其重要。
是否有关于如何为我的自定义类实现 GetHashCode
的标准算法或最佳实践,这样我就不会降低性能?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(22)
微软领导了几种散列方法...
我可以猜测,对于多个大整数,您可以使用这个:
对于多类型也是如此:首先使用
GetHashCode()< 全部转换为
int
/代码>那么 int 值将被异或,结果就是你的哈希值。
对于那些使用hash作为ID(我的意思是唯一值)的人来说,hash自然就限制在位数上,我认为对于hash算法来说是5个字节,至少是MD5。
您可以将多个值转换为哈希值,其中一些值是相同的,因此不要将其用作标识符。 (也许有一天我会使用你的组件)
Microsoft lead for several way of hashing...
I can guess that for multiple big int you can use this:
And same for multi-type: all converted first to
int
usingGetHashCode()
then the int values will be xor'ed and the result is your hash.
For those who use hash as ID (I mean an unique value), hash is naturally limited to a number of digits, I think it was 5 bytes for hashing algorithm, at least MD5.
You may turn multiple values to a hashed value and some of them be same, so don't use it as an identifier. (maybe some day I am going to use your component)
这是一个静态帮助器类,实现了 Josh Bloch 的实现; 并提供显式重载以“防止”装箱,并专门针对长原语实现哈希。
您可以传递与您的 equals 实现相匹配的字符串比较。
因为 Hash 输出始终是 int,所以您可以直接链接 Hash 调用。
This is a static helper class that implements Josh Bloch's implementation; and provides explicit overloads to "prevent" boxing, and also to implement the hash specifically for the long primitives.
You can pass a string comparison that matches your equals implementation.
Because the Hash output is always an int, you can just chain Hash calls.
我使用上面选择的答案的实现遇到了浮点数和小数的问题。
该测试失败(浮点数;即使我将 2 个值切换为负值,哈希值也是相同的):
但是该测试通过(使用整数):
我更改了我的实现,不对原始类型使用 GetHashCode,它似乎工作得更好
I ran into an issue with floats and decimals using the implementation selected as the answer above.
This test fails (floats; hash is the same even though I switched 2 values to be negative):
But this test passes (with ints):
I changed my implementation to not use GetHashCode for the primitive types and it seems to work better
如果您想从
netstandard2.1
填充HashCode
注意:如果与
struct
一起使用,它将因装箱而分配内存In case you want to polyfill
HashCode
fromnetstandard2.1
Note: If used with
struct
, it will allocate memory due to boxing可以尝试采用 C++ Boost 库中的方法。 像这样:
然后:
Can try to adopt approach from C++ Boost libraries. Something like this:
and then:
我想将我的最新发现添加到我经常回来的这个帖子中。
我当前的视觉工作室/项目设置提供了自动将元组重构为结构的功能。 这将生成一个 GetHashCode 函数,如下所示:
编辑:为了澄清 AuftragGesperrt、Auftrag_gesperrt_von 和 Auftrag_gesperrt_am 是属性。 如果微软开发人员使用此功能,它可能是一个不错的解决方案。
I want to add my newest findings to this thread I came back to so often.
My current visual studio / project setup provides the functionallity to automatically refactors tuples to structs. This will generate a GetHashCode function like so:
EDIT: to clarify AuftragGesperrt, Auftrag_gesperrt_von and Auftrag_gesperrt_am are properties. If the microsoft devs use this function its probably not too bad of a solution.
我通常会选择 Josh Bloch 的 fabulous 中给出的实现方式 Effective Java。 它速度很快,并且创建了一个相当好的哈希值,不太可能导致冲突。 选择两个不同的素数,例如 17 和 23,然后执行以下操作:
正如注释中所述,您可能会发现最好选择一个大素数来相乘。 显然 486187739 很好......虽然我见过的大多数小数字示例都倾向于使用素数,但至少有类似的算法经常使用非素数。 在稍后的 not-quite-FNV 示例中,例如,我使用了显然运行良好的数字 - 但初始值不是素数。 (乘法常数是素数。我不太清楚它有多重要。)
这比
XOR
哈希码的常见做法更好,主要有两个原因。 假设我们有一个带有两个int
字段的类型:顺便说一下,早期的算法是 C# 编译器当前用于匿名类型的算法。
此页面提供了相当多的选项。 我认为对于大多数情况,上述内容“足够好”,并且非常容易记住和正确执行。 FNV 替代方案同样简单,但使用不同的常量和
XOR< /code> 而不是
ADD
作为组合操作。 它看起来与下面的代码类似,但普通的 FNV 算法对单个字节进行操作,因此这需要修改为每个字节执行一次迭代,而不是每个 32 位哈希值。 FNV 还设计用于可变长度的数据,而我们在这里使用它的方式始终针对相同数量的字段值。 对此答案的评论表明,这里的代码实际上并不像上面的加法方法那样有效(在测试的示例案例中)。请注意,需要注意的一件事是,理想情况下,您应该防止将相等敏感(因此对哈希码敏感)状态添加到依赖于哈希码的集合后发生更改。
根据文档:
FNV 文章的链接已损坏,但以下是互联网档案馆中的副本:< a href="https://archive.vn/KJeJy" rel="noreferrer">永远困惑 - 哈希的艺术
I usually go with something like the implementation given in Josh Bloch's fabulous Effective Java. It's fast and creates a pretty good hash which is unlikely to cause collisions. Pick two different prime numbers, e.g. 17 and 23, and do:
As noted in comments, you may find it's better to pick a large prime to multiply by instead. Apparently 486187739 is good... and although most examples I've seen with small numbers tend to use primes, there are at least similar algorithms where non-prime numbers are often used. In the not-quite-FNV example later, for example, I've used numbers which apparently work well - but the initial value isn't a prime. (The multiplication constant is prime though. I don't know quite how important that is.)
This is better than the common practice of
XOR
ing hashcodes for two main reasons. Suppose we have a type with twoint
fields:By the way, the earlier algorithm is the one currently used by the C# compiler for anonymous types.
This page gives quite a few options. I think for most cases the above is "good enough" and it's incredibly easy to remember and get right. The FNV alternative is similarly simple, but uses different constants and
XOR
instead ofADD
as a combining operation. It looks something like the code below, but the normal FNV algorithm operates on individual bytes, so this would require modifying to perform one iteration per byte, instead of per 32-bit hash value. FNV is also designed for variable lengths of data, whereas the way we're using it here is always for the same number of field values. Comments on this answer suggest that the code here doesn't actually work as well (in the sample case tested) as the addition approach above.Note that one thing to be aware of is that ideally you should prevent your equality-sensitive (and thus hashcode-sensitive) state from changing after adding it to a collection that depends on the hash code.
As per the documentation:
The link to the FNV article is broken but here is a copy in the Internet Archive: Eternally Confuzzled - The Art of Hashing
ValueTuple - C# 7 的更新
正如 @cactuaroid 在评论中提到的,可以使用值元组。 这节省了一些击键,更重要的是纯粹在堆栈上执行(没有垃圾):(
注意:使用匿名类型的原始技术似乎在堆上创建一个对象,即垃圾,因为匿名类型被实现为类,尽管这可能由编译器优化。对这些选项进行基准测试会很有趣,但元组选项应该更好。)
匿名类型(原始答案)
微软已经提供了一个很好的通用 HashCode 生成器:只需将属性/字段值复制到匿名类型即可 。输入并散列它:
这适用于任意数量的属性。 它不使用拳击。 它只是使用框架中已经为匿名类型实现的算法。
ValueTuple - Update for C# 7
As @cactuaroid mentions in the comments, a value tuple can be used. This saves a few keystrokes and more importantly executes purely on the stack (no Garbage):
(Note: The original technique using anonymous types seems to create an object on the heap, i.e. garbage, since anonymous types are implemented as classes, though this might be optimized out by the compiler. It would be interesting to benchmark these options, but the tuple option should be superior.)
Anonymous Type (Original Answer)
Microsoft already provides a good generic HashCode generator: Just copy your property/field values to an anonymous type and hash it:
This will work for any number of properties. It does not use boxing. It just uses the algorithm already implemented in the framework for anonymous types.
使用 System.HashCode
如果您使用的是 .NET Standard 2.1 或更高版本,则可以使用 System.HashCode 结构。 在早期框架上,可以从
Microsoft.Bcl.HashCode
获取 包。 有两种使用方法:HashCode.Combine
Combine
方法可用于创建哈希码,最多给出 8 个对象。HashCode.Add
Add
方法可帮助您处理集合:GetHashCode 变得简单
System.HashCode
的替代方案,非常易于使用,同时速度仍然很快。 您可以阅读完整的博客文章“GetHashCode Made Easy”了解更多详细信息和评论。用例
实现
怎样才是好的算法?
性能
计算哈希码的算法需要很快。 简单的算法通常会更快。 不分配额外内存的系统也会减少垃圾收集的需要,从而提高性能。
特别是在 C# 哈希函数中,您经常使用
unchecked
关键字来停止溢出检查以提高性能。确定性
哈希算法必须是确定性,即给定相同的输入,它必须始终产生相同的输出。
减少冲突
计算哈希码的算法需要将哈希冲突保持在最小值。 哈希冲突是指对两个不同对象的两次调用
GetHashCode
产生相同哈希码时发生的情况。 请注意,碰撞是允许的(有些人误认为不允许碰撞),但应将碰撞保持在最低限度。许多哈希函数包含幻数,例如
17
或23
。 这些是特殊的质数,与使用非质数相比,它们的数学特性有助于减少哈希冲突-质数。哈希均匀性
一个好的哈希函数应该在其输出范围内尽可能均匀地映射预期输入,即它应该基于均匀分布的输入输出广泛的哈希值。 它应该具有哈希一致性。
防止 DoS
在 .NET Core 中,每次重新启动应用程序时,您都会获得不同的哈希代码。 这是一项防止拒绝服务攻击 (DoS) 的安全功能。 对于 .NET Framework,您应该通过添加以下 App.config 文件来启用此功能:
由于此功能,哈希代码永远不应该在创建它们的应用程序域之外使用,它们永远不应该用作集合中的关键字段,并且永远不应该保留它们。
阅读有关此内容的更多信息
加密安全吗?
该算法不必是加密哈希函数。 这意味着它不必满足以下条件:
Using
System.HashCode
If you are using .NET Standard 2.1 or above, you can use the System.HashCode struct. On earlier frameworks it is available from the
Microsoft.Bcl.HashCode
package. There are two methods of using it:HashCode.Combine
The
Combine
method can be used to create a hash code, given up to eight objects.HashCode.Add
The
Add
method helps you to deal with collections:GetHashCode Made Easy
An alternative to
System.HashCode
that is super easy to use while still being fast. You can read the full blog post 'GetHashCode Made Easy' for more details and comments.Usage Example
Implementation
What Makes a Good Algorithm?
Performance
The algorithm that calculates a hash code needs to be fast. A simple algorithm is usually going to be a faster one. One that does not allocate extra memory will also reduce need for garbage collection, which will in turn also improve performance.
In C# hash functions specifically, you often use the
unchecked
keyword which stops overflow checking to improve performance.Deterministic
The hashing algorithm needs to be deterministic i.e. given the same input it must always produce the same output.
Reduce Collisions
The algorithm that calculates a hash code needs to keep hash collisions to a minumum. A hash collision is a situation that occurs when two calls to
GetHashCode
on two different objects produce identical hash codes. Note that collisions are allowed (some have the misconceptions that they are not) but they should be kept to a minimum.A lot of hash functions contain magic numbers like
17
or23
. These are special prime numbers which due to their mathematical properties help to reduce hash collisions as compared to using non-prime numbers.Hash Uniformity
A good hash function should map the expected inputs as evenly as possible over its output range i.e. it should output a wide range of hashes based on its inputs that are evenly spread. It should have hash uniformity.
Prevent's DoS
In .NET Core each time you restart an application you will get different hash codes. This is a security feature to prevent Denial of Service attacks (DoS). For .NET Framework you should enable this feature by adding the following App.config file:
Because of this feature, hash codes should never be used outside of the application domain in which they were created, they should never be used as key fields in a collection and they should never be persisted.
Read more about this here.
Cryptographically Secure?
The algorithm does not have to be a Cryptographic hash function. Meaning it does not have to satisfy the following conditions:
这是我的哈希码助手。
它的优点是它使用泛型类型参数,因此不会引起装箱:
它还有扩展方法来提供流畅的接口,所以你可以像这样使用它:
或像这样:
Here is my hashcode helper.
It's advantage is that it uses generic type arguments and therefore will not cause boxing:
Also it has extension method to provide a fluent interface, so you can use it like this:
or like this:
我在 Helper 库中有一个哈希类,我将其用于此目的。
然后,您可以将其用作:
我没有评估其性能,因此欢迎任何反馈。
I have a Hashing class in Helper library that I use it for this purpose.
Then, simply you can use it as:
I didn't assess its performance, so any feedback is welcomed.
这是我的帮助器类,使用 Jon Skeet 的实现。
用法:
如果您想避免为 System.Int32 编写扩展方法:
它仍然避免任何堆分配,并且使用完全相同的方式:
编辑(2018 年 5 月):
EqualityComparer.Default
getter现在是 JIT 内在函数 - Stephen Toub 在 拉取请求 ="https://blogs.msdn.microsoft.com/dotnet/2018/04/18/performance-improvements-in-net-core-2-1" rel="nofollow noreferrer">此博文 。Here's my helper class using Jon Skeet's implementation.
Usage:
If you want to avoid writing an extension method for System.Int32:
It still avoids any heap allocation and is used exactly the same way:
Edit (May 2018):
EqualityComparer<T>.Default
getter is now a JIT intrinsic - the pull request is mentioned by Stephen Toub in this blog post.在大多数情况下,Equals() 比较多个字段,GetHash() 是在一个字段上还是在多个字段上进行散列并不重要。 您只需要确保计算哈希值确实便宜(没有分配,请)和快速(没有繁重的计算,当然没有数据库连接),并提供良好的分布。
繁重的工作应该是 Equals() 方法的一部分; 哈希应该是一个非常便宜的操作,以便能够在尽可能少的项目上调用 Equals()。
最后一点提示:不要依赖 GetHashCode() 在多次应用程序运行中保持稳定。 许多 .Net 类型不保证其哈希码在重新启动后保持不变,因此您应该仅将 GetHashCode() 的值用于内存数据结构。
In most cases where Equals() compares multiple fields it doesn't really matter if your GetHash() hashes on one field or on many. You just have to make sure that calculating the hash is really cheap (No allocations, please) and fast (No heavy computations and certainly no database connections) and provides a good distribution.
The heavy lifting should be part of the Equals() method; the hash should be a very cheap operation to enable calling Equals() on as few items as possible.
And one final tip: Don't rely on GetHashCode() being stable over multiple aplication runs. Many .Net types don't guarantee their hash codes to stay the same after a restart, so you should only use the value of GetHashCode() for in memory data structures.
直到最近,我的答案仍然与乔恩·斯基特的答案非常接近。 然而,我最近开始了一个使用二次方哈希表的项目,即内部表的大小为 8、16、32 等的哈希表。有一个很好的理由支持素数大小,但是有二次方尺寸也有一些优点。
它非常糟糕。 因此,经过一些实验和研究后,我开始使用以下内容重新散列我的散列:
然后我的二次方散列表不再糟糕了。
但这让我感到不安,因为上面的内容不应该起作用。 或者更准确地说,除非原始的 GetHashCode() 在某个非常特殊的方面很糟糕,否则它不应该起作用。
重新混合哈希码并不能改善一个好的哈希码,因为唯一可能的影响是我们引入了更多的冲突。
重新混合哈希码并不能改善糟糕的哈希码,因为唯一可能的效果是我们将例如值 53 上的大量冲突更改为大量值 18,3487,291。
重新混合哈希码只能改进哈希码,该哈希码至少在避免整个范围(232 可能值)内的绝对冲突方面做得相当好,但在对实际值进行取模时却很难避免冲突。在哈希表中使用。 虽然二的幂表的更简单模数使这一点更加明显,但它对更常见的素数表也产生了负面影响,只是不那么明显(重新散列的额外工作将超过好处,但好处仍然存在)。
编辑:我还使用了开放寻址,这也会增加对碰撞的敏感性,也许比它是二的幂这一事实更重要。
好吧,令人不安的是 string.GetHashCode() 实现有多少="noreferrer">.NET(或研究此处)可以通过这种方式进行改进(按顺序由于碰撞减少,测试运行速度提高了约 20-30 倍),更令人不安的是我自己的哈希码可以改进多少(远不止于此)。
我过去编写的所有 GetHashCode() 实现(实际上用作此网站上答案的基础)都比我想象的要糟糕得多。 大多数时候,它对于大部分用途来说“足够好”,但我想要更好的东西。
因此,我将该项目放在一边(无论如何,它是一个宠物项目),并开始研究如何在 .NET 中快速生成良好的、分布良好的哈希代码。
最后我决定将 SpookyHash 移植到 .NET。 事实上,上面的代码是使用 SpookyHash 从 32 位输入生成 32 位输出的快速路径版本。
现在,SpookyHash 并不是一段容易快速记住的代码。 我的移植甚至更少,因为我手动内联了很多内容以获得更好的速度*。 但这就是代码重用的目的。
然后我把那个项目放在一边,因为正如原始项目产生了如何产生更好的哈希码的问题一样,该项目产生了如何产生更好的.NET memcpy的问题。
然后我回来了,并生成了大量重载,可以轻松地将几乎所有本机类型(
decimal
†)输入到哈希码中。它速度很快,这主要归功于 Bob Jenkins,因为我移植的他的原始代码仍然更快,特别是在算法优化的 64 位机器上‡。
完整的代码可以在https://bitbucket.org/JonHanna/spookilysharp/src查看,但是认为上面的代码是它的简化版本。
但是,由于它现在已经编写完成,因此可以更轻松地使用它:
它还需要种子值,因此如果您需要处理不受信任的输入并希望防止哈希 DoS 攻击,您可以根据正常运行时间或类似设置种子,并使结果无法被攻击者预测:
*其中一个很大的惊喜是手动内联返回
(x << n) | 的旋转方法。 (x >> -n)
改进了一些东西。 我本来确信抖动会为我内嵌这一点,但分析显示并非如此。†
decimal
虽然来自 C#,但从 .NET 角度来看并不是原生的。 它的问题在于,它自己的GetHashCode()
将精度视为重要的,而它自己的Equals()
则不然。 两者都是有效的选择,但不能像这样混合在一起。 在实现您自己的版本时,您需要选择执行一个或另一个,但我不知道您想要哪个。‡通过比较。 如果用于字符串,64 位上的 SpookyHash 比 32 位上的
string.GetHashCode()
快得多,又比 64 位上的string.GetHashCode()
稍快,它比 32 位上的 SpookyHash 快得多,但仍然足够快,是一个合理的选择。Up until recently my answer would have been very close to Jon Skeet's here. However, I recently started a project which used power-of-two hash tables, that is hash tables where the size of the internal table is 8, 16, 32, etc. There's a good reason for favouring prime-number sizes, but there are some advantages to power-of-two sizes too.
And it pretty much sucked. So after a bit of experimentation and research I started re-hashing my hashes with the following:
And then my power-of-two hash table didn't suck any more.
This disturbed me though, because the above shouldn't work. Or more precisely, it shouldn't work unless the original
GetHashCode()
was poor in a very particular way.Re-mixing a hashcode can't improve a great hashcode, because the only possible effect is that we introduce a few more collisions.
Re-mixing a hash code can't improve a terrible hash code, because the only possible effect is we change e.g. a large number of collisions on value 53 to a large number of value 18,3487,291.
Re-mixing a hash code can only improve a hash code that did at least fairly well in avoiding absolute collisions throughout its range (232 possible values) but badly at avoiding collisions when modulo'd down for actual use in a hash table. While the simpler modulo of a power-of-two table made this more apparent, it was also having a negative effect with the more common prime-number tables, that just wasn't as obvious (the extra work in rehashing would outweigh the benefit, but the benefit would still be there).
Edit: I was also using open-addressing, which would also have increased the sensitivity to collision, perhaps more so than the fact it was power-of-two.
And well, it was disturbing how much the
string.GetHashCode()
implementations in .NET (or study here) could be improved this way (on the order of tests running about 20-30 times faster due to fewer collisions) and more disturbing how much my own hash codes could be improved (much more than that).All the GetHashCode() implementations I'd coded in the past, and indeed used as the basis of answers on this site, were much worse than I'd throught. Much of the time it was "good enough" for much of the uses, but I wanted something better.
So I put that project to one side (it was a pet project anyway) and started looking at how to produce a good, well-distributed hash code in .NET quickly.
In the end I settled on porting SpookyHash to .NET. Indeed the code above is a fast-path version of using SpookyHash to produce a 32-bit output from a 32-bit input.
Now, SpookyHash is not a nice quick to remember piece of code. My port of it is even less so because I hand-inlined a lot of it for better speed*. But that's what code reuse is for.
Then I put that project to one side, because just as the original project had produced the question of how to produce a better hash code, so that project produced the question of how to produce a better .NET memcpy.
Then I came back, and produced a lot of overloads to easily feed just about all of the native types (except
decimal
†) into a hash code.It's fast, for which Bob Jenkins deserves most of the credit because his original code I ported from is faster still, especially on 64-bit machines which the algorithm is optimised for‡.
The full code can be seen at https://bitbucket.org/JonHanna/spookilysharp/src but consider that the code above is a simplified version of it.
However, since it's now already written, one can make use of it more easily:
It also takes seed values, so if you need to deal with untrusted input and want to protect against Hash DoS attacks you can set a seed based on uptime or similar, and make the results unpredictable by attackers:
*A big surprise in this is that hand-inlining a rotation method that returned
(x << n) | (x >> -n)
improved things. I would have been sure that the jitter would have inlined that for me, but profiling showed otherwise.†
decimal
isn't native from the .NET perspective though it is from the C#. The problem with it is that its ownGetHashCode()
treats precision as significant while its ownEquals()
does not. Both are valid choices, but not mixed like that. In implementing your own version, you need to choose to do one, or the other, but I can't know which you'd want.‡By way of comparison. If used on a string, the SpookyHash on 64 bits is considerably faster than
string.GetHashCode()
on 32 bits which is slightly faster thanstring.GetHashCode()
on 64 bits, which is considerably faster than SpookyHash on 32 bits, though still fast enough to be a reasonable choice.从 https://github.com/dotnet/coreclr/pull/14863 开始,有是一种生成哈希码的新方法,非常简单! 只需编写
这将生成高质量的哈希代码,而无需担心实现细节。
As of https://github.com/dotnet/coreclr/pull/14863, there is a new way to generate hash codes that is super simple! Just write
This will generate a quality hash code without you having to worry about the implementation details.
这是一个很好的方法:
以下是如何使用它:
This is a good one:
And here is how to use it:
这是 Jon Skeet 上面发布的算法的另一个流畅实现,但其中不包含分配或装箱操作:
:
用法 由于泛型类型约束,编译器将确保不会使用类调用
HashValue
。 但编译器不支持HashObject
,因为添加通用参数也会添加装箱操作。Here is another fluent implementation of the algorithm posted above by Jon Skeet, but which includes no allocations or boxing operations:
Usage:
The compiler will ensure
HashValue
is not called with a class due to the generic type constraint. But there is no compiler support forHashObject
since adding a generic argument also adds a boxing operation.这是我的简单方法。 我为此使用经典的构建器模式。 它是类型安全的(无装箱/拆箱)并且与 .NET 2.0 兼容(无扩展方法等)。
它的使用方式如下:
这是实际的构建器类:
Here is my simplistic approach. I am using the classic builder pattern for this. It is typesafe (no boxing/unboxing) and also compatbile with .NET 2.0 (no extension methods etc.).
It is used like this:
And here is the acutal builder class:
如果我们的属性不超过 8 个(希望如此),这里还有另一种选择。
ValueTuple
是一个结构体,并且似乎有一个可靠的GetHashCode
实现。这意味着我们可以简单地这样做:
让我们看一下 .NET Core 对
ValueTuple
的GetHashCode
的当前实现。这是来自
ValueTuple
:这是来自
HashHelper
:英文:
如果能更多地了解 ROL-5 哈希码算法的属性,那就太好了。
遗憾的是,我们自己的
GetHashCode
遵循ValueTuple
可能不会像我们希望和期望的那么快。 相关讨论中的此评论说明了直接调用HashHelpers。组合
性能更高。 另一方面,该代码是内部的,因此我们必须复制代码,从而牺牲了我们在这里获得的大部分内容。 另外,我们有责任记住首先与随机种子Combine
。 我不知道如果我们跳过这一步会产生什么后果。If we have no more than 8 properties (hopefully), here is another alternative.
ValueTuple
is a struct and appears to have a solidGetHashCode
implementation.That means we could simply do this:
Let's take a look at .NET Core's current implementation for
ValueTuple
'sGetHashCode
.This is from
ValueTuple
:And this is from
HashHelper
:In English:
It would be nice to know more about the properties of this ROL-5 hash code algorithm.
Regrettably, deferring to
ValueTuple
for our ownGetHashCode
may not be as fast as we would like and expect. This comment in a related discussion illustrates that directly callingHashHelpers.Combine
is more performant. On the flip side, that one is internal, so we'd have to copy the code, sacrificing much of what we had gained here. Also, we'd be responsible for remembering to firstCombine
with the random seed. I don't know what the consequences are if we skip that step.ReSharper 用户可以使用
ReSharper -> 生成 GetHashCode、Equals 等。 编辑-> 生成代码-> 平等会员
。ReSharper users can generate GetHashCode, Equals, and others with
ReSharper -> Edit -> Generate Code -> Equality Members
.我的大部分工作都是通过数据库连接完成的,这意味着我的类都具有来自数据库的唯一标识符。 我总是使用数据库中的 ID 来生成哈希码。
Most of my work is done with database connectivity which means that my classes all have a unique identifier from the database. I always use the ID from the database to generate the hashcode.
与 nightcoder 的解决方案非常相似,只是如果您愿意的话,可以更容易地提高素数。
PS:这是你在嘴里吐了一点的时候,知道这可以重构为一种具有 9 个默认值的方法,但它会更慢,所以你闭上眼睛并尝试忘记它。
Pretty much similar to nightcoder's solution except it's easier to raise primes if you want to.
PS: This is one of those times where you puke a little in your mouth, knowing that this could be refactored into one method with 9 default's but it would be slower, so you just close your eyes and try to forget about it.