3

I encountered a (for me) strange behaviour. Here is the code:

var objects = from i in items
              //some calculations
              select something;

// other calculations

objects.ToList().ForEach(x => {
    //some calculations
    x.Property = "some text"
});

return objects;

First I generate a IEnumerable, it is a query to the db, I skipped the details. Then I have to do other calculations and at the end I iterate over my objects to set a further parameter. Running this code, once the IEnumerable objects is returned, their Property is not set.

Otherwise if I move the ToList() in the Linq expression as followed, the Property is set:

var objects = (from i in items
              //some calculations
              select something).ToList();

// other calculations

objects.ForEach(x => {
    //some calculations
    x.Property = "some text"
});

return objects;

As far I know, the objects are not copied, but referenced...right? What does behind the apparently codes happen? What is the difference?

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
Emaborsa
  • 2,360
  • 4
  • 28
  • 50
  • Possible duplicate of [ToList()-- Does it Create a New List?](http://stackoverflow.com/questions/2774099/tolist-does-it-create-a-new-list) – Nahuel Ianni Jun 23 '16 at 12:53
  • 2
    When you do `objects.ToList()` the query get executed. Then you return `objects`. Let's say that someone then iterate over that returned object: in this case the query will be executed again (so new objects will be created) – Matteo Umili Jun 23 '16 at 12:53
  • 5
    `var objects` without `ToList` is just a query, so the definition not the execution. If you materialize it, for example with `ToList` or `forach` it is executed. So by appending `ToList` the database gets queried and an in-memory list is filled. `List.ForEach` will enumerate them and you modify the property. In the first approach this list is thrown away immediately because it is not stored in a variable. The second version is correct. – Tim Schmelter Jun 23 '16 at 12:53
  • @TimSchmelter Could you please explain the "this list is thrown away immediately because it is not stored in a variable"? Even though it is not materialized on the variable definition, shouldn't it be once the ToList or ForEach gets executed, storing the result on the objects variable? – Nahuel Ianni Jun 23 '16 at 12:55
  • 4
    @NahuelIanni: no, the `objects` variable is still just the db-query. The list is not stored anywhere. Another, probably better, approach is to assign the text to the property in the query itself. Then you don't need to loop the list afterwards. – Tim Schmelter Jun 23 '16 at 12:57

2 Answers2

6

Remember that a Linq query is just a query - it is saying "if I ask for this data, here's what I want_. It does not hold any data but waits until you ask_ for the results (via foreach, ToList, ToArray, etc.)

The difference is you're not capturing the list returned by ToList in the first example. It's used by the ForEach call but the objects variable still holds a reference to the original query. When you ask for it again - it fetches the data again. When it fetches the data again, new objects are created.

So in that respect - yes ToList() creates a new list. It does not modify the object that it's called on to turn it into a list.

In your second example the objects variable holds a reference to the list generated from the query via ToList(), so when you modify them and return objects you're returning the same list.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • 2
    @Taemyr I think you're the one not understanding. What DStanley and I wrote in our answers is exactly what's happening. – Maximilian Riegler Jun 23 '16 at 12:58
  • @DStanley The first edit makes a significant improvement. Although I would prefer an answer that makes it explicit that the new items are created by the select. – Taemyr Jun 23 '16 at 13:31
0

In your first code sample the objects.ToList() is in fact creating a new object of type List you're working on with your ForEach. After the ForEach is done you're still just returning your IEnumerable and the list you actually wanted to return will be gone.

By assigning the LINQ query with a ToList() to the object variable, you're using the newly created list in your ForEach.

Maximilian Riegler
  • 22,720
  • 4
  • 62
  • 71
  • I think you miss the point. Its the changes to the objects in the list that he is missing. – Taemyr Jun 23 '16 at 12:54
  • 1
    Exactly, because he is returning the `IEnumerable` and not storing the resulting list of the `ToList()` call in his first sample. – Maximilian Riegler Jun 23 '16 at 12:56
  • 1
    @Taemyr The key to this is that the `IEnumerable` is likely a `IQueryable` which means the underlying data are not objects that can be changed, but data that will be queried from the DB on each iteration. – juharr Jun 23 '16 at 12:59
  • @juharr Agreed, or the select creates new objects from some other mechanism. But this answer makes no indication that the items are created during execution of the select. – Taemyr Jun 23 '16 at 13:16