Reputation: 7414
I noticed an issue with enumerating over a collection that was a result of a .Select<T>()
I can enumerate over it, and modify each item in the collection. When I look at the collection after the enumeration is completed each item is left un-modified.
public class FooModel
{
public Guid? Id { get; set; }
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IEnumerable<FooModel> foos = Enumerable.Range(0, 100)
.Select(m => new FooModel());
foreach(FooModel f in foos)
{
f.Id = Guid.NewGuid();
}
Assert.IsTrue(foos.All(foo => foo.Id.HasValue));
}
}
If I append .ToList()
or .ToArray()
to execute the enumerable, then it modifies the items within the collection.
I understand that the IEnumerable
isn't executed after the .Select<T>
, but when the foreach
runs it creates an enumerator and executes the IEnumerable on the foo local field. Why isn't it referencing the same object that the Select<T>
creates?
I've seen questions on when to use IEnumerable
over List
but I understand the difference between the two. Describing vs implementing more or less. My question is more in regards to why the foreach over a local field, executes the IEnumerable and doesn't operate on the same objects referenced in the local variable.
Upvotes: 1
Views: 56
Reputation: 152644
Why isn't it referencing the same object that the Select creates?
Because foo
is using Enumerable.Range
which enumerates on-the-fly. There is no underlying data store that you are iterating over.
When you call foreach
you are executing the underlying query, which creates 100 FooModel
objects on the fly, which you modify.
When you call .All
, you execute the query again, which creates another 100 Foo
objects on the fly that have not been modified.
When you hydrate the results via ToList
or ToArray
, you are then looping over a concrete collection, and your changes to the underlying objects will persist.
Upvotes: 2