Enumerable.Sum () переполнен - PullRequest
       9

Enumerable.Sum () переполнен

11 голосов
/ 05 февраля 2010

Эй, я использую метод расширения Enumerable.Sum() из LINQ для вычисления хеш-кодов, и у меня проблема с OverflowExceptions, когда код становится большим. Я пытался поместить вызов в блок unchecked, но, похоже, это не помогло.

В документации MSDN для этого метода сказано, что он выдаст, если значение станет слишком большим, но я проверил в отражателе, и это все, что есть:

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;
}

Исходя из этой декомпиляции, я ожидаю, что она либо переполнится, либо не зависит от контекста вызывающего кода. Почему он переполнен и как я могу остановить его?

Ответы [ 3 ]

9 голосов
/ 05 февраля 2010

Код действительно выполняется в блоке C # checked. Проблема в том, что рефлектор неправильно декомпилирует checked блоки и вместо этого показывает их как обычные математические операции. Вы можете проверить это самостоятельно, создав проверенный блок, скомпилировав код, а затем декомпилировав его в отражатель.

Вы также можете убедиться в этом, взглянув на IL вместо декомпилированного кода C #. Вместо кода операции добавления IL вы увидите, что добавление происходит с помощью add.ovf. Это версия add, которая создает переполнения

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 

Нет способа заставить этот конкретный метод не генерировать переполнение. Ваши лучшие варианты следующие

  1. Переключиться на больший тип, например long
  2. Напишите свою собственную версию Sum, в которой не используется проверенное дополнение
7 голосов
/ 03 апреля 2013

Я написал эту функцию для обобщенных перечислимых элементов. Я хотел бы услышать любые замечания по этому поводу.

public static int SequenceHashCode<T>(IEnumerable<T> seq)
{
    unchecked
    {
        return seq != null ? seq.Aggregate(0, (sum,obj) => sum+obj.GetHashCode()) : 0;
    }
}
1 голос
/ 05 февраля 2010

checked применяется только к выражениям в текущем блоке, а не к любому (уже скомпилированному) вызванному методу. Чтобы использовать непроверенную математику, вам нужно реализовать собственную версию Sum внутри unchecked блока

...