100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

Deferred vs Immediate Execution

LINQ queries either execute immediately when defined or defer execution until enumerated — understanding which operators do which prevents subtle bugs and needless repeated work.

FoundationsIntermediate9 min readJul 10, 2026
Analogies

Deferred vs Immediate Execution

Most LINQ operators — Where, Select, OrderBy, GroupBy — use deferred execution: writing var query = list.Where(x => x.Runs > 50) does not filter anything yet; it just builds a query object describing what to do. The filtering only happens when something enumerates the query, such as a foreach loop. Other operators — ToList(), ToArray(), Count(), Sum(), First(), Max() — use immediate execution: they force the query to run right then and return a concrete result or collection.

🏏

Cricket analogy: Like a bowling change that a captain announces but only actually happens when the umpire calls 'play', a deferred LINQ query like var q = list.Where(x => x.Runs > 50) is only defined, not executed, until something enumerates it — such as a foreach loop.

Deferred Execution in Action

Because deferred operators like Where re-evaluate their predicate against the live source every time the query is enumerated, results can change between two enumerations of the same query variable if the underlying collection was modified in between. This is a common source of confusion: a developer assumes a query variable holds a fixed result set, when in fact it holds a re-runnable plan that reflects whatever the source collection looks like at the moment of enumeration.

🏏

Cricket analogy: Like re-checking the required run rate every single over because it recalculates from the current score each time, a deferred query such as var lowScores = scores.Where(s => s < 20) re-evaluates against the live list every time you foreach over it, picking up any changes made in between.

Immediate Execution in Action

Calling a materializing operator — ToList(), ToArray(), ToDictionary() — or a value-producing operator — Count(), Sum(), First(), Max() — forces the entire deferred query to run right away and captures the results as a fixed snapshot. From that point on, the resulting List<T> or scalar value is completely independent of the source collection; later changes to the source have no effect on it.

🏏

Cricket analogy: Like locking in the final scorecard the moment the umpires call stumps, calling .ToList() on a deferred query forces immediate execution and captures a fixed snapshot of results at that instant, unaffected by later changes to the source list.

Common Pitfalls

Two mistakes trip up developers repeatedly. First, enumerating the same deferred query multiple times — once in a foreach, once in a Count() call — re-executes the entire pipeline each time, wasting CPU on expensive filters or hitting a database twice. The fix is to materialize once with .ToList() and reuse that list. Second, modifying a collection (adding or removing items) while a deferred query over it is being enumerated throws an InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

🏏

Cricket analogy: Like re-running DRS ball-tracking analysis from scratch every single time a commentator references it instead of caching the result, forgetting to call .ToList() on an expensive deferred query and then enumerating it twice re-executes the whole filter both times.

csharp
var names = new List<string> { "Ana", "Ben", "Cara" };

var query = names.Where(n => n.Length > 3); // deferred, nothing runs yet
names.Add("Diego"); // mutates the source before enumeration

foreach (var n in query)
    Console.WriteLine(n); // prints "Diego" too — the query re-ran against the updated list

// Force immediate execution to snapshot results now
var snapshot = names.Where(n => n.Length > 3).ToList();
names.Add("Eshaan"); // no effect on 'snapshot'

Adding or removing items from a List<T> while a foreach loop is enumerating a deferred query over that same list throws InvalidOperationException: Collection was modified; enumeration operation may not execute. Materialize with .ToList() first if you need to modify the source during iteration.

  • Deferred execution (Where, Select, OrderBy, GroupBy) builds a query plan that runs only when enumerated.
  • Immediate execution (ToList(), ToArray(), Count(), Sum(), First(), Max()) runs the query right away and returns a fixed result.
  • Deferred queries re-evaluate against the live source every time they're enumerated, so results can change between enumerations.
  • Enumerating an expensive deferred query multiple times re-executes the whole pipeline each time — materialize once with ToList() to avoid this.
  • Modifying a source collection while enumerating a deferred query over it throws InvalidOperationException.
  • Once materialized (e.g., via ToList()), a result is a fixed snapshot independent of later changes to the source.

Practice what you learned

Was this page helpful?

Topics covered

#LINQStudyNotes#MicrosoftTechnologies#DeferredVsImmediateExecution#Deferred#Immediate#Execution#Action#StudyNotes#SkillVeris#ExamPrep