Delegates and Events
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 43 of 70.
A delegate is a type that represents a reference to a method. Think of it as a variable that holds a method instead of data. This allows you to pass methods as arguments, store them in collections, and call them dynamically.
You declare a delegate by specifying the method signature it can reference:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Usage
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alert: System startedEvents build on delegates to implement the publish-subscribe pattern. A class raises an event, and other classes can subscribe to receive notifications. The event keyword restricts how the delegate can be used - subscribers can only add or remove handlers, not invoke the event directly.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Notify all subscribers
}
}
// Subscribing
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // Button was clicked!The += operator subscribes a handler, while -= unsubscribes. The ?.Invoke() pattern safely calls the event only if there are subscribers. Events are fundamental to GUI applications, game engines, and any system where objects need to react to changes without tight coupling.
Challenge
EasyLet's build a temperature monitoring system that uses delegates and events to notify subscribers whenever the temperature changes. This is a classic example of the publish-subscribe pattern that events enable so elegantly.
You'll organize your code across two files:
TemperatureMonitor.cs: Create aTemperatureMonitorclass in theMonitoringnamespace that acts as the publisher. Your monitor should:- Have a private field to store the current temperature
- Declare an event using
Action<int>calledTemperatureChangedthat fires whenever the temperature is updated - Have a
Temperatureproperty (int) where the setter updates the private field and raises the event with the new temperature value
?.Invoke()pattern to safely raise the event only when there are subscribers.Program.cs: In your main file, create aTemperatureMonitorand subscribe two different handlers to itsTemperatureChangedevent:- One handler that prints:
Alert: Temperature is now {value} degrees - Another handler that prints:
Logged: {value}
- One handler that prints:
You will receive two inputs:
- First temperature value (integer)
- Second temperature value (integer)
For example, if the inputs are 25 and 30, the output should be:
Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30Notice how setting the Temperature property automatically notifies all subscribers - the monitor doesn't need to know anything about who's listening. That's the beauty of the event-driven approach!
Cheat sheet
A delegate is a type that represents a reference to a method, allowing you to pass methods as arguments, store them in collections, and call them dynamically.
Declare a delegate by specifying the method signature:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Usage
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alert: System startedEvents implement the publish-subscribe pattern using delegates. The event keyword restricts delegate usage - subscribers can only add or remove handlers, not invoke the event directly.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Notify all subscribers
}
}
// Subscribing
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // Button was clicked!Use += to subscribe a handler and -= to unsubscribe. The ?.Invoke() pattern safely calls the event only if there are subscribers.
Try it yourself
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Read input
int temp1 = Convert.ToInt32(Console.ReadLine());
int temp2 = Convert.ToInt32(Console.ReadLine());
// TODO: Create a TemperatureMonitor instance
// TODO: Subscribe a handler that prints: "Alert: Temperature is now {value} degrees"
// TODO: Subscribe another handler that prints: "Logged: {value}"
// TODO: Set the temperature to temp1
// TODO: Set the temperature to temp2
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator8Advanced OOP Concepts
Composition over InheritanceGenerics (Classes & Methods)Delegates and EventsAttributes and ReflectionIDisposable & using StatementDependency Injection Basics