Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

What is the relation between the IEnumerable produced by LINQ and the original list?

I have the following lines of code:

var list = new List<Test>() { new Test("Test1"), new Test("Test2") };
var enumerable = list.Where(t => t.Content == "Test1");

Console.WriteLine($"Enumerable count: {enumerable.Count()}");
Console.WriteLine($"List count: {list.Count}");

list.RemoveAll(t => t.Content == "Test1");

Console.WriteLine($"Enumerable count: {enumerable.Count()}");
Console.WriteLine($"List count: {list.Count}");

I would expect the output to be

Enumerable count: 1
List count: 2
Enumerable count: 1
List count: 1

But in fact, the output is

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Enumerable count: 1
List count: 2
Enumerable count: 0
List count: 1

Meaning removing the object from the list, also removes it from the IEnumerable. I thought I have a fairly firm grasp on object oriented programming, but this behaviour seems very unexpected to me.

Could anyone explain what’s going on behind the scenes? I’ll add that if I add .ToList() to the original Where-statement, it all works as I would have expected.

EDIT: The question was closed with a reference to a question about the difference between a list and IEnumerable. That is not at all relevant to what is going on in this question. The issue here was that the IEnumerable was a reference to the LINQ query itself, and not its own collection.

>Solution :

LINQ is lazy and the actual execution is deferred until the moment one of the materializable operations is invoked (Count, foreach, ToList, First, etc.). And the whole enumerable will be enumerated for every such operation. This is very easily observed with side-effect:

var enumerable = list
    .Where(t =>
    {
        Console.WriteLine("Test deffered: " + t.Content);
        return t.Content == "Test1";
    });

So in you case you will perform the enumeration twice processing the whole list (for every enumerable.Count()) but between the enumerations the list has changed so you see the effect.

This laziness is actually quite useful in many cases – for example when building queries (for the database via Entity Framework) in dynamic fashion:

var query = context.Something.AsQueriable();

if(filter.Name is not null)
{
   query = query.Where(s => s.Name == filter.Name);
}
...

Or reusing the query to fetch different results:

var query = ...;
var total = await query.CountAsync();

var page = await query.Skip(page*size).Take(size).ToListAsync();
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading