Delegates and lambdas
Treat behavior as data — store functions in variables and pass them to other methods.
12 min read
You will learn
- Explain what a delegate is
- Use Func, Action and Predicate
- Write lambda expressions
- Write methods that take functions as parameters
Functions as values
So far, variables have held data: numbers, strings, objects. C# also lets a variable hold a function. That one idea unlocks sorting by any rule, filtering with any condition, event handlers, LINQ, ASP.NET Core's endpoint mapping and much more.
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25 — call it like a methodDelegates
A delegate is a type that describes a method signature — its parameters and return type. A variable of a delegate type can hold any method (or lambda) with that signature.
You rarely declare your own delegate types, because .NET provides generic ones for almost every shape:
| Delegate | Shape | Example |
|---|---|---|
Func<TResult> | no parameters → returns a value | Func<DateTime> now = () => DateTime.Now; |
Func<T, TResult> | one parameter → returns a value | Func<string, int> len = s => s.Length; |
Func<T1, T2, TResult> | two parameters → returns a value | Func<int, int, int> add = (a, b) => a + b; |
Action | no parameters, returns nothing | Action beep = () => Console.Beep(); |
Action<T> | one parameter, returns nothing | Action<string> log = msg => Console.WriteLine(msg); |
Predicate<T> | one parameter → bool | Predicate<int> isEven = n => n % 2 == 0; |
The last type argument of Func is the return type.
Lambda expressions
A lambda is an anonymous, inline function: parameters => body.
x => x * 2 // one parameter, expression body
(a, b) => a + b // several parameters
() => Console.WriteLine("hi") // no parameters
s => // statement body for more logic
{
var trimmed = s.Trim();
return trimmed.ToUpper();
}
(int x, int y) => x * y // explicit parameter types (usually inferred)Methods with a matching signature work too:
bool IsLong(string s) => s.Length > 5;
Func<string, bool> check = IsLong; // a method group — no parentheses!Passing functions to methods
This is where delegates shine. Write the general algorithm once, and let the caller supply the specific behavior:
List<T> Where<T>(List<T> items, Func<T, bool> predicate)
{
var result = new List<T>();
foreach (var item in items)
if (predicate(item)) result.Add(item);
return result;
}
var numbers = new List<int> { 1, 5, 12, 7, 20 };
var big = Where(numbers, n => n > 6); // 12, 7, 20
var even = Where(numbers, n => n % 2 == 0); // 12, 20One method, endless conditions. You just wrote a tiny version of LINQ's Where.
You've already used this pattern without knowing it:
list.RemoveAll(x => x < 0);
list.Sort((a, b) => a.Name.CompareTo(b.Name));
Array.Find(people, p => p.Age > 30);Returning functions
A method can also create and return a function:
Func<int, int> Adder(int amount) => x => x + amount;
var addTen = Adder(10);
Console.WriteLine(addTen(5)); // 15addTen remembers amount = 10 even after Adder has returned. That's a closure — the topic of the next lesson.
Under the hood
A delegate is an object: it stores a reference to the target method (and, for instance methods, the target object). That's why you can put them in variables, lists and dictionaries — e.g. a Dictionary<string, Func<double, double, double>> mapping "+" to an addition lambda.
Try it
C# runtime not loaded
Key takeaways
- Delegates let variables and parameters hold functions.
- Use
Func<..., TResult>for functions returning a value,Action<...>for void ones. - Lambdas (
x => x * 2) are concise inline functions; method names work as delegates too. - Passing functions as parameters lets you write general algorithms with pluggable behavior.
Exercise
Transform applies a function to every element and returns the results. Filter keeps only the items for which keep returns true (it's generic, so it works with any type). Compose returns a new function that applies f first, then g.
C# runtime not loaded
Hint 1
Transform: create a result array of the same length and set result[i] = transform(values[i]). A Func is called just like a method.
Hint 2
Filter: create a new List<T> and Add each item where keep(item) is true.
Hint 3
Compose returns a lambda: return x => g(f(x));
Show solution
var tens = Transform(new[] { 1, 2, 3 }, x => x * 10);
Console.WriteLine(string.Join(", ", tens));
int[] Transform(int[] values, Func<int, int> transform)
{
var result = new int[values.Length];
for (int i = 0; i < values.Length; i++)
{
result[i] = transform(values[i]);
}
return result;
}
List<T> Filter<T>(List<T> items, Func<T, bool> keep)
{
var result = new List<T>();
foreach (var item in items)
{
if (keep(item)) result.Add(item);
}
return result;
}
Func<int, int> Compose(Func<int, int> f, Func<int, int> g) => x => g(f(x));Quiz
4 questions · 3 correct to pass1.What does `Func<int, string>` represent?
2.Which delegate type fits a method that takes a string and returns nothing?
3.What is `x => x * x`?
4.Given `Func<int, int> twice = n => n * 2;`, what is `twice(twice(3))`?
To complete this lesson
- ○ Solve the exercise
- ○ Pass the quiz (3/4 correct)