C# > Advanced C# > Delegates and Events > Event Declaration and Handling
Custom Event Declaration and Handling in C#
This code snippet demonstrates how to declare a custom event in C# using delegates and how to handle that event in another class. Events allow classes to notify other classes when something interesting happens. This is a fundamental aspect of building loosely coupled and responsive applications.
Basic Event Declaration
This example demonstrates a typical event pattern in C#. First, a delegate `DataProcessedEventHandler` is defined, representing the signature of the event handler. Then, an `EventArgs` derived class `DataProcessedEventArgs` holds the data associated with the event. The `DataProcessor` class declares the `DataProcessed` event and defines a `ProcessData` method that, after simulating data processing, raises the event using `OnDataProcessed`. The `DataConsumer` class subscribes to the event using the `+=` operator and handles the event in the `Processor_DataProcessed` method. Unsubscribing from the event is done via the `-=` operator.
using System;
// 1. Define a delegate type for the event
public delegate void DataProcessedEventHandler(object sender, DataProcessedEventArgs e);
// 2. Define a class to hold event data (optional but recommended)
public class DataProcessedEventArgs : EventArgs
{
public string Data { get; set; }
public DateTime ProcessedTime { get; set; }
public DataProcessedEventArgs(string data)
{
Data = data;
ProcessedTime = DateTime.Now;
}
}
// 3. Define a class that will raise the event
public class DataProcessor
{
// 4. Declare the event using the delegate type
public event DataProcessedEventHandler DataProcessed;
public void ProcessData(string data)
{
Console.WriteLine($"Processing data: {data}");
// Simulate some processing
System.Threading.Thread.Sleep(1000); // Simulate work
// 5. Raise the event (if there are subscribers)
OnDataProcessed(new DataProcessedEventArgs(data));
}
// 6. Create a protected virtual method to raise the event
protected virtual void OnDataProcessed(DataProcessedEventArgs e)
{
// Make a copy to be thread safe.
DataProcessedEventHandler handler = DataProcessed;
// Only raise the event if there are subscribers.
if (handler != null)
{
handler(this, e);
}
}
}
// 7. Define a class that will subscribe to the event
public class DataConsumer
{
public void Subscribe(DataProcessor processor)
{
// 8. Subscribe to the event
processor.DataProcessed += Processor_DataProcessed;
}
private void Processor_DataProcessed(object sender, DataProcessedEventArgs e)
{
Console.WriteLine($"Data processed: {e.Data} at {e.ProcessedTime}");
}
public void Unsubscribe(DataProcessor processor)
{
processor.DataProcessed -= Processor_DataProcessed;
}
}
public class Example
{
public static void Main(string[] args)
{
// Usage
DataProcessor processor = new DataProcessor();
DataConsumer consumer = new DataConsumer();
// Subscribe the consumer to the processor's event
consumer.Subscribe(processor);
// Process some data (this will raise the event)
processor.ProcessData("Important Data");
consumer.Unsubscribe(processor);
processor.ProcessData("Data after unsubscribe");
Console.ReadKey();
}
}
Concepts Behind the Snippet
This snippet highlights several key concepts: * Delegates: Type-safe function pointers that define the signature of the event handler. * Events: A mechanism that allows a class or object to notify other classes or objects when something of interest occurs. Events use delegates under the hood. * Event Handlers: Methods that are executed when an event is raised. * EventArgs: A base class for event data that can be extended to include custom data specific to the event. * Publisher-Subscriber Pattern: Events implement the publisher-subscriber pattern, where the class that raises the event (the publisher) doesn't need to know the details of the classes that handle the event (the subscribers).
Real-Life Use Case
Consider a UI application where a button click needs to trigger an action in another part of the application. The button class can define a `Click` event. Other parts of the application can subscribe to this event and execute their specific logic when the button is clicked. This promotes loose coupling because the button doesn't need to know what specific actions are performed when it's clicked.
Best Practices
Interview Tip
Be prepared to explain the difference between delegates and events. Delegates are type-safe function pointers, while events are a language construct built on top of delegates that provide a controlled way for classes to subscribe to and be notified of events. Events encapsulate delegates to prevent subscribers from directly invoking the delegate, only allowing them to add or remove handlers.
When to Use Them
Use events when you need to implement the publisher-subscriber pattern, where one class (the publisher) needs to notify other classes (the subscribers) when something interesting happens, without knowing the specific details of the subscribers. This is especially useful for UI applications, asynchronous operations, and decoupled systems.
Alternatives
Pros
Cons
FAQ
-
What is the difference between a delegate and an event?
A delegate is a type-safe function pointer that defines the signature of a method. An event is a language construct built on top of delegates that provides a controlled way for classes to subscribe to and be notified of events. Events encapsulate delegates to prevent subscribers from directly invoking the delegate, only allowing them to add or remove handlers. -
Why should I use a protected virtual `OnEventName` method to raise an event?
Using a protected virtual `OnEventName` method allows derived classes to override the event raising behavior. This provides greater flexibility and extensibility. -
How do I prevent memory leaks when using events?
Always unsubscribe from events when you no longer need to receive notifications. This prevents objects from being garbage collected because they are still referenced by the event's delegate list.