12

嘿,我正在使用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;
}

基于此反编译,我希望它会溢出或不溢出,具体取决于调用代码的上下文。为什么它会溢出,我怎样才能让它停止?

4

3 回答 3

9

代码确实在 C#checked块中执行。问题是反射器没有正确反编译checked块,而是将它们显示为正常的数学运算。您可以通过创建检查块、编译代码然后在反射器中反编译来自行验证这一点。

您还可以通过查看 IL 而不是反编译的 C# 代码来验证这一点。您将看到添加发生在 add.ovf 中,而不是添加 IL 操作码。这是引发溢出的 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 版本,不使用检查加法
于 2010-02-05T17:01:03.407 回答
7

我为通用枚举编写了这个函数。我很想听听有关它的任何评论。

public static int SequenceHashCode<T>(IEnumerable<T> seq)
{
    unchecked
    {
        return seq != null ? seq.Aggregate(0, (sum,obj) => sum+obj.GetHashCode()) : 0;
    }
}
于 2013-04-03T12:13:19.203 回答
1

checked仅适用于当前块中的表达式,不适用于任何(已编译的)调用方法。要使用未经检查的数学,您需要在块Sum内实现自己的版本unchecked

于 2010-02-05T17:05:31.067 回答