Переопределение GetHashCode в VB без поддержки проверенных / непроверенных ключевых слов?
поэтому я пытаюсь выяснить, как правильно переопределить GetHashCode()
в VB для большого количества пользовательских объектов. Немного поисков приводит меня к это замечательный ответ.
за исключением одной проблемы: VB не хватает обоих checked
и unchecked
ключевое слово в .NET 4.0. Во всяком случае, насколько я могу судить. Поэтому, используя реализацию Джона Скита, я попытался создать такое переопределение на довольно простом классе, который имеет три основных члена: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, что, когда он попытался вычислить второе поле хэша (значение), он переполнен. Потому что я не могу найти эквивалент VB для granular checked
/unchecked
поддержка, я не могу обойти это.
я также не хочу удалять Integer проверка переполнения по всему проекту. Эта штука может быть....40% завершено (я сделал это, TBH), и у меня есть намного больше кода для записи, поэтому мне нужны эти проверки переполнения в течение некоторого времени.
какой будет "безопасная" версия Джона GetHashCode
версия для VB и Int32? Или у .NET 4.0 есть checked
/unchecked
в нем где-то, что я не нахожу очень легко на MSDN?
EDIT:
По связанному вопросу 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/feedback/details/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 " также может применяться, но я его не тестировал.
7 ответов
используйте Long, чтобы избежать переполнения:
Dim hash As Long = 17
'' etc..
Return CInt(hash And &H7fffffffL)
оператор 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
Теперь вы можете написать:
Dim hash As Long = 17
'' etc..
Return NoOverflows.LongToInteger(hash)
вот реализация, объединяющая ответ Ганса Пассанта и ответ Джона Скита.
он работает даже для миллионов свойств (т. е. без исключений переполнения целых чисел) и очень быстр (менее 20 мс для генерации хэш-кода для класса с 1,000,000 полями и едва измеримым для класса только с 100 полями).
вот структура для обработки переполнений:
<StructLayout(LayoutKind.Explicit)>
Private Structure HashCodeNoOverflow
<FieldOffset(0)> Public Int64 As Int64
<FieldOffset(0)> Public Int32 As Int32
End Structure
и простой 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
или если вы предпочитаете:
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
У меня была такая же проблема с реализацией решения г-на скита в vb.net - ... Я закончил тем, что использовал оператор Mod, чтобы добраться туда. Каждый мод целым числом.MaxValue должен возвращать только наименее значимый компонент до этой точки и всегда будет находиться в Integer.MaxValue и целое число.MinValue -- который должен иметь тот же эффект, что и unchecked. Вам, вероятно, не нужно мод так часто, как я (это только тогда, когда есть шанс получить больше, чем длинный (что означало бы объединение большого количества хэша коды), а затем один раз в конце), но вариант этого работает для меня (и позволяет вам играть с использованием гораздо больших простых чисел, как и некоторые другие хэш-функции, не беспокоясь).
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
вы можете реализовать подходящий помощник хэш-кода в отдельной сборке, используя C# и unchecked
ключевое слово или проверка переполнения для всего проекта (возможно в обоих VB.NET и проекты c#). Если вы хотите, вы можете использовать ilmerge
чтобы объединить эту сборку с основной сборкой.
улучшена ответа переопределение GetHashCode в VB без поддержки проверенных / непроверенных ключевых слов?
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 с целочисленным аргументом не обнуляет верхние байты.
Я также обнаружил, что RemoveIntegerChecks свойство MsBuild влияет /removeintchecks свойство компилятора VB, которое предотвращает компилятор от проверки выполнения:
<PropertyGroup>
<RemoveIntegerChecks>true</RemoveIntegerChecks>
</PropertyGroup>
после исследования, что VB не дал нам ничего подобного unchecked
и бушуя немного (c# dev теперь делает vb), я реализовал решение, близкое к тому, которое опубликовал Ханс Пассант. Мне это не удалось. Ужасное представление. Это, безусловно, было связано с моей реализацией, а не с решением, которое опубликовал Ханс. Я мог бы вернуться и более точно скопировать его решение.
тем не менее, я решил проблему с другим решением. Сообщение с жалобой на отсутствие unchecked
на языке VB страница запросов функций дала мне идею использовать хэш-алгоритм уже в рамках. В моей проблеме у меня был String
и Guid
который я хотел использовать для словарного ключа. Я решил Tupple(Of Guid, String)
было бы прекрасным внутренним хранилищем данных.
Оригинальная Плохая Версия
Public Structure HypnoKey
Public Sub New(name As String, areaId As Guid)
_resourceKey = New Tuple(Of Guid, String)(resourceAreaId, key)
End Sub
Private ReadOnly _name As String
Private ReadOnly _areaId As Guid
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public ReadOnly Property AreaId As Guid
Get
Return _areaId
End Get
End Property
Public Overrides Function GetHashCode() As Integer
'OMFG SO BAD
'TODO Fail less hard
End Function
End Structure
Значительно Улучшенная Версия
Public Structure HypnoKey
Public Sub New(name As String, areaId As Guid)
_innerKey = New Tuple(Of Guid, String)(areaId , key)
End Sub
Private ReadOnly _innerKey As Tuple(Of Guid, String)
Public ReadOnly Property Name As String
Get
Return _innerKey.Item2
End Get
End Property
Public ReadOnly Property AreaId As Guid
Get
Return _innerKey.Item1
End Get
End Property
Public Overrides Function GetHashCode() As Integer
Return _innerKey.GetHashCode() 'wow! such fast (enuf)
End Function
End Structure
Итак, хотя я ожидаю, что есть гораздо лучшие решения, чем это, я очень доволен. У меня хорошая работа. Также неприятный код утилиты исчез. Надеюсь, это полезно для некоторых других бедных разработчиков, вынужденных писать VB, которые сталкиваются с этим сообщением.
Ура