29

GetHashCode()そのため、VB で多数のカスタム オブジェクトを正しくオーバーライドする方法を見つけようとしています。少し検索すると、この素晴らしい答えにたどり着きます。

ただし、問題が 1 つあります。VBには、.NET 4.0のcheckedandキーワードがありません。uncheckedとにかく、私が知る限り。Name As Stringそこで、Jon Skeet の実装を使用して、Value As Int32、 、の 3 つの主要メンバーを持つかなり単純なクラスで、このようなオーバーライドを作成しようとしました[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 の上限に十分近く、ハッシュの 2 番目のフィールド (値) を計算しようとすると、オーバーフローしました。checkedグラニュラー/サポートに相当する VB が見つからないため、uncheckedこれを回避することはできません。

また、プロジェクト全体で整数オーバーフロー チェックを削除したくありません。これはおそらく....40% 完了しています (私が作成したものです、TBH)。さらに多くのコードを記述する必要があるため、これらのオーバーフロー チェックをかなりの時間行う必要があります。

GetHashCodeVB および Int32 用の Jon のバージョンの「安全な」バージョンは何でしょうか? または、.NET 4.0 には、MSDN で簡単に見つけられない checked/が含まれていますか? 編集: リンクされたSOの質問ごとに、一番下にある愛されていない回答の1つが解決策を提供しました。準と言うのは、それが…不正行為のように感じるからです。物乞いは選ぶ人になれませんよね?unchecked



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」ハッシュも適用される可能性があると思いますが、テストしていません。

4

7 に答える 7

25

オーバーフローを回避するには、Long を使用します。

Dim hash As Long = 17
'' etc..
Return CInt(hash And &H7fffffffL)

And 演算子は、オーバーフロー例外がスローされないようにします。ただし、これにより、計算されたハッシュ コードの「精度」が 1 ビット失われ、結果は常に正になります。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

これで、次のように記述できます。

Dim hash As Long = 17
'' etc..
Return NoOverflows.LongToInteger(hash)
于 2011-01-11T11:03:16.587 に答える
2

uncheckedC#とキーワードを使用するか、プロジェクト全体のオーバーフローチェックをオンにする(VB.NETプロジェクトとC#プロジェクトの両方で可能)ことにより、適切なハッシュコードヘルパーを別のアセンブリに実装できます。ilmerge必要に応じて、を使用してこのアセンブリをメインアセンブリにマージできます。

于 2011-01-11T10:25:54.547 に答える
2

チェック/チェックされていないキーワードのサポートなしでVBでGetHashCodeをオーバーライドする回答を改善しましたか?

Public Overrides Function GetHashCode() as Integer
  Dim hashCode as Long = 0
  If myReplacePattern IsNot Nothing Then _
    hashCode = ((hashCode*397) Xor myField.GetHashCode()) And &HffffffffL
  If myPattern IsNot Nothing Then _
    hashCode = ((hashCode*397) Xor myOtherField.GetHashCode()) And &HffffffffL
  Return CInt(hashCode)
End Function

各乗算後にトリミングがあります。And リテラルは明示的に Long として定義されます。これは、Integer 引数を持つ And 演算子が上位バイトをゼロ化しないためです。

于 2011-08-03T12:44:35.673 に答える