« WrappingStream Implementation | Main | Using If-Modified-Since in HTTP Requests »

June 1, 2009

Enumerable.Sum never returns null

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 at June 1, 2009 4:55 PM

Trackback Pings

TrackBack URL for this entry:
http://ancientblogs.logos.com/mt-cgi/mt-tb.cgi/293