LINQ, Delegates, and Interfaces
This cluster produces the single most frequently tested point in .NET interviews β deferred execution β plus two evergreen "compare these" questions. All three reward the clarify-then-answer habit.
The classic question: deferred execution
"This query runs, but the log shows the database was hit three times. Why?"
var flagged = transcripts.Where(t => t.NeedsReview);
if (flagged.Any()) log.Info($"{flagged.Count()} flagged");
return flagged.ToList();
The strong answer names the mechanism first: a LINQ query is a description, not a
result β it executes each time it's enumerated. Any(), Count(), and ToList() are three
enumerations, so against IQueryable that's three round trips (and against in-memory data,
three re-computations β worse if the source changed in between). Fix: materialize once
(ToList() early), then ask the list. Bonus depth: Count() > 0 on a query is itself a
smell β Any() can stop at the first row.
Common follow-up: "Is deferred execution bad, then?" No β it's what lets EF compose
Where clauses into one SQL statement before anything runs. The skill is knowing where
the boundary is: compose lazily, materialize deliberately, never enumerate twice by
accident.
IEnumerable vs IQueryable
Headline: both are lazy; the difference is where the work happens. IQueryable carries
an expression tree that a provider (EF Core) translates to SQL β filtering happens in the
database. IEnumerable runs compiled delegates in memory. The scenario version: "A search
page got slow after a refactor that changed a repository return type" β someone returned
IEnumerable, so Where now pulls the whole table into memory and filters there. Naming
that exact failure, and the verify step ("I'd check the generated SQL"), is a pass.
Delegates, events, and the interface question
"What's a delegate?" A type-safe reference to a method β Func<Transcript, bool> is what
every lambda you pass to LINQ actually is. Events are delegates with a safety contract:
outsiders can only subscribe/unsubscribe, not invoke or overwrite the list. One sentence of
depth: a subscriber that never unsubscribes keeps the publisher's reference alive β a classic
managed memory leak.
"Interface vs abstract class?" Answer it as a modeling choice, not a feature list: an interface is a capability contract ("can be reviewed"), an abstract class is a shared identity with shared implementation ("is a document, with common base behavior"). A type gets one base class but many interfaces; interfaces are what you mock in tests and inject via DI β which is why modern ASP.NET Core code is interface-heavy. Mentioning that interfaces can now carry default implementations, and that this narrowed but didn't erase the distinction, is the up-to-date flourish.
Practice prompts:
- Explain deferred execution using a query you actually wrote, including the exact line where execution happens.
- "When would you deliberately return
IEnumerablefrom a repository?" β defend it. - Design aloud: reviewers need pluggable transcript-flagging rules. Interface, delegate, or abstract class β and why?