-1

I have the following code:

List<int> no = new List<int>() { 1, 2, 3, 4, 5 };
var res2 = no.Sum(a => a * a);
Console.WriteLine(res2);
no.Add(100);
Console.WriteLine(res2);

I expect the following result:

55
10055

but both are 55

55
55

I have seen here which is about deferred evaluation but was not helpful. Sum is an extension method, but the result is not what I have mentioned. Why?

Green Falcon
  • 818
  • 3
  • 17
  • 48

4 Answers4

8

Only functions that return an IEnumerable<T> can be deferred in Linq (since they can be wrapped in an object that allows deferring).

The result of Sum is an int, so it can't possibly defer it in any meaningful way:

var res2 = no.Sum(a => a * a);
// res2 is now an integer with a value of 55
Console.WriteLine(res2);
no.Add(100);

// how are you expecting an integer to change its value here?
Console.WriteLine(res2);

You can defer the execution (not really defer, but explicitly call it), by assigning the lambda to, for example, a Func<T>:

List<int> no = new List<int>() { 1, 2, 3, 4, 5 };
Func<int> res2 = () => no.Sum(a => a * a);
Console.WriteLine(res2());
no.Add(100);
Console.WriteLine(res2());

This should correctly give 55 and 10055

Jcl
  • 27,696
  • 5
  • 61
  • 92
1

Generally you may assume that as long as a Linq function returns a IEnumerable or IQueryable of something, then the execution is probably deferred.

When the returned value is one item of type TSource or an object that implements ICollection < TSource > you can be assured that the execution is not deferred (anyone knows for any exceptions?)

To be absolutely certain: the MSDN descriptions of the Enumerable functions describe whether the function is implemented by using deferred execution.

For example Enumerable.Select:

This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated...

The function Enumerable.Max is not implemented using deferred execution. So if the sequence changes after you calculated the Max, the result of Max is not changed.

See also Stackoverflow: when is a Linq function deferred?

Community
  • 1
  • 1
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
1

A few LINQ methods (like Where and Select) are deferred because computing one result is independent from computing the next result. But not all methods that operate on an IEnumerable<T> are necessarily deferred.

For example, Sum will reduce all elements of the sequence into one. As such, it can compute nothing at all or everything, but there's no way to do anything in-between. Its authors chose to break their usual LINQ habit and they made it compute eagerly rather than lazily.

This is proven by the fact that Sum on an IEnumerable<int> has a return type of int, which is an already calculated integer:

int res2 = no.Sum(a => a * a);

If you want to defer the calculation of Sum, there's a simple way -- use a Func<int>:

Func<int> res2 = () => no.Sum(a => a * a);

Alternatively, you can make it a LINQ-like extension method:

public static Func<int> LazySum(this IEnumerable<int> sequence, Func<int, int> selector)
        => () => sequence.Sum(selector);

And then use it like this:

var res2 = no.LazySum(a => a * a);

Regardless of which one you choose, you can verify that it will give you deferred computation:

Console.WriteLine(res2()); // prints 55
no.Add(100);
Console.WriteLine(res2()); // prints 10055
Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104
1

Functions that return an IEnumerable can be deferred in Linq because they can be wrapped in an object that allows deferring.