Delegates, Action, Func, Predicate & Events Masterclass
Welcome to Phase 7 (Chapter 21): C# Delegates, Built-in Delegates (Func, Action, Predicate) & Events Masterclass! A delegate is a type-safe function pointer reference to a method with a specific signature. In this chapter, we master custom delegates, built-in delegates (Action, Func, Predicate), anonymous methods, event publisher-subscriber design patterns, custom event arguments, and event unsubscribing.
A delegate allows passing methods as parameters to other methods. .NET provides three built-in generic delegates to avoid declaring custom delegate types:
| Delegate | Return Type | Usage Description | Example Signature |
|---|---|---|---|
Action<T1, T2> | void (No return value) | Encapsulates a method that returns no result. | Action<string> log = msg => Console.WriteLine(msg); |
Func<T1, T2, TResult> | TResult (Last parameter) | Encapsulates a method that returns a result value. | Func<int, int, int> add = (a, b) => a + b; |
Predicate<T> | bool | Encapsulates a method that tests a condition. | Predicate<int> isEven = n => n % 2 == 0; |
// 1. Func โ Takes two ints, returns int
Func<int, int, int> add = (first, second) => first + second;
Console.WriteLine($"Func Add: {add(10, 20)}"); // 30
// 2. Action โ Takes string, returns void
Action<string> printMessage = msg => Console.WriteLine($"LOG: {msg}");
printMessage("Delegates in C# are powerful!");
// 3. Predicate โ Takes int, returns bool
Predicate<int> isPositive = num => num > 0;
Console.WriteLine($"Is 15 positive: {isPositive(15)}");
Events provide a notification system where a Publisher class triggers an event, and one or more Subscriber classes receive and handle that event asynchronously.
public class ProcessPublisher
{
// Declare Event using EventHandler delegate
public event EventHandler? ProcessCompleted;
public void StartProcess()
{
Console.WriteLine("Process started...");
System.Threading.Thread.Sleep(500); // Simulate work
OnProcessCompleted();
}
protected virtual void OnProcessCompleted()
{
ProcessCompleted?.Invoke(this, EventArgs.Empty); // Safe event invocation
}
}
// Subscriber Code
ProcessPublisher publisher = new();
publisher.ProcessCompleted += (sender, e) => Console.WriteLine("Subscriber received: Process Finished!");
publisher.StartProcess();
Q1: Why should I unsubscribe from events (-=)?
If a subscriber does not unsubscribe from a long-lived publisher event, the publisher holds a reference pointer to the subscriber, preventing the Garbage Collector from freeing subscriber memory (causing memory leaks).