Delegates and lambdas
A delegate is a type whose values are methods, and a lambda is a method written in place with no name. Follow seven lines of C#: the lambda x => x > limit becomes a delegate object on the heap, LINQ's Count calls it once per element and prints 2, and after limit changes the same delegate answers 3, because a lambda captures variables, not copies of their values.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01Which methods fit the delegate type Func<int, bool>?
In Func<T, TResult> the last type argument is the result. Func<int, bool> means one int parameter and a bool result, such as x => x > limit or a method bool IsEven(int x).
02In the lambda x => x > limit, what is x > limit?
Left of the => operator are the parameters (x); right of it is the body. For an expression lambda the value of the expression is returned. The body runs each time the delegate is called, not when it is created.
03nums is [3, 8, 1, 9]. How many times does nums.Count(isBig) call isBig?
Count calls the predicate once for every element and counts how many calls return true. Four elements means four calls; two of them return true when limit is 5.
04After limit = 2, nums.Count(isBig) returns 3. Why does the same delegate give a new answer?
C# captures variables, not copies of their values. limit lives in a closure object that both the main code and the lambda use, so a call after line 6 compares with 2: 3, 8 and 9 are bigger.
05Which delegate type fits a method that takes a string and returns nothing (void)?
Action<T> is the built-in delegate type for methods that return nothing. Func always has a result type, and void cannot be a type argument.
Glossary
- Delegate type
- A type whose values are references to methods with a given shape (parameter types and return type), such as Func<int, bool>.
- Delegate object
- An object (a reference type, on the heap) that knows which method to call (Delegate.Method) and on which object (Delegate.Target).
- Func and Action
- Built-in generic delegate types. Func<T, TResult> returns a TResult; Action<T> returns nothing.
- Lambda expression
- A method written in place with no name, such as x => x > limit. It converts to a delegate type.
- => (lambda operator)
- Separates a lambda's parameters (left) from its body (right). Read it as "goes to".
- Captured variable
- A local variable from outside a lambda that the lambda uses. The lambda shares the variable itself, not a copy of its value.
- Closure
- The hidden object the compiler makes to hold captured variables, so they live as long as the delegate that uses them.
- Predicate
- A method that answers true or false about one value, like the Func<int, bool> that LINQ's Count takes.
Notes and sources
- The closure object is how the C# compiler implements captured variables today: it generates a hidden class with limit as a field, and line 1 writes that field. Its name and shape are compiler details; the language only promises that the lambda and main share one variable.
- Delegate.Target and Delegate.Method are real properties. For this capturing lambda, Target is the closure object; for a lambda that captures nothing, the compiler may reuse one cached delegate object instead of making a new one.
- Enumerable.Count with a predicate calls it once per element and returns how many were true. The figure walks the array front to back, as .NET does.
- Every object in this program lives on the managed heap: the delegate, the closure and the array too, because arrays are reference types (they derive from System.Array). The figure leaves out the local variables isBig and nums, which only hold references to those objects, and the int n.
- Func, Console and the LINQ method Count work without using lines because new console projects turn on implicit usings (System and System.Linq among them).
- The program is real C# 12 (the .NET 8 SDK or later). Put the seven lines in Program.cs and dotnet run prints 2, then 3.
- Microsoft Learn: Delegates (C# programming guide)
- Microsoft Learn: Delegates and lambdas (.NET)
- Microsoft Learn: Lambda expressions and anonymous functions
- Microsoft Learn: Func<T, TResult> delegate
- Microsoft Learn: Action<T> delegate
- Microsoft Learn: Delegate.Target property
- Microsoft Learn: Enumerable.Count method
- Microsoft Learn: Arrays (C# reference)
- Microsoft Learn: Collection expressions
- Microsoft Learn: How to combine delegates (multicast delegates)
- ECMA-334: C# Language Specification
Quiz Unlocks after the last step.