visual valores tipos sobre sirve reservadas que para palabras lenguaje excepciones código convertir convenciones comandos codificacion vb.net gethashcode

vb.net - valores - ¿Anula GetHashCode en VB sin soporte de palabras clave comprobado/no verificado?



tipos de excepciones en visual basic (7)

Así que estoy tratando de descubrir cómo sobrescribir correctamente GetHashCode() en VB para una gran cantidad de objetos personalizados. Un poco de búsqueda me lleva a esta maravillosa respuesta .

Excepto que hay un problema: VB carece de la palabra clave checked y unchecked en .NET 4.0. Por lo que puedo decir, de todos modos. Entonces, al usar la implementación de Jon Skeet, intenté crear una anulación así en una clase bastante simple que tiene tres miembros principales: Name As String , Value As Int32 , y [Type] As System.Type . Así que se me ocurre:

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

Problema: Int32 es demasiado pequeño incluso para un objeto simple como este. La instancia particular que probé tiene "Nombre" como una cadena simple de 5 caracteres, y ese hash solo estaba lo suficientemente cerca del límite superior de Int32, que cuando intentó calcificar el segundo campo del hash (Valor), se desbordó. Debido a que no puedo encontrar un equivalente de VB para compatibilidad granular checked / unchecked , no puedo evitar esto.

Tampoco quiero eliminar las comprobaciones de desbordamiento de enteros en todo el proyecto. Esto quizás esté ... 40% completo (lo inventé, TBH), y tengo mucho más código para escribir, así que necesito estos controles de desbordamiento en su lugar por bastante tiempo.

¿Cuál sería la versión "segura" de la versión GetHashCode de Jon para VB e Int32? O, ¿.NET 4.0 ha checked / unchecked en algún lugar que no encuentro muy fácilmente en MSDN?


EDITAR:
Según la pregunta SO relacionada, una de las respuestas no queridas en la parte inferior proporcionó una cuasi- resolución. Digo casi porque parece que ... es hacer trampa. Los mendigos no pueden ser selectivos, ¿verdad?

Traducido de C # a un VB más legible y alineado con el objeto descrito anteriormente (Nombre, Valor, Tipo), obtenemos:

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

Esto hace que el compilador aparentemente "engañe" al generar un tipo anónimo, que luego compila fuera del espacio de nombres del proyecto, presumiblemente con las comprobaciones de desbordamiento de entero deshabilitadas, y permite que las operaciones matemáticas ocurran y simplemente se envuelven cuando se desborda. También parece involucrar códigos de box , que sé que son éxitos de rendimiento. Sin embargo, no hay unboxing.

Pero esto plantea una pregunta interesante. Innumerables veces, lo he visto aquí y en otras partes que tanto VB como C # generan el mismo código IL. Claramente, este no es el caso el 100% del tiempo ... Al igual que el uso de la palabra clave unchecked marcar de C # simplemente causa que se emita un código de operación diferente. Entonces, ¿por qué sigo viendo la suposición de que ambos producen la misma IL repetición? </ retórica-pregunta>

De todos modos, prefiero encontrar una solución que pueda implementarse dentro de cada módulo de objetos. Tener que crear tipos anónimos para cada uno de mis objetos va a parecer desordenado desde una perspectiva ILDASM. No estoy bromeando cuando digo que tengo muchas clases implementadas en mi proyecto.


EDIT2: Abrí un error en MSFT Connect, y la esencia del resultado del VB PM fue que lo considerarán, pero no contengan la respiración: https://connect.microsoft.com/VisualStudio/feedback/details/636564/checked-unchecked-keywords-in-visual-basic

Un vistazo rápido a los cambios en .NET 4.5 sugiere que aún no lo han considerado, entonces ¿quizás .NET 5?

Mi implementación final, que se ajusta a las limitaciones de GetHashCode, sin dejar de ser lo suficientemente rápida y única para VB, se encuentra debajo, derivada del ejemplo de "Rotación giratoria" en esta página :

