2010-02-05 11 views
11

Oye, estoy usando el método de extensión Enumerable.Sum() de LINQ para calcular los códigos hash, y estoy teniendo un problema con OverflowExceptions cuando el código se vuelve grande. Traté de poner la llamada en un bloque unchecked, pero eso no pareció ayudar.Enumerable.Sum() desbordamiento

La documentación de MSDN para el método dice que va a lanzar si el valor se hace demasiado grande, pero me registré reflector y esto es todo lo que hay:

public static int Sum(this IEnumerable<int> source) { 
    if (source == null) { 
     throw Error.ArgumentNull("source"); 
    } 
    int num = 0; 
    foreach (int num2 in source) { 
     num += num2; 
    } 
    return num; 
} 

En base a esta descompilación, yo esperaría que desbordamiento o no dependiendo del contexto del código de llamada. ¿Por qué se desborda y cómo puedo lograr que se detenga?

+3

Esto no responde a la pregunta sobre el desbordamiento ... pero si usa 'Suma' para calcular el código hash de un objeto, probablemente no va a crear códigos hash muy bien distribuidos. El enfoque típico es algo así como multiplicar-por-prime-y-izquierda-cambiar-xor-siguiente en un contexto sin marcar. –

+0

Sí, no es ideal, pero los códigos hash que estoy sumando (los códigos hash de los subcomponentes) se generan de una manera mucho mejor, así que no estoy tan preocupado por eso. (No estoy agregando 'int's, donde los pequeños cambios no producirían un código muy diferente.) Supongo que esto no es algo por lo que debería volverme loco, pero ¿tal vez es más importante de lo que imagino ...? –

+0

Podría desbordar solo dos elementos, si los códigos hash están en o cerca de Int32.MaxValue. Dado que se trata de enteros, esto no se vuelve aparente hasta que tenga muchos elementos, pero con una función hash correctamente distribuida esto arrojará excepciones más de las veces – thecoop

Respuesta

9

El código se está ejecutando efectivamente en un bloque C# checked. El problema es que el reflector no descompila correctamente los bloques checked y en su lugar los muestra como operaciones matemáticas normales. Puede verificar esto usted mismo creando un bloque verificado, compilando el código y luego descompilando en reflector.

También puede verificar esto mirando el IL en lugar del código decompilado C#. En lugar de agregar el código de operación IL, verá que la adición se produce con add.ovf. Esta es la versión del complemento que arroja sobre los desbordamientos

L_001a: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current() 
L_001f: stloc.1 
L_0020: ldloc.0 
L_0021: ldloc.1 
L_0022: add.ovf <-- This is an overflow aware addition 
L_0023: stloc.0 
L_0024: ldloc.2 

No hay manera de conseguir este método en particular no tirar en caso de desbordamiento. Sus mejores opciones son las siguientes

  1. Cambiar a un tipo más grande, como long
  2. Escribe tu propia versión de Suma que no hace uso de comprobarse además
+0

Gracias. Tengo que ponerme más cómodo con IL ... –

1

checked sólo se aplica a las expresiones en el bloque actual , no cualquier método llamado (ya compilado). Para usar las matemáticas sin marcar, deberá implementar su propia versión de Sum dentro de un bloque unchecked

+0

Así que se determina la distinción marcada/no marcada en tiempo de compilación? Hubiera esperado que fuera un tiempo de ejecución, dependiendo del contexto, pero supongo que me equivocaría. –

+0

Como JaredPar ha respondido, genera diferentes comandos IL ya sea en un bloque marcado o no marcado; usted no puede cambiar IL- – thecoop

7

Escribí esta función para los enumerables genéricos. Me encantaría escuchar cualquier comentario al respecto.

public static int SequenceHashCode<T>(IEnumerable<T> seq) 
{ 
    unchecked 
    { 
     return seq != null ? seq.Aggregate(0, (sum,obj) => sum+obj.GetHashCode()) : 0; 
    } 
} 
+1

ya compilado Me gusta eso. Por lo que puedo ver, falta un cheque para obj == null. – Fried