-3

Can anyone explain this behavior?

this code work:

Dictionary<string, int> fullPathTabAssociation = new Dictionary<string, int>();

    //bla bla.. 
    //here fullPathTabAssociation is populated
    ////bla bla.. 

var newValues = fullPathTabAssociation
  .Where(x => x.Value > index)
  .Select(x => new KeyValuePair<string, int>(x.Key, x.Value - 1))
  .ToList();

fullPathTabAssociation.Clear();

/*now newValues is populated with correct values*/

this code doesn't work

Dictionary<string, int> fullPathTabAssociation = new Dictionary<string, int>();

    //bla bla.. 
    //here fullPathTabAssociation is populated
    ////bla bla.. 

var newValues = fullPathTabAssociation
  .Where(x => x.Value > index)
  .Select(x => new KeyValuePair<string, int>(x.Key, x.Value - 1))

fullPathTabAssociation.Clear();

 /*now newValues is empty*/

The select function seems return a new IEnumerable, with the debug before fullPathTabAssociation.Clear() in both cases the values is correct for newValues and are different from fullPathTabAssociation. Specially I do not understand what happens in the last case

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
Jujjizzu
  • 41
  • 1
  • 5
  • 3
    *Linq* is **lazy** it does its work on *materialization* (`.ToList()`) only. – Dmitry Bychenko Jan 31 '19 at 13:45
  • 2
    See https://stackoverflow.com/questions/7324033/what-are-the-benefits-of-a-deferred-execution-in-linq – MakePeaceGreatAgain Jan 31 '19 at 13:45
  • 1
    A (deferred executed) LINQ query is just a query. It describes what has to be done to retrieve what you want. It is not yet executed. If you append `ToList` you _materialize_ this query and fill the result into a list. Your query's source is the dictionary, if you clear it the query will return nothing. If you first fill the result into a list, this will not be cleared if you clear the dictionary because the list is not coupled to the dictionary. – Tim Schmelter Jan 31 '19 at 13:48
  • thank you, deferred behavior was what I was looking for – Jujjizzu Jan 31 '19 at 13:55

2 Answers2

3

Linq is lazy; it postpones its work as far as it can (up to foreach or some kind of materialization).

In the first excerpt you materialize the query with .ToList() here Linq has to execute and provide List<T> collection:

var newValues = fullPathTabAssociation
  .Where(x => x.Value > index)
  .Select(x => new KeyValuePair<string, int>(x.Key, x.Value - 1))
  .ToList(); // <- Materizataion an actual List<T> required; Linq executes 

// Since we have newValues collection (not query) it doesn't depend on аullPathTabAssociation
fullPathTabAssociation.Clear();

In the second excerpt Linq doesn't have to do anything:

// Just a query, no materialization
var newValues = fullPathTabAssociation
  .Where(x => x.Value > index)
  .Select(x => new KeyValuePair<string, int>(x.Key, x.Value - 1));

fullPathTabAssociation.Clear();

...

// Only here (on foreach or materialization) Linq has to execute the query; 
// and it takes fullPathTabAssociation (which is empty) 
foreach (var item in newValues) {
  ...
}
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
2

The difference is that by using ToList you are executing the Linq query and creating new list. The contents of this newly created list are independent of fullPathTabAssociation.

In the second sample, you only store the query in newValues. The query is not executed yet and will only be executed after you cleared the source of the query. Thus, the result of the query is empty.

Markus
  • 20,838
  • 4
  • 31
  • 55