MSDN .NET Core Documentation

Exploring Event Creation and Handling

Event Creation in .NET Core

Events are a fundamental mechanism in .NET Core for enabling communication between objects. They allow an object (the publisher) to notify other objects (the subscribers) when a particular action or occurrence happens. This pattern is crucial for building responsive and decoupled applications.

Understanding Delegates

At the heart of the .NET event system are delegates. A delegate is a type that represents references to methods with a particular parameter list and return type. Think of it as a strongly typed function pointer.

To create an event, you first need a delegate type that defines the signature of the methods that will handle the event.

Defining the Delegate

The convention for event-handling delegates is to have a `void` return type and accept two parameters:

Example: Defining an Event Data Class and Delegate

Let's define a custom class to hold event data:


public class ProgressChangedEventArgs : EventArgs
{
    public int PercentageComplete { get; }

    public ProgressChangedEventArgs(int percentage)
    {
        PercentageComplete = percentage;
    }
}
                

Now, define the delegate type:


public delegate void ProgressChangedEventHandler(object sender, ProgressChangedEventArgs e);
                

Alternatively, and more commonly, you can use the built-in generic delegate EventHandler<TEventArgs>:


// Using the generic delegate
public delegate void ProgressChangedEventHandler(object sender, ProgressChangedEventArgs e);
// Is equivalent to:
// public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
                

Declaring the Event

Within the publisher class, you declare the event using the event keyword, followed by the delegate type and the event name.

Example: Publisher Class with an Event


public class DataUploader
{
    // Declare the event using the custom delegate
    public event ProgressChangedEventHandler ProgressChanged;

    // Or using the generic EventHandler
    // public event EventHandler<ProgressChangedEventArgs> ProgressChanged;

    public void UploadData(string data)
    {
        Console.WriteLine("Starting data upload...");
        for (int i = 0; i < 100; i++)
        {
            // Simulate upload progress
            System.Threading.Thread.Sleep(50);
            OnProgressChanged(i); // Raise the event
        }
        Console.WriteLine("Data upload complete.");
    }

    protected virtual void OnProgressChanged(int percentage)
    {
        // Create the event arguments
        ProgressChangedEventArgs args = new ProgressChangedEventArgs(percentage);

        // Safely invoke the event
        // The '?' handles the case where there are no subscribers
        ProgressChanged?.Invoke(this, args);
    }
}
                

The OnProgressChanged method is a common convention for raising events. It's often made protected virtual to allow derived classes to override the event-raising behavior.

Raising the Event

The publisher raises an event by invoking the delegate associated with it. The ?.Invoke() syntax is a safe way to do this, as it checks if there are any subscribers before attempting to invoke the delegate. If no methods are subscribed to the event, Invoke() will not be called, preventing a NullReferenceException.

Subscribing to an Event

Subscriber objects can subscribe to events by providing a method that matches the delegate's signature. This is done using the += operator.

Example: Subscriber Class


public class ConsoleProgressBar
{
    public void Subscribe(DataUploader uploader)
    {
        // Subscribe to the ProgressChanged event
        uploader.ProgressChanged += HandleProgressChanged;
    }

    public void Unsubscribe(DataUploader uploader)
    {
        // Unsubscribe from the ProgressChanged event
        uploader.ProgressChanged -= HandleProgressChanged;
    }

    private void HandleProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Console.WriteLine($"Upload Progress: {e.PercentageComplete}%");
        // You could update a UI progress bar here
    }
}
                

Unsubscribing from an Event

It's crucial to unsubscribe from events when they are no longer needed to prevent memory leaks. This is done using the -= operator. Failure to unsubscribe can lead to a subscriber object being kept in memory longer than intended, even if it's no longer actively used.

Putting It All Together

Here's how you might use these classes:

Example: Main Program Flow


public class Program
{
    public static void Main(string[] args)
    {
        DataUploader uploader = new DataUploader();
        ConsoleProgressBar progressBar = new ConsoleProgressBar();

        // Subscribe the progress bar to the uploader's event
        progressBar.Subscribe(uploader);

        // Start the data upload, which will trigger events
        uploader.UploadData("MyImportantData");

        // Optionally unsubscribe later
        // progressBar.Unsubscribe(uploader);

        Console.ReadKey(); // Keep console open
    }
}
                

Best Practices for Event Handling