C# & .NET (C# 14, .NET 10 LTS) Module 12
Delegates, lambdas, events and functional style interview questions
Interview questions on delegates, lambdas, events and functional style: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What is a delegate in C#, and how is it different from an interface with one method?
-
Fresher
What is a closure in C#?
-
Fresher
What is the difference between a public delegate field and an event?
Answered in the lesson Events, subscriptions and memory leaks
-
Fresher
What is a pure function, and why is it easier to test?
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
An order service combines three checks into one Func<Order, bool> with +=: stock, address and credit. Orders with a bad address still ship, and nothing logs an error. Explain what the combined delegate returns, why the bad orders pass, and how you would redesign the checks.
- What it tests
- What a multicast delegate returns, why that hides failed checks, what an exception in one target does, and a design that runs every check and reports every failure.
- First step
- Write down what calling the combined delegate does: each check runs in order with the same order, and only one bool comes back. Ask which check that is, and what happens to the results of the other two.
- Follow-ups
-
- How would you report every failed check, not only one?
- One check throws when an order has no pin code. What changes?
- Would an event be a better fit for the checks?
From the lesson Delegates, Func and Action
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A pricing loop runs 2 million times a minute and passes this.ApplyDiscount to a helper on every pass. A memory profile shows millions of short-lived Func<decimal, decimal> objects. Where do they come from, and how do you remove them without making the code harder to read?
- What it tests
- Method group conversions and when the compiler caches them, lambdas that capture this, measuring allocations, and choosing a fix that stays readable.
- First step
- Name the conversion: every pass turns the instance method group this.ApplyDiscount into a new delegate that holds the method and this. Measure the bytes with GC.GetAllocatedBytesForCurrentThread around the loop before you change anything.
- Follow-ups
-
- Why does a static method group cost nothing here when an instance one does?
- Does passing x => ApplyDiscount(x) instead fix it?
- How would you keep the allocation from coming back?
From the lesson Delegates, Func and Action
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A dashboard keeps a small Func<string> title callback for every report it has shown. Memory grows by about 8 MB per report, although the callbacks only return titles, and a memory snapshot shows the report arrays reachable from the callbacks. Explain how, and fix it.
- What it tests
- How closures keep captured variables alive, what a kept delegate holds on to, and fixes that capture only what is needed.
- First step
- Open the method that made the callback and list every variable that any lambda in it captures, including lambdas that ran once and were thrown away. Then ask which object the kept callback refers to, and what that object holds.
- Follow-ups
-
- Why does the report survive a full garbage collection?
- Would marking the title lambda static help?
- How would you prove that the fix works?
From the lesson Lambdas and closures
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A team proposes a rule: every lambda on a hot path must be static. What does static prevent and what does it not, how do you pass a static lambda the data it needs, and would you adopt the rule?
- What it tests
- What a capture costs in allocations, what a static lambda guarantees, APIs that take a state argument, and when a coding rule like this pays for itself.
- First step
- Start from what a capture makes at run time, and when, and measure it with GC.GetAllocatedBytesForCurrentThread before arguing either way. Then say what static promises, and what it leaves out.
- Follow-ups
-
- Show how a static lambda gets a value it needs from its caller.
- Does static make the lambda's own code run faster?
- Where would the rule get in the way?
From the lesson Lambdas and closures
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A payment service raises PaymentCompleted with five subscribers. An analytics handler starts throwing during an outage, and from then on the audit handler, subscribed after it, records no payments, although the payments succeed. Explain why, and redesign the raise without losing the analytics errors.
- What it tests
- Multicast invocation order, what an exception in one handler does to the others, isolating handlers with GetInvocationList, and deciding which handlers are independent.
- First step
- Write down the invocation list in order of subscription and mark where analytics sits. Raising the event calls the handlers one after another, and an exception that a handler does not catch leaves the raise at once.
- Follow-ups
-
- Should the publisher catch the exceptions, or each handler?
- The audit must never miss a payment. Is an event the right tool for it?
- What changes if one handler is async?
From the lesson Events, subscriptions and memory leaks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A background thread raises StockChanged, and a screen removes its handler in Dispose on the UI thread. Now and then the screen's handler runs after Dispose and throws ObjectDisposedException. The raise uses ?.Invoke. Explain why that does not prevent it, and fix it.
- What it tests
- What ?.Invoke guarantees and what it does not, delegate snapshots during a raise, the race between a raise and an unsubscribe, and handlers that tolerate a late call.
- First step
- Separate two promises: what ?.Invoke guarantees about null, and what it does not guarantee about a handler that is removed while a raise is under way. Then line up the two threads step by step.
- Follow-ups
-
- Would a lock around the raise and the unsubscribe fix it?
- How should the handler itself defend against a late call?
- How would you reproduce the race in a test?
From the lesson Events, subscriptions and memory leaks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A market-data service receives 20,000 price ticks a second and must feed a chart, a risk engine that sometimes pauses for half a second, and an archive that needs to know when the feed ends. Would you expose an event, IObservable<T>, IAsyncEnumerable<T> or a channel, and why?
- What it tests
- Matching push and pull delivery to consumers, back-pressure, end and error signals, and the cost of a slow consumer inside an event raise.
- First step
- Work out what a slow consumer does to each design: in an event, its handler runs inside the raise, so a half-second pause holds up 10,000 ticks for everyone. Then list which consumers need an end signal.
- Follow-ups
-
- What happens to the risk engine's backlog in a bounded channel when it falls behind?
- How does the archive learn that the feed ended or failed?
- Would you give every consumer the same mechanism?
From the lesson Events, subscriptions and memory leaks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
The cart page shows Rs 679 for an order and the invoice, priced a moment later, shows Rs 656.10. Both call ApplyOffers(cart) on the same cart object. How do you work out why, and how do you redesign the pricing so that it cannot happen again?
- What it tests
- Recognising side effects, shared mutable state and hidden inputs, proving them with a test, and a pure redesign with immutable data and explicit inputs.
- First step
- Call ApplyOffers twice on one cart in a test and compare. If the totals differ, list everything the method reads and writes besides its result: the cart's lines, static fields, the clock, configuration.
- Follow-ups
-
- What in the numbers points to a second 10 % discount?
- How do you make the first-order discount pure?
- How do you keep the cart page and the invoice from drifting apart again?
From the lesson Functional techniques in C#
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A sign-up API throws ValidationException for every bad field. During a wave of bot sign-ups it handles 5,000 invalid requests a second and its CPU use and garbage collections climb. Would you switch to a Result type, and what changes for the callers and for the cost?
- What it tests
- The cost of exceptions for expected failures, measured; Result types and error collection; where exceptions stay right; migrating callers safely.
- First step
- Separate expected failures, such as a bad age, from unexpected ones, such as a lost database. Then measure what one expected failure costs as an exception against a returned value, in heap bytes per failure.
- Follow-ups
-
- What does a user gain when every error is reported at once?
- Which failures would you still throw?
- How do you migrate callers without a big-bang change?
From the lesson Functional techniques in C#
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Senior to staff
A 3,000-line OrderService reads the clock, the database and configuration inside its pricing and shipping rules, and has almost no tests. How would you move it toward a functional core with an imperative shell, step by step, without stopping feature work?
- What it tests
- Finding side effects and hidden inputs, extracting pure functions safely, characterisation tests, and deciding how far to go before the cost outweighs the gain.
- First step
- Pick one rule that changes often, for example the shipping cut-off, and list what it reads. Write a test that pins today's behaviour, then move the rule into a pure method that takes those values as arguments.
- Follow-ups
-
- How do you test the rule before you have changed any code?
- What do you do with the clock?
- When do you stop?
From the lesson Functional techniques in C#
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
All C# & .NET (C# 14, .NET 10 LTS) interview questions · The C# & .NET (C# 14, .NET 10 LTS) track