Итак, я пытаюсь выяснить, как правильно переопределить GetHashCode()
в VB для большого количества пользовательских объектов. Немного поиска приводит меня к этому замечательному ответу.
За исключением одной проблемы: VB не хватает как ключевого слова checked
, так и unchecked
в .NET 4.0. Насколько я могу судить, так или иначе. Поэтому, используя реализацию 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, что, когда он пытался вычислить второе поле хэша (Value), он переполнился. Поскольку я не могу найти эквивалент VB для гранулярной поддержки checked
/unchecked
, я не могу обойти это.
Я также не хочу удалять проверки переполнения Integer по всему проекту. Эта вещь может быть.... 40% в полном объеме (я сделал это, TBH), и у меня есть намного больше кода для написания, поэтому мне нужны эти проверки переполнения на месте в течение довольно долгого времени.
Какова была бы "безопасная" версия версии Jon GetHashCode
для VB и Int32? Или .NET 4.0 имеет checked
/unchecked
в нем где-то, что я не нахожу очень легко на MSDN?
EDIT:
В рамках связанного вопроса SO один из нелюбимых ответов в самом низу обеспечивал квазирешение. Я говорю квази, потому что кажется, что это... обман. Однако нищие не могут быть выборами, верно?
Переведенный с С# в более читаемый 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
Это приводит к тому, что компилятор, по-видимому, "обманывает", создавая анонимный тип, который затем компилируется за пределами пространства имен проекта, предположительно с проверкой целостности целых чисел, и позволяет математике проходить и просто обертываться, когда она переполняется. Он также, по-видимому, включает в себя box
коды операций, которые, как я знаю, являются хитами производительности. Однако нет распаковки.
Но это вызывает интересный вопрос. Бесчисленное количество раз, я видел, что здесь и в другом месте сказано, что и VB, и С# генерируют один и тот же IL-код. Это явно не так в 100% случаев... Как и использование ключевого слова С# </rhetorical-question > unchecked
, просто приводит к исходу другого кода операции. Итак, почему я продолжаю видеть предположение, что оба продукта повторяют то же самое, что и IL?
В любом случае, я бы предпочел найти решение, которое может быть реализовано в каждом объектном модуле. Создание анонимных типов для каждого из моих объектов будет выглядеть беспорядочно с точки зрения ILDASM. Я не шучу, когда говорю, что в моем проекте реализовано много классов.
EDIT2: Я обнаружил ошибку в MSFT Connect, и суть результата PM VB заключалась в том, что они рассмотрят это, но не задерживайте дыхание:
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" также может применяться, но я его не тестировал.