在没有选中/取消选中关键字支持的情况下覆盖VB中的GetHashCode?

Kum*_*mba 24 vb.net gethashcode

所以我试图弄清楚如何GetHashCode()在VB中正确覆盖大量的自定义对象.一些搜索引导我得到这个美妙的答案.

除了有一个问题:VB缺少.NET 4.0中的checkedunchecked关键字.据我所知,无论如何.因此,使用乔恩斯基特的实现,我想对具有三个主要成员一个相当简单的类创建这样一个覆盖: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
Run Code Online (Sandbox Code Playgroud)

问题:即使像这样的简单对象,Int32也太小了.我测试的特定实例将"Name"作为一个简单的5个字符的字符串,并且该哈希值足够接近Int32的上限,当它试图计算哈希值(Value)的第二个字段时,它会溢出.因为我找不到粒度checked/ unchecked支持的VB等价物,所以我无法解决这个问题.

我也不想删除整个项目中的Integer溢出检查.这个东西可能...... 40%完成(我做了,TBH),我有更多的代码要写,所以我需要这些溢出检查很长一段时间.

GetHashCode对于VB和Int32 ,Jon的版本的"安全"版本是什么?或者,.NET 4.0 在某个地方有checked/ unchecked我在MSDN上很难找到吗?


编辑:
根据链接的SO问题,最底层的一个不受欢迎的答案提供了一个解决方案.我说准,因为它感觉像是......作弊.乞丐不能选择,对吧?

从C#转换为更易读的VB并与上述对象(Name,Value,Type)对齐,我们得到:

Public Overrides Function GetHashCode() As Int32
    Return New With { _
        Key .A = _Name, _
        Key .B = _Value, _
        Key .C = _Type
     }.GetHashCode()
End Function
Run Code Online (Sandbox Code Playgroud)

这会触发编译器显然通过生成匿名类型来"欺骗",然后它会在项目命名空间之外编译,可能会禁用整数溢出检查,并允许数学发生并在溢出时简单地回绕.它似乎也涉及box操作码,我知道它是性能命中.但是,没有取消装箱.

但这提出了一个有趣的问题.无数次,我已经看到它在这里和其他地方声明VB和C#都生成相同的IL代码.这显然不是100%的情况......就像使用C#的unchecked关键字只会导致不同的操作码被发出.那么为什么我还要继续看到两者都产生完全相同的IL不断重复的假设?  </修辞-问题>

无论如何,我宁愿找到一个可以在每个对象模块中实现的解决方案.从ILDASM的角度来看,必须为我的每一个对象创建匿名类型都会变得混乱.当我说我在项目中实施了很多课程时,我不是在开玩笑.


EDIT2:我确实在MSFT Connect上打开了一个错误,VB PM结果的要点是他们会考虑它,但不要屏住呼吸:https: //connect.microsoft.com/VisualStudio/反馈/信息/ 636564 /签未选中的关键字,在视觉,基本

快速浏览.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
Run Code Online (Sandbox Code Playgroud)

我也认为"Shift-Add-XOR"哈希也可能适用,但我还没有测试过.

Han*_*ant 23

使用Long可以避免溢出:

Dim hash As Long = 17
'' etc..
Return CInt(hash And &H7fffffffL)
Run Code Online (Sandbox Code Playgroud)

And运算符确保不会抛出溢出异常.然而,这确实在计算的哈希码中失去了一点"精度",结果总是正的.VB.NET没有内置函数来避免它,但你可以使用一个技巧:

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
Run Code Online (Sandbox Code Playgroud)

现在你可以写:

Dim hash As Long = 17
'' etc..
Return NoOverflows.LongToInteger(hash)
Run Code Online (Sandbox Code Playgroud)

  • @Hans:"40亿是一个非常大的数字." - 他们对IPv4说同样的事情:)最终/ 8次分配将于今年年初发布.现在每个人都在"340万亿^ 3 IPv6地址是一个大量"的潮流.给我们时间,我们都会看到340万亿^ 3到底有多大:) (2认同)
  • 这是不正确的.最大的有符号整数是&H7FFFFFFF,它应该是掩码值,否则在转换回整数时会出现溢出. (2认同)
  • 只是想向任何讨厌魔法数字的人指出(由于这些评论之类的论据),您可以这样做:`Return hash And` **`Int32.MaxValue`** (2认同)

JRS*_*JRS 9

这是一个结合Hans Passant答案Jon Skeet答案的实现.

它甚至可以用于数百万个属性(即没有整数溢出异常)并且非常快(对于具有1,000,000个字段的类生成哈希代码少于20毫秒,对于只有100个字段的类几乎不可测量).

这是处理溢出的结构:

<StructLayout(LayoutKind.Explicit)>
Private Structure HashCodeNoOverflow
    <FieldOffset(0)> Public Int64 As Int64
    <FieldOffset(0)> Public Int32 As Int32
End Structure
Run Code Online (Sandbox Code Playgroud)

还有一个简单的GetHashCode函数:

Public Overrides Function GetHashCode() As Integer

    Dim hashCode As HashCodeNoOverflow

    hashCode.Int64 = 17

    hashCode.Int64 = CLng(hashCode.Int32) * 23 + Field1.GetHashCode
    hashCode.Int64 = CLng(hashCode.Int32) * 23 + Field2.GetHashCode
    hashCode.Int64 = CLng(hashCode.Int32) * 23 + Field3.GetHashCode

    Return hashCode.Int32

End Function
Run Code Online (Sandbox Code Playgroud)

或者如果您愿意:

Public Overrides Function GetHashCode() As Integer

    Dim hashCode = New HashCodeNoOverflow With {.Int32 = 17}

    For Each field In Fields
        hashCode.Int64 = CLng(hashCode.Int32) * 23 + field.GetHashCode
    Next

    Return hashCode.Int32

End Function
Run Code Online (Sandbox Code Playgroud)


Sea*_*art 5

我在vb.net中实现Skeet先生的解决方案时遇到了同样的问题.我最终使用Mod运算符到达那里.每个Mod by Integer.MaxValue应该只返回到该点的最不重要的组件,并且将始终在Integer.MaxValue和Integer.MinValue中 - 这应该与未选中的效果相同.你可能没有像我那样经常修改(只有当它有可能变得比一个长的大(这意味着要结合很多哈希码)然后一次结束时),但是这个的一个变种对我来说(并且让你玩一些像其他散列函数一样使用更大的素数而不用担心).

Public Overrides Function GetHashCode() As Int32
    Dim hash as Int64 = 17
    hash = (hash * 23 + _Name.GetHashCode()) Mod Integer.MaxValue
    hash = (hash * 23 + _Value) Mod Integer.MaxValue
    hash = (hash * 23 + _Type.GetHashCode()) Mod Integer.MaxValue
    Return Convert.ToInt32(hash)
End Function
Run Code Online (Sandbox Code Playgroud)