A modern city needs an automated response system — when a fire is detected, a callback dispatches the fire team; when a building is completed, subscribers are notified. In C#, this is powered by delegates, lambda expressions, and events. These are the building blocks of event-driven programming that make .NET so powerful for everything from desktop apps to microservices.
Delegates — Type-Safe Function Pointers
A delegate is a type that represents a reference to a method with a specific signature. Think of it as a contract: "any method matching this signature can be assigned here."
// Declare a delegate type
delegate void AlertHandler(string message);
// A method that matches the delegate signature
static void ShowAlert(string msg)
{
Console.WriteLine($"Alert: {msg}");
}
// Using it
AlertHandler handler = ShowAlert;
handler("Fire detected!"); // Output: Alert: Fire detected!
Lambda Expressions — Concise Inline Functions
Lambda expressions (introduced in C# 3.0) let you write anonymous methods inline using the => ("goes to") operator. They are the heart of LINQ and modern C#:
// Expression lambda (single expression, return is implicit)
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25
// Statement lambda (multiple statements, needs braces and explicit return)
Func<int, int, int> add = (a, b) =>
{
int result = a + b;
return result;
};
// Lambda with no parameters
Action greet = () => Console.WriteLine("Hello, Architect!");
Built-in Delegate Types: Action, Func, and Predicate
C# provides three generic delegate types so you rarely need to declare your own:
Action<T> — Takes parameters but returns void. Use for side effects (logging, printing).
Func<T, TResult> — Takes parameters and returns a value. The last type parameter is always the return type.
Predicate<T> — Takes one parameter and returns bool. Used for filtering/testing conditions.
// Action<T> — no return value
Action<string> dispatch = team => Console.WriteLine($"Action: Dispatching {team}");
dispatch("team");
// Func<T, TResult> — has a return value
Func<int, bool> isPositive = n => n > 0;
Console.WriteLine(isPositive(5)); // True
// Predicate<T> — specialized bool-returning delegate
Predicate<string> startsWithA = s => s.StartsWith("A");
List<string> names = new List<string> { "Alice", "Bob", "Anna" };
List<string> aNames = names.FindAll(startsWithA); // ["Alice", "Anna"]
Multicast Delegates
Delegates can chain multiple methods using +=. When invoked, all chained methods execute in order:
Action<string> emergency = msg => Console.WriteLine($"Log: {msg}");
emergency += msg => Console.WriteLine($"Alert: {msg}");
emergency += msg => Console.WriteLine($"SMS: {msg}");
emergency("Building on fire!");
// Log: Building on fire!
// Alert: Building on fire!
// SMS: Building on fire!
Events — Controlled Delegate Access
An event is a delegate with access restrictions: only the declaring class can invoke it, but any class can subscribe. This is the foundation of the Observer pattern in .NET:
class CityPlanner
{
// Declare the event using EventHandler
public event Action<string> BuildingCompleted;
public void CompleteBuilding(string name)
{
Console.WriteLine($"Building {name} is done!");
BuildingCompleted?.Invoke(name); // Safely invoke
}
}
// Subscribe to the event
var planner = new CityPlanner();
planner.BuildingCompleted += name => Console.WriteLine($"Event: {name} completed!");
planner.CompleteBuilding("Sky Tower");
// Building Sky Tower is done!
// Event: Sky Tower completed!
Time to build the city's automated response system using all of these concepts, Architect!
📋 Instructions
**Build the City's Automated Response System!**
1. Create a `delegate void AlertDelegate(string message)` (declare outside `Program` class or at class level)
2. Create a static method `ShowAlert(string message)` that prints `"Alert: {message}"`
3. In `Main`, assign `ShowAlert` to an `AlertDelegate` variable and call it with `"Fire detected!"`
4. Create an `Action` called `dispatch` that prints `"Action: Dispatching {value}"`; call it with `"team"`
5. Create a `Predicate` called `needsInspection` that returns `true` if a value is greater than 20
6. Create a `List` with values `{10, 25, 30, 15, 40}` and use `FindAll(needsInspection)` — print `"Filter: {count} buildings need inspection"` where `count` is the result count
7. Create an `Action` called `onComplete`, assign it a lambda that prints `"Event: {value}"` — call it with `"Building completed!"`
8. Print `"All systems operational"`
Declare the delegate as `delegate void AlertDelegate(string message);` before the class. Assign it with `AlertDelegate alert = ShowAlert;` then call `alert("Fire detected!")`. For the Predicate, `Predicate<int> p = n => n > 20;` and `list.FindAll(p)` returns a filtered list. `Action<string>` takes a string and returns void.