Delegates & Events
Lesson 13 • Expert Track
Build event-driven programs with delegates, Action, Func, and the publisher-subscriber pattern.
What You'll Learn
- • Declare and use custom delegates
- • Use built-in
Action,Func, andPredicatedelegates - • Create events with
EventHandler<T> - • Implement the publisher-subscriber (pub/sub) pattern
- • Pass functions as parameters for flexible, decoupled code
Real-World Analogy
A delegate is like a job posting — it describes the skills needed (parameters and return type), and any qualified method can fill the role. An event is like a newspaper subscription — the publisher sends notifications, and subscribers react however they want.
Running C# Locally: Install the .NET SDK or use dotnetfiddle.net.
Custom Delegates
A delegate is a type that represents a reference to a method with a specific signature. You can assign any matching method to a delegate and invoke it like a function.
Delegates — Type-Safe Function Pointers
Create a delegate and pass different methods as parameters.
using System;
// A delegate is a type-safe function pointer
delegate int MathOperation(int a, int b);
class Program
{
static int Add(int a, int b) => a + b;
static int Multiply(int a, int b) => a * b;
static int Subtract(int a, int b) => a - b;
static void Main()
{
// Assign a method to a delegate
MathOperation op = Add;
Console.WriteLine($"Add(3, 5) = {op(3, 5)}");
// Re-assign to a different method
op = Multiply;
Console.W
...Action, Func & Predicate
C# provides built-in generic delegates so you rarely need custom ones. Action<T> is for void methods, Func<T, TResult> returns a value, and Predicate<T> returns bool.
Action, Func & Predicate
Use built-in delegates with lambdas for filtering and transforming.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Action<T> — delegate that returns void
Action<string> greet = name => Console.WriteLine($"Hello, {name}! 👋");
greet("Alice");
greet("Bob");
// Func<T, TResult> — delegate that returns a value
Func<int, int, int> add = (a, b) => a + b;
Func<int, int, int> max = (a, b) => a > b ? a : b;
Console.WriteLine($"\nadd(3, 5) = {add(3, 5)}");
...Common Mistakes
- • Invoking null events: Always use
MyEvent?.Invoke()with null-conditional. If no subscribers, the event is null. - • Using events vs delegates: Events add access restrictions — only the declaring class can raise them. Use events for pub/sub, delegates for callbacks.
- • Memory leaks: Always unsubscribe (
-=) from events when an object is done, especially in long-lived applications.
Events — Publisher/Subscriber Pattern
Events let a class notify other classes when something happens. The publisher raises the event, and any number of subscribers react — without the publisher knowing anything about them.
Events — Order Processing System
Build an event-driven order system with multiple subscribers.
using System;
// Custom EventArgs for extra data
class OrderEventArgs : EventArgs
{
public string OrderId { get; }
public decimal Total { get; }
public OrderEventArgs(string orderId, decimal total)
{
OrderId = orderId;
Total = total;
}
}
class OrderProcessor
{
// Declare an event using EventHandler<T>
public event EventHandler<OrderEventArgs> OrderPlaced;
public event EventHandler<OrderEventArgs> OrderShipped;
public void PlaceOrder(string
...Pro Tips
- 💡 Prefer
ActionandFuncover custom delegates — less boilerplate. - 💡 Use
EventHandler<TEventArgs>for events instead of raw delegates. - 💡 Lambda expressions (
=>) are the most concise way to create delegate instances. - 💡 Events enforce encapsulation — only the owner class can invoke them.
Lesson Complete! 🎉
You now understand delegates and events — the foundation of event-driven C#. Next: reading and writing files with System.IO.
Sign up for free to track which lessons you've completed and get learning reminders.