I wrote the following code recently, and was surprised when ReSharper warned me that the condition is always true:
IEnumerable<int?> values = // get some values
int? sum = values.Sum();
if (sum.HasValue) { /* this code is always executed */ }It’s even more surprising when you consider the following difference:
int?[] values = new int?[] { 1, null };
int? sum1 = values.Sum(); // returns 1
int? sum2 = values[0] + values[1]; // returns nullHere, sum1 is 1, but sum2 is null.
Since Sum<int?> never returns null (not even for any empty sequence, or a
sequence containing all nulls), it’s odd that its return type is int?,
implying that null is a possible return value. Anders
explains that this return type is to keep the pattern of T
Sum<T>(IEnumerable<T>) for nullable types.
But what if you want Sum to return null if the sequence contains a null?
This is easy to simulate using Aggregate, as C# already propagates nulls
properly when using the addition operator:
public static class EnumerableUtility
{
public static int? NullableSum(this IEnumerable<int?> values)
{
return values.Aggregate((int?) 0, (sum, value) => sum + value);
}
}The initial value of 0 is specified to force the sum of an empty list to be
zero; you could change it to `default(int?)` to make an empty list sum
to `null`. A possible optimisation would be to rewrite it with a
foreach loop that returns null as soon as the first null in the sequence
is found.
Update: My very smart coworker points out that changing the initial aggregate value to default(int?) makes the function return null for any input. (This is probably a good reason to include a full unit test suite with every blog post…) A custom enumerator (or test of values.Any() first) could be used if returning null as the sum of an empty sequence is desired.
Posted by Bradley Grainger on June 01, 2009