所以我试图弄清楚如何GetHashCode()
在 VB 中正确覆盖大量自定义对象。稍微搜索一下,我就得到了这个美妙的答案。
除了有一个问题:VB在.NET 4.0 中缺少checked
and关键字。unchecked
据我所知,无论如何。因此,使用 Jon Skeet 的实现,我尝试在一个相当简单的类上创建这样的覆盖,该类具有三个主要成员:Name As String
、Value As Int32
和[Type] As System.Type
. 因此,我想出了:
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 等效项,所以我无法解决这个问题。
我也不想在整个项目中删除整数溢出检查。这件事可能已经完成了....40%(这是我编的,TBH),而且我还有很多代码要编写,所以我需要在相当长的一段时间内完成这些溢出检查。
Jon 的GetHashCode
VB 和 Int32 版本的“安全”版本是什么?或者,.NET 4.0 中是否有checked
/unchecked
我在 MSDN 上不太容易找到的地方?
编辑:
根据链接的 SO 问题,最底部不受欢迎的答案之一提供了准解决方案。我说准是因为感觉它是……作弊。不过,乞丐不能挑剔,对吧?
从 C# 翻译成更易读的 VB 并与上述对象(名称、值、类型)对齐,我们得到:
Public Overrides Function GetHashCode() As Int32
Return New With { _
Key .A = _Name, _
Key .B = _Value, _
Key .C = _Type
}.GetHashCode()
End Function
这显然会触发编译器通过生成一个匿名类型来“作弊”,然后它会在项目命名空间之外进行编译,大概禁用整数溢出检查,并允许进行数学运算并在溢出时简单地回绕。它似乎也涉及box
操作码,我知道这会影响性能。不过没有拆箱。
但这提出了一个有趣的问题。无数次,我在这里和其他地方看到它指出 VB 和 C# 都生成相同的 IL 代码。这显然不是 100% 的情况......就像使用 C# 的 </修辞问题>unchecked
关键字只会导致发出不同的操作码。那么为什么我会继续看到两者都产生完全相同的 IL 的假设不断重复呢?
无论如何,我宁愿找到一个可以在每个对象模块中实现的解决方案。从 ILDASM 的角度来看,必须为我的每一个对象创建匿名类型看起来会很混乱。当我说我的项目中实现了很多类时,我不是在开玩笑。
EDIT2:我确实在 MSFT Connect 上打开了一个错误,VB PM 结果的要点是他们会考虑它,但不要屏住呼吸:
https ://connect.microsoft.com/VisualStudio/反馈/详细信息/636564/checked-unchecked-keywords-in-visual-basic
快速浏览一下 .NET 4.5 的变化表明他们还没有考虑到它,所以也许是 .NET 5?
下面是我的最终实现,它符合 GetHashCode 的约束,同时对于 VB 来说仍然足够快且足够独特,源自此页面上的“旋转哈希”示例:
'// 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”哈希也可能适用,但我还没有测试过。