S7.0 What This Teaches
- Delegates: type-safe function pointers
- Built-in delegate types:
Action,Func,Predicate - Lambda expressions:
(x) => expression - Closures: lambdas capturing outer variables
- Passing delegates as callbacks and higher-order functions
- Multicast delegates
S7.1 Delegate Basics
// Declare a delegate type
delegate int MathOp(int a, int b);
// Assign a method
MathOp add = (a, b) => a + b;
MathOp mul = (a, b) => a * b;
Console.WriteLine(add(3, 4)); // 7
Console.WriteLine(mul(3, 4)); // 12
// Store and pass as argument
static int Apply(int x, int y, MathOp op) => op(x, y);
Console.WriteLine(Apply(5, 6, add)); // 11
S7.2 Action and Func
// Action<T> - takes arguments, returns void
Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
greet("Alice");
// Func<T, TResult> - takes arguments, returns TResult
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 4)); // 7
// Predicate<T> - returns bool (shorthand for Func<T, bool>)
Predicate<int> isEven = n => n % 2 == 0;
Console.WriteLine(isEven(4)); // True
// Higher-order function using Func
static List<T> Filter<T>(List<T> items, Func<T, bool> predicate) =>
items.Where(predicate).ToList();
S7.3 Lambda Expressions
// Expression lambda - single expression, implicit return
Func<int, int> square = x => x * x;
// Statement lambda - multiple statements in braces
Func<int, int> safeSqrt = x =>
{
if (x < 0) return 0;
return (int)Math.Sqrt(x);
};
// No parameters
Action printTime = () => Console.WriteLine(DateTime.Now);
// Multiple parameters
Func<int, int, int> clamp = (value, max) => Math.Min(value, max);
S7.4 Closures
int multiplier = 3;
Func<int, int> triple = x => x * multiplier;
Console.WriteLine(triple(5)); // 15
multiplier = 10;
Console.WriteLine(triple(5)); // 50 - sees the changed value!
// Factory function returning a closure
static Func<int, int> MakeAdder(int delta) => x => x + delta;
var addFive = MakeAdder(5);
var addTen = MakeAdder(10);
Console.WriteLine(addFive(3)); // 8
Console.WriteLine(addTen(3)); // 13
S7.5 Multicast Delegates
+=
adds a method to the invocation list:
Action<string> log = msg => Console.WriteLine($"[Log] {msg}");
Action<string> alert = msg => Console.WriteLine($"[ALERT] {msg}");
Action<string> notify = log;
notify += alert; // both will be called
notify("Server started");
// [Log] Server started
// [ALERT] Server started
notify -= log; // remove from invocation list
S7.6 Example - All Together
// Delegates - pipeline of transformation steps with Func composition.
var pipeline = Compose<string, string>(
s => s.Trim(),
s => s.ToLower(),
s => s.Replace(" ", "_")
);
string[] inputs = { " Hello World ", " Foo Bar ", " C Sharp " };
foreach (string s in inputs)
Console.WriteLine(pipeline(s));
static Func<T, T> Compose<T, T2>(params Func<T, T>[] steps)
where T2 : T
{
return input => steps.Aggregate(input, (acc, f) => f(acc));
}
hello_world
foo_bar
c_sharp
S7.7 Exercise
Exercise
- Write a
Memoize<T, TResult>(Func<T, TResult> f)method that caches previously computed results in a dictionary and returns a newFunc<T, TResult>. Test it with a slow function. - Use an
Action<string>multicast delegate to create a simple logging system with Console and file handlers. - Write a higher-order function
Retry(Action action, int times)that calls action up to times times, catching exceptions on each attempt.
S7.8 Common Mistakes
Captured loop variable
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
actions.Add(() => Console.WriteLine(i)); // captures i, not a copy
actions.ForEach(a => a()); // prints 3 3 3 - all see i=3!
// Fix: capture a local copy
for (int i = 0; i < 3; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy)); // prints 0 1 2
}
Null delegate invocation
Action<string> handler = null;
handler("test"); // NullReferenceException
handler?.Invoke("test"); // safe - no-ops if null
S7.9 Key Terms
| Term | Meaning |
|---|---|
| delegate | Type representing a method reference with a specific signature |
| Action<T> | Built-in delegate that takes parameters and returns void |
| Func<T, TResult> | Built-in delegate that takes parameters and returns TResult |
| Predicate<T> | Shorthand for Func<T, bool> |
| lambda expression | (params) => body - anonymous method assigned to a delegate |
| closure | Lambda that captures variables from its enclosing scope |
| multicast delegate | Delegate holding multiple method references; all called on invoke |
| higher-order function | Function that takes or returns another function |