2016-02-17 29 views
3

我有一個簡單相加的擴展:C#LINQ總和()擴展爲大量

public static int? SumOrNull<TSource>(this IEnumerable<TSource> source, Func<TSource, int> projection) 
{ 
    return source.Any() 
     ? source.Sum(projection) 
     : (int?)null; 
} 

但它會導致System.OverflowException: Arithmetic operation resulted in an overflow.

我想要做的是這樣的:

public static ulong? SumOrNull<TSource>(this IEnumerable<TSource> source, Func<TSource, int> projection) 
{ 
    return source.Any() 
     ? source.Sum(projection) 
     : (ulong?)null; 
} 

但是Linq Sum沒有重載,返回ulong和編譯錯誤。 任何方式來使這項工作?

+0

cast'source.Sum(projection)'to'ulong' –

+0

可能有解決方法:'decimal.MaxValue> ulong.MaxValue'返回'true'因此'Sum(e =>(decimal)e.Value)'。請記住,Linq-to-Sql不喜歡黑客。 – PTwr

+1

你需要ulong嗎?也許很長就足夠了? – slawekwin

回答

5

您可以手動實施它。這裏有一個例子:

public static ulong? SumOrNull<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, int> projection) 
{ 
    bool any = false; 

    ulong sum = 0; 

    foreach (var item in source) 
    { 
     any = true; 

     //As commented by CodesInChaos, 
     //we use the checked keyword to make sure that 
     //we throw an exception if there are any negative numbers 
     sum = sum + (ulong)checked((uint)projection(item)); 
    } 

    if (!any) 
     return null; 

    return sum; 
} 
+0

簡單地將投影結果轉換爲'long',例如'source.Sum((long)projection)'? –

+0

這種方法的優點是隻能枚舉一次輸入序列。 – CodesInChaos

+1

我不會直接從signed int轉換爲無符號long值。我可能會使用'(ulong)checked((uint)value)'代替。 – CodesInChaos

1

Sum方法有很長的overload,但你需要傳遞一個IEnumerable<long>,爲了做到這一點,就可以把投影的結果long

public static ulong? SumOrNull<TSource>(this IEnumerable<TSource> source, Func<TSource, int> projection) 
{ 
    return source.Any() 
      ? (ulong?)source.Sum(x => (long)projection(x)) 
      : null; 
} 
1

如果你堅持ulong(未long),可以使用Aggregate,而不是Sum

public static ulong? SumOrNull<TSource>(IEnumerable<TSource> source, 
             Func<TSource, ulong> projection) { 
    return source.Any() 
    ? source.Aggregate((ulong?) 0, (s, x) => s + projection(x)) 
    : null; 
}