Перечисляемое переполнение (Sum)
Эй, я использую метод расширения 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;
}
Основываясь на этой декомпиляции, я ожидаю, что она будет либо переполняться, либо не зависящей от контекста вызывающего кода. Почему это переполняется, и как я могу заставить его остановиться?
Ответы
Ответ 1
Код действительно выполняется в блоке С# checked
. Проблема в том, что рефлектор неправильно декомпилирует блоки checked
и вместо этого отображает их как обычные математические операции. Вы можете проверить это самостоятельно, создав проверенный блок, скомпилировав код и затем декомпилировав его в рефлекторе.
Вы также можете проверить это, посмотрев IL, а не декомпилированный код С#. Вместо кода ввода IL IL вы увидите, что добавление происходит с помощью add.ovf. Это версия добавления, которая бросает на переполнения
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
Невозможно заставить этот конкретный метод не перебрасывать. Ваши лучшие варианты следующие
- Перейдите к большему типу, например
long
- Напишите свою собственную версию Sum, которая не использует проверенное дополнение
Ответ 2
Я написал эту функцию для общих перечислений. Мне бы хотелось услышать какие-либо замечания по этому поводу.
public static int SequenceHashCode<T>(IEnumerable<T> seq)
{
unchecked
{
return seq != null ? seq.Aggregate(0, (sum,obj) => sum+obj.GetHashCode()) : 0;
}
}
Ответ 3
checked
применяется только к выражениям в текущем блоке, а не к любому (уже скомпилированному) вызванному методу. Чтобы использовать непроверенную математику, вам нужно реализовать собственную версию Sum
внутри блока unchecked