''// 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

También creo que el hash "Shift-Add-XOR" también puede aplicarse, pero no lo he probado.


Aquí hay una implementación que combina la respuesta de Hans Passant y la respuesta de Jon Skeet .

Funciona incluso para millones de propiedades (es decir, sin excepciones de desbordamiento de enteros) y es muy rápido (menos de 20 ms para generar código hash para una clase con 1,000,000 campos y apenas medible para una clase con solo 100 campos).

Aquí está la estructura para manejar los desbordamientos:

<StructLayout(LayoutKind.Explicit)> Private Structure HashCodeNoOverflow <FieldOffset(0)> Public Int64 As Int64 <FieldOffset(0)> Public Int32 As Int32 End Structure

Y una simple función 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

O si lo prefiere:

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


Después de investigar que VB no nos había dado nada como unchecked y furioso por un tiempo (c # dev ahora haciendo vb), implementé una solución cercana a la publicada por Hans Passant. Fallé en eso. Terrible rendimiento. Esto fue sin duda debido a mi implementación y no a la solución que Hans publicó. Podría haber retrocedido y copiar más de cerca su solución.

Sin embargo, resolví el problema con una solución diferente. Una publicación quejándose de la falta de unchecked en la página de solicitudes de características del lenguaje VB me dio la idea de usar un algoritmo hash ya en el marco. En mi problema, tenía un String and Guid que quería usar para una clave de diccionario. Decidí que un Tupple(Of Guid, String) sería una excelente tienda interna de datos.

Versión original mala

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

Versión muy mejorada

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

Por lo tanto, aunque espero que haya soluciones mucho mejores que esta, estoy bastante contento. Mi desempeño es bueno. Además, el desagradable código de utilidad se ha ido. Esperemos que esto sea útil para otros desarrolladores pobres que se ven obligados a escribir a VB que se encuentra con esta publicación.

Aclamaciones


Puede implementar un asistente de código hash adecuado en un ensamblaje separado, ya sea usando C # y la palabra clave unchecked o la comprobación de desbordamiento para todo el proyecto (posible en ambos proyectos VB.NET y C #). Si lo desea, puede usar ilmerge para fusionar este ensamblaje con su ensamblaje principal.


Respuesta mejorada ¿ Reemplazando GetHashCode en VB sin soporte de palabras clave comprobado / no verificado?

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

Hay un recorte después de cada multiplicación. Y el literal se define explícitamente como Largo porque el operador Y con un argumento entero no pone a cero los bytes superiores.


También descubrí que la propiedad RemoveIntegerChecks MsBuild afecta /removeintchecks propiedad del compilador de VB que impide que el compilador /removeintchecks comprobaciones en tiempo de ejecución:

<PropertyGroup> <RemoveIntegerChecks>true</RemoveIntegerChecks> </PropertyGroup>


Tuve el mismo problema al implementar la solución del Sr. Skeet en vb.net. Terminé usando el operador Mod para llegar allí. Cada Mod por Integer.MaxValue debería devolver el componente menos significativo hasta ese punto y siempre estará dentro de Integer.MaxValue y Integer.MinValue, que debería tener el mismo efecto que si no se hubiera marcado. Probablemente no tengas que modificar tanto como yo (solo cuando hay una posibilidad de que sea más grande que una larga (lo que significaría combinar MUCHOS códigos hash) y luego una vez al final), pero una variante de esto funciona para mí (y te permite jugar usando primos mucho más grandes como algunas de las otras funciones hash sin preocuparte).

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


Use Long para evitar el desbordamiento:

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

El operador Y garantiza que no se lanzará ninguna excepción de desbordamiento. Sin embargo, esto pierde un poco de "precisión" en el código hash calculado, el resultado siempre es positivo. VB.NET no tiene una función incorporada para evitarlo, pero puede usar un truco:

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

Ahora puedes escribir:

Dim hash As Long = 17 '''' etc.. Return NoOverflows.LongToInteger(hash)