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 null
Here, 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