问题描述
所以我试图弄清楚如何在 VB 中为大量自定义对象正确覆盖 GetHashCode()
.经过一番搜索,我找到了 这个精彩的答案.
So I'm trying to figure out how to correctly override GetHashCode()
in VB for a large number of custom objects. A bit of searching leads me to this wonderful answer.
除了有一个问题:VB 在 .NET 4.0 中缺少 checked
和 unchecked
关键字.据我所知,无论如何.因此,使用 Jon Skeet 的实现,我尝试在具有三个主要成员的相当简单的类上创建这样的覆盖:Name As String
、Value As Int32
和 [Type] As System.Type
.因此我想出了:
Except there's one problem: VB lacks both the checked
and unchecked
keyword in .NET 4.0. As far as I can tell, anyways. So using Jon Skeet's implementation, I tried creating such an override on a rather simple class that has three main members: Name As String
, Value As Int32
, and [Type] As System.Type
. Thus I come up with:
Public Overrides Function GetHashCode() As Int32
Dim hash As Int32 = 17
hash = hash * 23 + _Name.GetHashCode()
hash = hash * 23 + _Value
hash = hash * 23 + _Type.GetHashCode()
Return hash
End Function
问题:Int32 对于这样的简单对象来说也太小了.我测试的特定实例将名称"作为一个简单的 5 个字符的字符串,并且单独的散列就足够接近 Int32 的上限,当它尝试计算散列的第二个字段(值)时,它溢出了.因为我找不到用于粒度 checked
/unchecked
支持的 VB 等价物,所以我无法解决这个问题.
Problem: Int32 is too small for even a simple object such as this. The particular instance I tested has "Name" as a simple 5-character string, and that hash alone was close enough to Int32's upper limit, that when it tried to calc the second field of the hash (Value), it overflowed. Because I can't find a VB equivalent for granular checked
/unchecked
support, I can't work around this.
我也不想在整个项目中删除整数溢出检查.这件事可能已经完成了....40%(我编的,TBH),而且我还有很多代码要写,所以我需要在很长一段时间内进行这些溢出检查.
I also do not want to remove Integer overflow checks across the entire project. This thing is maybe....40% complete (I made that up, TBH), and I have a lot more code to write, so I need these overflow checks in place for quite some time.
对于 VB 和 Int32,Jon 的 GetHashCode
版本的安全"版本是什么?或者,.NET 4.0 是否有 checked
/unchecked
在我在 MSDN 上不容易找到的某个地方?
根据链接的 SO 问题,其中一个 不受欢迎的答案 提供了一个准解决方案.我说准是因为感觉就像是......作弊.不过,乞丐不能挑剔,对吧?
What would be the "safe" version of Jon's GetHashCode
version for VB and Int32? Or, does .NET 4.0 have checked
/unchecked
in it somewhere that I'm not finding very easily on MSDN?
Per the linked SO question, one of the unloved answers at the very bottom provided a quasi-solution. I say quasi because it feels like it's....cheating. Beggars can't be choosers, though, right?
从 C# 转换为更易读的 VB 并与上述对象(名称、值、类型)对齐,我们得到:
Translated from from C# into a more readable VB and aligned to the object described above (Name, Value, Type), we get:
Public Overrides Function GetHashCode() As Int32
Return New With { _
Key .A = _Name, _
Key .B = _Value, _
Key .C = _Type
}.GetHashCode()
End Function
这显然会触发编译器通过生成匿名类型来欺骗",然后它在项目命名空间之外编译,大概是禁用了整数溢出检查,并允许进行数学运算并在溢出时简单地回绕.它似乎还涉及 box
操作码,我知道这会影响性能.不过没有拆箱.
This triggers the compiler apparently to "cheat" by generating an anonymous type, which it then compiles outside of the project namespace, presumably with integer overflow checks disabled, and allows the math to take place and simply wrap around when it overflows. It also seems to involve box
opcodes, which I know to be performance hits. No unboxing, though.
</rhetorical-question>
</rhetorical-question>
无论如何,我宁愿找到一个可以在每个对象模块中实现的解决方案.从 ILDASM 的角度来看,必须为我的每一个对象创建匿名类型会看起来很混乱.当我说我的项目中实现了很多 类时,我并不是在开玩笑.
我确实在 MSFT Connect 上打开了一个错误,VB PM 结果的要点是他们会考虑它,但不要屏住呼吸:https://connect.microsoft.com/VisualStudio/feedback/details/636564/checked-unchecked-keywords-in-visual-basic
Anyways, I'd rather find a solution that can be implemented within each object module. Having to create Anonymous Types for every single one of my objects is going to look messy from an ILDASM perspective. I'm not kidding when I say I have a lot of classes implemented in my project.
I did open up a bug on MSFT Connect, and the gist of the outcome from the VB PM was that they'll consider it, but don't hold your breath:https://connect.microsoft.com/VisualStudio/feedback/details/636564/checked-unchecked-keywords-in-visual-basic
快速浏览 .NET 4.5 中的变化表明他们还没有考虑过,所以也许是 .NET 5?
A quick look at the changes in .NET 4.5 suggests they've not considered it yet, so maybe .NET 5?
我的最终实现符合 GetHashCode 的约束,同时对于 VB 仍然快速且足够独特如下,源自 这个页面:
My final implementation, which fits the constraints of GetHashCode, while still being fast and unique enough for VB is below, derived from the "Rotating Hash" example on this page:
'// The only sane way to do hashing in VB.NET because it lacks the
'// checked/unchecked keywords that C# has.
Public Const HASH_PRIME1 As Int32 = 4
Public Const HASH_PRIME2 As Int32 = 28
Public Const INT32_MASK As Int32 = &HFFFFFFFF
Public Function RotateHash(ByVal hash As Int64, ByVal hashcode As Int32) As Int64
Return ((hash << HASH_PRIME1) Xor (hash >> HASH_PRIME2) Xor hashcode)
End Function
我也认为Shift-Add-XOR"哈希也可能适用,但我还没有测试过.
I also think the "Shift-Add-XOR" hash may also apply, but I haven't tested it.
推荐答案
使用 Long 避免溢出:
Use Long to avoid the overflow:
Dim hash As Long = 17
'' etc..
Return CInt(hash And &H7fffffffL)
And 运算符确保不会引发溢出异常.然而,这确实会在计算出的哈希码中丢失一位精度",结果始终为正.VB.NET 没有内置函数来避免它,但你可以使用一个技巧:
The And operator ensures no overflow exception is thrown. This however does lose one bit of "precision" in the computed hash code, the result is always positive. VB.NET has no built-in function to avoid it, but you can use a trick:
Imports System.Runtime.InteropServices
Module NoOverflows
Public Function LongToInteger(ByVal value As Long) As Integer
Dim cast As Caster
cast.LongValue = value
Return cast.IntValue
End Function
<StructLayout(LayoutKind.Explicit)> _
Private Structure Caster
<FieldOffset(0)> Public LongValue As Long
<FieldOffset(0)> Public IntValue As Integer
End Structure
End Module
现在你可以写:
Dim hash As Long = 17
'' etc..
Return NoOverflows.LongToInteger(hash)
这篇关于在没有选中/未选中关键字支持的情况下覆盖 VB 中的 GetHashCode?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!