LINQ Cost: Deferred Pipelines, Multiple Enumeration, Materialization
Every LINQ stage is a lazy view, not a result โ which is a feature until a pipeline is enumerated twice, recomputed twice, or built per-iteration inside a hot loop.
Deferred by default
Where, Select, Take... return a view over the source. No work happens
until something enumerates (foreach, ToList, Count, First...). Two
consequences matter for performance:
Multiple enumeration. Every enumeration re-runs the whole pipeline โ and re-runs the source:
var query = orders.Where(o => o.Total > 100); // nothing runs yet
if (query.Count() > 0) // enumerates: filter runs once
Render(query); // enumerates AGAIN: filter runs twice
If the pipeline is consumed more than once, materialize once:
var list = query.ToList();. If it is consumed exactly once, adding
ToList() only spends an allocation for nothing.
Enumeration is not free per-element either. A pipeline of three stages does three delegate calls per element. For most app code this is irrelevant; inside a hot loop over millions of items it can show up. The fix is not "ditch LINQ" โ it is "don't build pipelines in loops":
// pipeline rebuilt (and re-run) per iteration
foreach (var o in orders)
if (orders.Where(x => x.Id > o.Id).Any()) ... // O(n^2)
// compute the view once, then use it
var later = orders.Where(x => x.Id > threshold).ToList();
Materialization choices
ToList()/ToArray()โ snapshot; needed for repeated use or when the source mutates later.Count()on a pipeline โ enumerates the whole thing;Countproperty on aListis free. Knowing which you're touching matters.FirstvsFirstOrDefaultvsSingleโ same laziness, different expectations;Singlemay scan past the match to prove uniqueness.
The performance review lens
Reading someone's code for cost, the checklist that catches 90%:
- Any string
+=inside a loop? โStringBuilder. - Any
Contains/Removeon a list inside a loop? โHashSet/Dictionary. - Any LINQ pipeline enumerated more than once? โ materialize once.
- Any query built inside another loop? โ hoist and run once.
- And above all: was any of it measured before being called slow?