10

I suppose answer is trivial:

List<int?> intList = new List<int?>(){null, null, null};
int? sum = list.Sum();
int sum2 = list.Sum(a => a.HasValue ? a.Value : 0);
int sum3 = list.Sum().Value;

Sum is always returning 0, why is nullable needed at all? Is there a way to force linq sum to return null? What am I missing here?

broadband
  • 3,266
  • 6
  • 43
  • 73
  • In terms of logic, how is **sum** of items supposed to be a **non existent object**? Anyway, you can simply check that `if (intList.All(x => x == null)) return null;`. – Yeldar Kurmangaliyev Apr 21 '17 at 08:02
  • I think this has more in common with relational algebra and the implementation on major relational databases. Usually NULL is ignored in the majority of aggregation functions except for COUNT() which is debatable. I don't say that LINQ is following relational algebra, is more about the behavior on a set of data. There are some historical points [here](https://msdn.microsoft.com/en-us/library/aa479863.aspx). – andreim Apr 21 '17 at 08:34

5 Answers5

7

Here is the implementation of Sum()

public static int? Sum(this IEnumerable<int?> source) {
if (source == null) throw Error.ArgumentNull("source");
int sum = 0;
checked {
    foreach (int? v in source) {
        if (v != null) sum += v.GetValueOrDefault();
    }
}
return sum;

The reason for not returning null is the way it's implemented - the usage of int sum = 0; as result can never return null.

fubo
  • 44,811
  • 17
  • 103
  • 137
  • Ahh, here lies the reason for this. But still, why would microsoft develop Sum like this. Sum always return some value int, decimal, float, .. – broadband Apr 21 '17 at 08:27
  • @broadband Actually it's even [documented](https://msdn.microsoft.com/en-us/library/bb156065(v=vs.110).aspx): *This method returns **zero** if source contains no elements.*. As of why, you should ask MSFTs, for me it's a bug (which due to their backward compatibility policy will never be fixed) because the corresponding `IQueryable` method works differently when implemented by real (non LINQ to Objects) query provider. – Ivan Stoev Apr 21 '17 at 08:46
  • @IvanStoev don't think is a bug. Look at some examples from the db world - SUM and other aggregation functions ignores NULL on SqlServer, Oracle, MySql, Postgres, Mongo, ... It seems that multiple players go towards the same thing. So Linq goes the same way too. So the queryable provider will work by issuing the command to the database server thus obtaining the same result 0. The only difference is in the fact that the Enumerable.Sum() throws an exception if null is provided vs. SUM() on SQL which returns NULL when no value is provided. – andreim Apr 21 '17 at 09:08
  • 2
    @omacarena We are ok with ignoring nulls. The problem is that all databases (SQL) `SUM` return `NULL` when there are no non null values in the aggregating set, but LINQ returns 0 (zero) which is inconsistent. – Ivan Stoev Apr 21 '17 at 09:16
  • The reason Sum always returns zero is not because of the usage of `int sum = 0`. Rather it is because of the check for null `if (v != null)`. Without this check, it would return null if there is null value in the collection – Xiaoguo Ge Dec 11 '19 at 05:34
4

You can try this:

int? sum = intList.TrueForAll(x => x == null) ? null : intList.Sum();
daniell89
  • 1,832
  • 16
  • 28
4

As fubo already wrote:

The reason for not returning null is the way it's implemented - the usage of int sum = 0; as result can never return null.

Why not write your own extension method, like this:

public static int? NullableSum( this IEnumerable<int?> source)
{
    int? sum = null;
    foreach (int? v in source)
    {
        if (v != null)
        {
            if (sum == null)
            {
                sum = 0;
            }

            sum += v.GetValueOrDefault();
        }
    }

    return sum;
}
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
Martin Backasch
  • 1,829
  • 3
  • 20
  • 30
1

It simply matches the type of your input type. If you change your list to List it will return int not int?, since your list is int? that is what it returns...

Milney
  • 6,253
  • 2
  • 19
  • 33
1

You can also try this if your linq sum to return null:

int? sum = intList.AsQueryable().Any(a => a.HasValue) ? intList.AsQueryable().Sum(a => a.Value) : (int?)null;
Willy David Jr
  • 8,604
  • 6
  • 46
  • 57