C#idiomatic C#
how-to-declare-multiple-events-using-event-propertiesstandard/events

Core library

Declare multiple events using event properties

Learn how to declare many events by using event properties. Define delegate collections, event keys, and event properties. Implement add and remove accessor methods.

  • page standard/events/how-to-declare-multiple-events-using-event-properties
  • updated 03/25/2026

When a class defines many events using field-like event declarations, the compiler generates a backing field for each one. Every instance of that class allocates memory for all of those fields—even for events that are never subscribed. With many events, this per-instance overhead wastes memory.

To avoid this memory overhead, use event properties backed by a delegate collection. The collection must provide methods that set, access, and retrieve delegates by event key. System.ComponentModel.EventHandlerList is designed for this purpose, but you can also use a System.Collections.Hashtable or a class derived from System.Collections.DictionaryBase. Keep the collection's implementation details private.

Each event property defines an add accessor and a remove accessor. The add accessor adds the input delegate to the delegate collection, and the remove accessor removes it. Both accessors use a predefined key to add and remove instances from the collection.

Prerequisites

Familiarize yourself with the concepts in the Events article.

Define multiple events using event properties

These steps create a Sensor class that exposes 10 event properties, all backed by a single EventHandlerList.

  1. Define an event data class that inherits from System.EventArgs.

    Add properties for each piece of data you want to pass to the handler:

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Sensor.csSensorEventArgs

csharp
// <EventProperties>
using System.ComponentModel;

// <SensorEventArgs>
public class SensorEventArgs(string sensorId, double value) : EventArgs
{
    public string SensorId { get; } = sensorId;
    public double Value { get; } = value;
}
// </SensorEventArgs>

// Sensor defines 10 event properties backed by a single EventHandlerList.
// EventHandlerList is memory-efficient for classes with many events: it only
// allocates storage for events that have active subscribers.
class Sensor
{
    // <EventHandlerListField>
    protected EventHandlerList listEventDelegates = new();
    // </EventHandlerListField>

    // <EventKeys>
    static readonly object temperatureChangedKey = new();
    static readonly object humidityChangedKey = new();
    static readonly object pressureChangedKey = new();
    static readonly object batteryLowKey = new();
    static readonly object signalLostKey = new();
    static readonly object signalRestoredKey = new();
    static readonly object thresholdExceededKey = new();
    static readonly object calibrationRequiredKey = new();
    static readonly object dataReceivedKey = new();
    static readonly object errorDetectedKey = new();
    // </EventKeys>

    // <SingleEventProperty>
    public event EventHandler<SensorEventArgs> TemperatureChanged
    {
        add    { listEventDelegates.AddHandler(temperatureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(temperatureChangedKey, value); }
    }
    protected virtual void OnTemperatureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[temperatureChangedKey])?.Invoke(this, e);
    // </SingleEventProperty>

    public event EventHandler<SensorEventArgs> HumidityChanged
    {
        add    { listEventDelegates.AddHandler(humidityChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(humidityChangedKey, value); }
    }
    protected virtual void OnHumidityChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[humidityChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> PressureChanged
    {
        add    { listEventDelegates.AddHandler(pressureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(pressureChangedKey, value); }
    }
    protected virtual void OnPressureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[pressureChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> BatteryLow
    {
        add    { listEventDelegates.AddHandler(batteryLowKey, value); }
        remove { listEventDelegates.RemoveHandler(batteryLowKey, value); }
    }
    protected virtual void OnBatteryLow(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[batteryLowKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalLost
    {
        add    { listEventDelegates.AddHandler(signalLostKey, value); }
        remove { listEventDelegates.RemoveHandler(signalLostKey, value); }
    }
    protected virtual void OnSignalLost(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalLostKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalRestored
    {
        add    { listEventDelegates.AddHandler(signalRestoredKey, value); }
        remove { listEventDelegates.RemoveHandler(signalRestoredKey, value); }
    }
    protected virtual void OnSignalRestored(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalRestoredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ThresholdExceeded
    {
        add    { listEventDelegates.AddHandler(thresholdExceededKey, value); }
        remove { listEventDelegates.RemoveHandler(thresholdExceededKey, value); }
    }
    protected virtual void OnThresholdExceeded(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[thresholdExceededKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> CalibrationRequired
    {
        add    { listEventDelegates.AddHandler(calibrationRequiredKey, value); }
        remove { listEventDelegates.RemoveHandler(calibrationRequiredKey, value); }
    }
    protected virtual void OnCalibrationRequired(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[calibrationRequiredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> DataReceived
    {
        add    { listEventDelegates.AddHandler(dataReceivedKey, value); }
        remove { listEventDelegates.RemoveHandler(dataReceivedKey, value); }
    }
    protected virtual void OnDataReceived(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[dataReceivedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ErrorDetected
    {
        add    { listEventDelegates.AddHandler(errorDetectedKey, value); }
        remove { listEventDelegates.RemoveHandler(errorDetectedKey, value); }
    }
    protected virtual void OnErrorDetected(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[errorDetectedKey])?.Invoke(this, e);
}
// </EventProperties>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Sensor.vbSensorEventArgs

vb
' <EventProperties>
Imports System.ComponentModel

' <SensorEventArgs>
Public Class SensorEventArgs
    Inherits EventArgs

    Public ReadOnly Property SensorId As String
    Public ReadOnly Property Value As Double

    Public Sub New(sensorId As String, value As Double)
        Me.SensorId = sensorId
        Me.Value = value
    End Sub
End Class
' </SensorEventArgs>

' Sensor defines 10 event properties backed by a single EventHandlerList.
' EventHandlerList is memory-efficient for classes with many events: it only
' allocates storage for events that have active subscribers.
Class Sensor

    ' <EventHandlerListField>
    Protected listEventDelegates As New EventHandlerList()
    ' </EventHandlerListField>

    ' <EventKeys>
    Shared ReadOnly temperatureChangedKey  As New Object()
    Shared ReadOnly humidityChangedKey     As New Object()
    Shared ReadOnly pressureChangedKey     As New Object()
    Shared ReadOnly batteryLowKey          As New Object()
    Shared ReadOnly signalLostKey          As New Object()
    Shared ReadOnly signalRestoredKey      As New Object()
    Shared ReadOnly thresholdExceededKey   As New Object()
    Shared ReadOnly calibrationRequiredKey As New Object()
    Shared ReadOnly dataReceivedKey        As New Object()
    Shared ReadOnly errorDetectedKey       As New Object()
    ' </EventKeys>

    ' <SingleEventProperty>
    Public Custom Event TemperatureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(temperatureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(temperatureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(temperatureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event
    ' </SingleEventProperty>

    Public Custom Event HumidityChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(humidityChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(humidityChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(humidityChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event PressureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(pressureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(pressureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(pressureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event BatteryLow As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(batteryLowKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(batteryLowKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(batteryLowKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalLost As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalLostKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalLostKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalLostKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalRestored As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalRestoredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalRestoredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalRestoredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ThresholdExceeded As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(thresholdExceededKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(thresholdExceededKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(thresholdExceededKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event CalibrationRequired As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(calibrationRequiredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(calibrationRequiredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(calibrationRequiredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event DataReceived As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(dataReceivedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(dataReceivedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(dataReceivedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ErrorDetected As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(errorDetectedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(errorDetectedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(errorDetectedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

End Class
' </EventProperties>

  1. Declare an System.ComponentModel.EventHandlerList field to store the delegates:

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Sensor.csEventHandlerListField

csharp
// <EventProperties>
using System.ComponentModel;

// <SensorEventArgs>
public class SensorEventArgs(string sensorId, double value) : EventArgs
{
    public string SensorId { get; } = sensorId;
    public double Value { get; } = value;
}
// </SensorEventArgs>

// Sensor defines 10 event properties backed by a single EventHandlerList.
// EventHandlerList is memory-efficient for classes with many events: it only
// allocates storage for events that have active subscribers.
class Sensor
{
    // <EventHandlerListField>
    protected EventHandlerList listEventDelegates = new();
    // </EventHandlerListField>

    // <EventKeys>
    static readonly object temperatureChangedKey = new();
    static readonly object humidityChangedKey = new();
    static readonly object pressureChangedKey = new();
    static readonly object batteryLowKey = new();
    static readonly object signalLostKey = new();
    static readonly object signalRestoredKey = new();
    static readonly object thresholdExceededKey = new();
    static readonly object calibrationRequiredKey = new();
    static readonly object dataReceivedKey = new();
    static readonly object errorDetectedKey = new();
    // </EventKeys>

    // <SingleEventProperty>
    public event EventHandler<SensorEventArgs> TemperatureChanged
    {
        add    { listEventDelegates.AddHandler(temperatureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(temperatureChangedKey, value); }
    }
    protected virtual void OnTemperatureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[temperatureChangedKey])?.Invoke(this, e);
    // </SingleEventProperty>

    public event EventHandler<SensorEventArgs> HumidityChanged
    {
        add    { listEventDelegates.AddHandler(humidityChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(humidityChangedKey, value); }
    }
    protected virtual void OnHumidityChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[humidityChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> PressureChanged
    {
        add    { listEventDelegates.AddHandler(pressureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(pressureChangedKey, value); }
    }
    protected virtual void OnPressureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[pressureChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> BatteryLow
    {
        add    { listEventDelegates.AddHandler(batteryLowKey, value); }
        remove { listEventDelegates.RemoveHandler(batteryLowKey, value); }
    }
    protected virtual void OnBatteryLow(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[batteryLowKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalLost
    {
        add    { listEventDelegates.AddHandler(signalLostKey, value); }
        remove { listEventDelegates.RemoveHandler(signalLostKey, value); }
    }
    protected virtual void OnSignalLost(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalLostKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalRestored
    {
        add    { listEventDelegates.AddHandler(signalRestoredKey, value); }
        remove { listEventDelegates.RemoveHandler(signalRestoredKey, value); }
    }
    protected virtual void OnSignalRestored(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalRestoredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ThresholdExceeded
    {
        add    { listEventDelegates.AddHandler(thresholdExceededKey, value); }
        remove { listEventDelegates.RemoveHandler(thresholdExceededKey, value); }
    }
    protected virtual void OnThresholdExceeded(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[thresholdExceededKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> CalibrationRequired
    {
        add    { listEventDelegates.AddHandler(calibrationRequiredKey, value); }
        remove { listEventDelegates.RemoveHandler(calibrationRequiredKey, value); }
    }
    protected virtual void OnCalibrationRequired(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[calibrationRequiredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> DataReceived
    {
        add    { listEventDelegates.AddHandler(dataReceivedKey, value); }
        remove { listEventDelegates.RemoveHandler(dataReceivedKey, value); }
    }
    protected virtual void OnDataReceived(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[dataReceivedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ErrorDetected
    {
        add    { listEventDelegates.AddHandler(errorDetectedKey, value); }
        remove { listEventDelegates.RemoveHandler(errorDetectedKey, value); }
    }
    protected virtual void OnErrorDetected(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[errorDetectedKey])?.Invoke(this, e);
}
// </EventProperties>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Sensor.vbEventHandlerListField

vb
' <EventProperties>
Imports System.ComponentModel

' <SensorEventArgs>
Public Class SensorEventArgs
    Inherits EventArgs

    Public ReadOnly Property SensorId As String
    Public ReadOnly Property Value As Double

    Public Sub New(sensorId As String, value As Double)
        Me.SensorId = sensorId
        Me.Value = value
    End Sub
End Class
' </SensorEventArgs>

' Sensor defines 10 event properties backed by a single EventHandlerList.
' EventHandlerList is memory-efficient for classes with many events: it only
' allocates storage for events that have active subscribers.
Class Sensor

    ' <EventHandlerListField>
    Protected listEventDelegates As New EventHandlerList()
    ' </EventHandlerListField>

    ' <EventKeys>
    Shared ReadOnly temperatureChangedKey  As New Object()
    Shared ReadOnly humidityChangedKey     As New Object()
    Shared ReadOnly pressureChangedKey     As New Object()
    Shared ReadOnly batteryLowKey          As New Object()
    Shared ReadOnly signalLostKey          As New Object()
    Shared ReadOnly signalRestoredKey      As New Object()
    Shared ReadOnly thresholdExceededKey   As New Object()
    Shared ReadOnly calibrationRequiredKey As New Object()
    Shared ReadOnly dataReceivedKey        As New Object()
    Shared ReadOnly errorDetectedKey       As New Object()
    ' </EventKeys>

    ' <SingleEventProperty>
    Public Custom Event TemperatureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(temperatureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(temperatureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(temperatureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event
    ' </SingleEventProperty>

    Public Custom Event HumidityChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(humidityChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(humidityChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(humidityChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event PressureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(pressureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(pressureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(pressureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event BatteryLow As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(batteryLowKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(batteryLowKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(batteryLowKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalLost As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalLostKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalLostKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalLostKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalRestored As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalRestoredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalRestoredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalRestoredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ThresholdExceeded As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(thresholdExceededKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(thresholdExceededKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(thresholdExceededKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event CalibrationRequired As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(calibrationRequiredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(calibrationRequiredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(calibrationRequiredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event DataReceived As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(dataReceivedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(dataReceivedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(dataReceivedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ErrorDetected As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(errorDetectedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(errorDetectedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(errorDetectedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

End Class
' </EventProperties>

  1. Declare a unique key for each event.

    EventHandlerList stores delegates by key. Use a static readonly object (Shared ReadOnly in Visual Basic) for each key—object identity ensures each key is truly unique:

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Sensor.csEventKeys

csharp
// <EventProperties>
using System.ComponentModel;

// <SensorEventArgs>
public class SensorEventArgs(string sensorId, double value) : EventArgs
{
    public string SensorId { get; } = sensorId;
    public double Value { get; } = value;
}
// </SensorEventArgs>

// Sensor defines 10 event properties backed by a single EventHandlerList.
// EventHandlerList is memory-efficient for classes with many events: it only
// allocates storage for events that have active subscribers.
class Sensor
{
    // <EventHandlerListField>
    protected EventHandlerList listEventDelegates = new();
    // </EventHandlerListField>

    // <EventKeys>
    static readonly object temperatureChangedKey = new();
    static readonly object humidityChangedKey = new();
    static readonly object pressureChangedKey = new();
    static readonly object batteryLowKey = new();
    static readonly object signalLostKey = new();
    static readonly object signalRestoredKey = new();
    static readonly object thresholdExceededKey = new();
    static readonly object calibrationRequiredKey = new();
    static readonly object dataReceivedKey = new();
    static readonly object errorDetectedKey = new();
    // </EventKeys>

    // <SingleEventProperty>
    public event EventHandler<SensorEventArgs> TemperatureChanged
    {
        add    { listEventDelegates.AddHandler(temperatureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(temperatureChangedKey, value); }
    }
    protected virtual void OnTemperatureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[temperatureChangedKey])?.Invoke(this, e);
    // </SingleEventProperty>

    public event EventHandler<SensorEventArgs> HumidityChanged
    {
        add    { listEventDelegates.AddHandler(humidityChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(humidityChangedKey, value); }
    }
    protected virtual void OnHumidityChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[humidityChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> PressureChanged
    {
        add    { listEventDelegates.AddHandler(pressureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(pressureChangedKey, value); }
    }
    protected virtual void OnPressureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[pressureChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> BatteryLow
    {
        add    { listEventDelegates.AddHandler(batteryLowKey, value); }
        remove { listEventDelegates.RemoveHandler(batteryLowKey, value); }
    }
    protected virtual void OnBatteryLow(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[batteryLowKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalLost
    {
        add    { listEventDelegates.AddHandler(signalLostKey, value); }
        remove { listEventDelegates.RemoveHandler(signalLostKey, value); }
    }
    protected virtual void OnSignalLost(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalLostKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalRestored
    {
        add    { listEventDelegates.AddHandler(signalRestoredKey, value); }
        remove { listEventDelegates.RemoveHandler(signalRestoredKey, value); }
    }
    protected virtual void OnSignalRestored(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalRestoredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ThresholdExceeded
    {
        add    { listEventDelegates.AddHandler(thresholdExceededKey, value); }
        remove { listEventDelegates.RemoveHandler(thresholdExceededKey, value); }
    }
    protected virtual void OnThresholdExceeded(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[thresholdExceededKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> CalibrationRequired
    {
        add    { listEventDelegates.AddHandler(calibrationRequiredKey, value); }
        remove { listEventDelegates.RemoveHandler(calibrationRequiredKey, value); }
    }
    protected virtual void OnCalibrationRequired(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[calibrationRequiredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> DataReceived
    {
        add    { listEventDelegates.AddHandler(dataReceivedKey, value); }
        remove { listEventDelegates.RemoveHandler(dataReceivedKey, value); }
    }
    protected virtual void OnDataReceived(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[dataReceivedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ErrorDetected
    {
        add    { listEventDelegates.AddHandler(errorDetectedKey, value); }
        remove { listEventDelegates.RemoveHandler(errorDetectedKey, value); }
    }
    protected virtual void OnErrorDetected(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[errorDetectedKey])?.Invoke(this, e);
}
// </EventProperties>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Sensor.vbEventKeys

vb
' <EventProperties>
Imports System.ComponentModel

' <SensorEventArgs>
Public Class SensorEventArgs
    Inherits EventArgs

    Public ReadOnly Property SensorId As String
    Public ReadOnly Property Value As Double

    Public Sub New(sensorId As String, value As Double)
        Me.SensorId = sensorId
        Me.Value = value
    End Sub
End Class
' </SensorEventArgs>

' Sensor defines 10 event properties backed by a single EventHandlerList.
' EventHandlerList is memory-efficient for classes with many events: it only
' allocates storage for events that have active subscribers.
Class Sensor

    ' <EventHandlerListField>
    Protected listEventDelegates As New EventHandlerList()
    ' </EventHandlerListField>

    ' <EventKeys>
    Shared ReadOnly temperatureChangedKey  As New Object()
    Shared ReadOnly humidityChangedKey     As New Object()
    Shared ReadOnly pressureChangedKey     As New Object()
    Shared ReadOnly batteryLowKey          As New Object()
    Shared ReadOnly signalLostKey          As New Object()
    Shared ReadOnly signalRestoredKey      As New Object()
    Shared ReadOnly thresholdExceededKey   As New Object()
    Shared ReadOnly calibrationRequiredKey As New Object()
    Shared ReadOnly dataReceivedKey        As New Object()
    Shared ReadOnly errorDetectedKey       As New Object()
    ' </EventKeys>

    ' <SingleEventProperty>
    Public Custom Event TemperatureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(temperatureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(temperatureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(temperatureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event
    ' </SingleEventProperty>

    Public Custom Event HumidityChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(humidityChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(humidityChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(humidityChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event PressureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(pressureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(pressureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(pressureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event BatteryLow As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(batteryLowKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(batteryLowKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(batteryLowKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalLost As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalLostKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalLostKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalLostKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalRestored As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalRestoredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalRestoredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalRestoredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ThresholdExceeded As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(thresholdExceededKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(thresholdExceededKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(thresholdExceededKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event CalibrationRequired As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(calibrationRequiredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(calibrationRequiredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(calibrationRequiredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event DataReceived As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(dataReceivedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(dataReceivedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(dataReceivedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ErrorDetected As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(errorDetectedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(errorDetectedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(errorDetectedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

End Class
' </EventProperties>

  1. Define each event as an event property with custom add and remove accessors.

    Each accessor calls AddHandler or RemoveHandler on the list using that event's key. In C#, also add a protected virtual raise method that retrieves the delegate from the list by key and invokes it. In Visual Basic, the Custom Event declaration already includes a RaiseEvent block that does this:

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Sensor.csSingleEventProperty

csharp
// <EventProperties>
using System.ComponentModel;

// <SensorEventArgs>
public class SensorEventArgs(string sensorId, double value) : EventArgs
{
    public string SensorId { get; } = sensorId;
    public double Value { get; } = value;
}
// </SensorEventArgs>

// Sensor defines 10 event properties backed by a single EventHandlerList.
// EventHandlerList is memory-efficient for classes with many events: it only
// allocates storage for events that have active subscribers.
class Sensor
{
    // <EventHandlerListField>
    protected EventHandlerList listEventDelegates = new();
    // </EventHandlerListField>

    // <EventKeys>
    static readonly object temperatureChangedKey = new();
    static readonly object humidityChangedKey = new();
    static readonly object pressureChangedKey = new();
    static readonly object batteryLowKey = new();
    static readonly object signalLostKey = new();
    static readonly object signalRestoredKey = new();
    static readonly object thresholdExceededKey = new();
    static readonly object calibrationRequiredKey = new();
    static readonly object dataReceivedKey = new();
    static readonly object errorDetectedKey = new();
    // </EventKeys>

    // <SingleEventProperty>
    public event EventHandler<SensorEventArgs> TemperatureChanged
    {
        add    { listEventDelegates.AddHandler(temperatureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(temperatureChangedKey, value); }
    }
    protected virtual void OnTemperatureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[temperatureChangedKey])?.Invoke(this, e);
    // </SingleEventProperty>

    public event EventHandler<SensorEventArgs> HumidityChanged
    {
        add    { listEventDelegates.AddHandler(humidityChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(humidityChangedKey, value); }
    }
    protected virtual void OnHumidityChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[humidityChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> PressureChanged
    {
        add    { listEventDelegates.AddHandler(pressureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(pressureChangedKey, value); }
    }
    protected virtual void OnPressureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[pressureChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> BatteryLow
    {
        add    { listEventDelegates.AddHandler(batteryLowKey, value); }
        remove { listEventDelegates.RemoveHandler(batteryLowKey, value); }
    }
    protected virtual void OnBatteryLow(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[batteryLowKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalLost
    {
        add    { listEventDelegates.AddHandler(signalLostKey, value); }
        remove { listEventDelegates.RemoveHandler(signalLostKey, value); }
    }
    protected virtual void OnSignalLost(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalLostKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalRestored
    {
        add    { listEventDelegates.AddHandler(signalRestoredKey, value); }
        remove { listEventDelegates.RemoveHandler(signalRestoredKey, value); }
    }
    protected virtual void OnSignalRestored(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalRestoredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ThresholdExceeded
    {
        add    { listEventDelegates.AddHandler(thresholdExceededKey, value); }
        remove { listEventDelegates.RemoveHandler(thresholdExceededKey, value); }
    }
    protected virtual void OnThresholdExceeded(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[thresholdExceededKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> CalibrationRequired
    {
        add    { listEventDelegates.AddHandler(calibrationRequiredKey, value); }
        remove { listEventDelegates.RemoveHandler(calibrationRequiredKey, value); }
    }
    protected virtual void OnCalibrationRequired(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[calibrationRequiredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> DataReceived
    {
        add    { listEventDelegates.AddHandler(dataReceivedKey, value); }
        remove { listEventDelegates.RemoveHandler(dataReceivedKey, value); }
    }
    protected virtual void OnDataReceived(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[dataReceivedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ErrorDetected
    {
        add    { listEventDelegates.AddHandler(errorDetectedKey, value); }
        remove { listEventDelegates.RemoveHandler(errorDetectedKey, value); }
    }
    protected virtual void OnErrorDetected(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[errorDetectedKey])?.Invoke(this, e);
}
// </EventProperties>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Sensor.vbSingleEventProperty

vb
' <EventProperties>
Imports System.ComponentModel

' <SensorEventArgs>
Public Class SensorEventArgs
    Inherits EventArgs

    Public ReadOnly Property SensorId As String
    Public ReadOnly Property Value As Double

    Public Sub New(sensorId As String, value As Double)
        Me.SensorId = sensorId
        Me.Value = value
    End Sub
End Class
' </SensorEventArgs>

' Sensor defines 10 event properties backed by a single EventHandlerList.
' EventHandlerList is memory-efficient for classes with many events: it only
' allocates storage for events that have active subscribers.
Class Sensor

    ' <EventHandlerListField>
    Protected listEventDelegates As New EventHandlerList()
    ' </EventHandlerListField>

    ' <EventKeys>
    Shared ReadOnly temperatureChangedKey  As New Object()
    Shared ReadOnly humidityChangedKey     As New Object()
    Shared ReadOnly pressureChangedKey     As New Object()
    Shared ReadOnly batteryLowKey          As New Object()
    Shared ReadOnly signalLostKey          As New Object()
    Shared ReadOnly signalRestoredKey      As New Object()
    Shared ReadOnly thresholdExceededKey   As New Object()
    Shared ReadOnly calibrationRequiredKey As New Object()
    Shared ReadOnly dataReceivedKey        As New Object()
    Shared ReadOnly errorDetectedKey       As New Object()
    ' </EventKeys>

    ' <SingleEventProperty>
    Public Custom Event TemperatureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(temperatureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(temperatureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(temperatureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event
    ' </SingleEventProperty>

    Public Custom Event HumidityChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(humidityChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(humidityChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(humidityChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event PressureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(pressureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(pressureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(pressureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event BatteryLow As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(batteryLowKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(batteryLowKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(batteryLowKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalLost As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalLostKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalLostKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalLostKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalRestored As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalRestoredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalRestoredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalRestoredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ThresholdExceeded As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(thresholdExceededKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(thresholdExceededKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(thresholdExceededKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event CalibrationRequired As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(calibrationRequiredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(calibrationRequiredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(calibrationRequiredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event DataReceived As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(dataReceivedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(dataReceivedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(dataReceivedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ErrorDetected As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(errorDetectedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(errorDetectedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(errorDetectedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

End Class
' </EventProperties>

Repeat this pattern for each event, using its corresponding key.

  1. Subscribe to the event using the += operator (in Visual Basic, AddHandler):

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Program.csSubscribeEvent

csharp
Sensor sensor = new Sensor();
// <SubscribeEvent>
sensor.TemperatureChanged += Sensor_TemperatureChanged;
// </SubscribeEvent>

// <HandleEvent>
static void Sensor_TemperatureChanged(object? sender, SensorEventArgs e) =>
    Console.WriteLine($"Sensor {e.SensorId}: temperature changed to {e.Value}.");
// </HandleEvent>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Program.vbSubscribeEvent

vb
Module Program
    Sub Main()
        Dim sensor As New Sensor()
        ' <SubscribeEvent>
        AddHandler sensor.TemperatureChanged, AddressOf Sensor_TemperatureChanged
        ' </SubscribeEvent>
    End Sub

    ' <HandleEvent>
    Sub Sensor_TemperatureChanged(sender As Object, e As SensorEventArgs)
        Console.WriteLine("Sensor {0}: temperature changed to {1}.", e.SensorId, e.Value)
    End Sub
    ' </HandleEvent>
End Module

  1. Define the event handler.

    The signature must match the System.EventHandler delegate—an object sender and your event data class as the second parameter:

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Program.csHandleEvent

csharp
Sensor sensor = new Sensor();
// <SubscribeEvent>
sensor.TemperatureChanged += Sensor_TemperatureChanged;
// </SubscribeEvent>

// <HandleEvent>
static void Sensor_TemperatureChanged(object? sender, SensorEventArgs e) =>
    Console.WriteLine($"Sensor {e.SensorId}: temperature changed to {e.Value}.");
// </HandleEvent>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Program.vbHandleEvent

vb
Module Program
    Sub Main()
        Dim sensor As New Sensor()
        ' <SubscribeEvent>
        AddHandler sensor.TemperatureChanged, AddressOf Sensor_TemperatureChanged
        ' </SubscribeEvent>
    End Sub

    ' <HandleEvent>
    Sub Sensor_TemperatureChanged(sender As Object, e As SensorEventArgs)
        Console.WriteLine("Sensor {0}: temperature changed to {1}.", e.SensorId, e.Value)
    End Sub
    ' </HandleEvent>
End Module

The following example shows the complete Sensor class implementation:

sample · csharpdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/csharp/Sensor.csEventProperties

csharp
// <EventProperties>
using System.ComponentModel;

// <SensorEventArgs>
public class SensorEventArgs(string sensorId, double value) : EventArgs
{
    public string SensorId { get; } = sensorId;
    public double Value { get; } = value;
}
// </SensorEventArgs>

// Sensor defines 10 event properties backed by a single EventHandlerList.
// EventHandlerList is memory-efficient for classes with many events: it only
// allocates storage for events that have active subscribers.
class Sensor
{
    // <EventHandlerListField>
    protected EventHandlerList listEventDelegates = new();
    // </EventHandlerListField>

    // <EventKeys>
    static readonly object temperatureChangedKey = new();
    static readonly object humidityChangedKey = new();
    static readonly object pressureChangedKey = new();
    static readonly object batteryLowKey = new();
    static readonly object signalLostKey = new();
    static readonly object signalRestoredKey = new();
    static readonly object thresholdExceededKey = new();
    static readonly object calibrationRequiredKey = new();
    static readonly object dataReceivedKey = new();
    static readonly object errorDetectedKey = new();
    // </EventKeys>

    // <SingleEventProperty>
    public event EventHandler<SensorEventArgs> TemperatureChanged
    {
        add    { listEventDelegates.AddHandler(temperatureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(temperatureChangedKey, value); }
    }
    protected virtual void OnTemperatureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[temperatureChangedKey])?.Invoke(this, e);
    // </SingleEventProperty>

    public event EventHandler<SensorEventArgs> HumidityChanged
    {
        add    { listEventDelegates.AddHandler(humidityChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(humidityChangedKey, value); }
    }
    protected virtual void OnHumidityChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[humidityChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> PressureChanged
    {
        add    { listEventDelegates.AddHandler(pressureChangedKey, value); }
        remove { listEventDelegates.RemoveHandler(pressureChangedKey, value); }
    }
    protected virtual void OnPressureChanged(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[pressureChangedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> BatteryLow
    {
        add    { listEventDelegates.AddHandler(batteryLowKey, value); }
        remove { listEventDelegates.RemoveHandler(batteryLowKey, value); }
    }
    protected virtual void OnBatteryLow(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[batteryLowKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalLost
    {
        add    { listEventDelegates.AddHandler(signalLostKey, value); }
        remove { listEventDelegates.RemoveHandler(signalLostKey, value); }
    }
    protected virtual void OnSignalLost(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalLostKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> SignalRestored
    {
        add    { listEventDelegates.AddHandler(signalRestoredKey, value); }
        remove { listEventDelegates.RemoveHandler(signalRestoredKey, value); }
    }
    protected virtual void OnSignalRestored(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[signalRestoredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ThresholdExceeded
    {
        add    { listEventDelegates.AddHandler(thresholdExceededKey, value); }
        remove { listEventDelegates.RemoveHandler(thresholdExceededKey, value); }
    }
    protected virtual void OnThresholdExceeded(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[thresholdExceededKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> CalibrationRequired
    {
        add    { listEventDelegates.AddHandler(calibrationRequiredKey, value); }
        remove { listEventDelegates.RemoveHandler(calibrationRequiredKey, value); }
    }
    protected virtual void OnCalibrationRequired(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[calibrationRequiredKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> DataReceived
    {
        add    { listEventDelegates.AddHandler(dataReceivedKey, value); }
        remove { listEventDelegates.RemoveHandler(dataReceivedKey, value); }
    }
    protected virtual void OnDataReceived(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[dataReceivedKey])?.Invoke(this, e);

    public event EventHandler<SensorEventArgs> ErrorDetected
    {
        add    { listEventDelegates.AddHandler(errorDetectedKey, value); }
        remove { listEventDelegates.RemoveHandler(errorDetectedKey, value); }
    }
    protected virtual void OnErrorDetected(SensorEventArgs e) =>
        ((EventHandler<SensorEventArgs>?)listEventDelegates[errorDetectedKey])?.Invoke(this, e);
}
// </EventProperties>

sample · vbdocs/standard/events/snippets/how-to-handle-multiple-events-using-event-properties/vb/Sensor.vbEventProperties

vb
' <EventProperties>
Imports System.ComponentModel

' <SensorEventArgs>
Public Class SensorEventArgs
    Inherits EventArgs

    Public ReadOnly Property SensorId As String
    Public ReadOnly Property Value As Double

    Public Sub New(sensorId As String, value As Double)
        Me.SensorId = sensorId
        Me.Value = value
    End Sub
End Class
' </SensorEventArgs>

' Sensor defines 10 event properties backed by a single EventHandlerList.
' EventHandlerList is memory-efficient for classes with many events: it only
' allocates storage for events that have active subscribers.
Class Sensor

    ' <EventHandlerListField>
    Protected listEventDelegates As New EventHandlerList()
    ' </EventHandlerListField>

    ' <EventKeys>
    Shared ReadOnly temperatureChangedKey  As New Object()
    Shared ReadOnly humidityChangedKey     As New Object()
    Shared ReadOnly pressureChangedKey     As New Object()
    Shared ReadOnly batteryLowKey          As New Object()
    Shared ReadOnly signalLostKey          As New Object()
    Shared ReadOnly signalRestoredKey      As New Object()
    Shared ReadOnly thresholdExceededKey   As New Object()
    Shared ReadOnly calibrationRequiredKey As New Object()
    Shared ReadOnly dataReceivedKey        As New Object()
    Shared ReadOnly errorDetectedKey       As New Object()
    ' </EventKeys>

    ' <SingleEventProperty>
    Public Custom Event TemperatureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(temperatureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(temperatureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(temperatureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event
    ' </SingleEventProperty>

    Public Custom Event HumidityChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(humidityChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(humidityChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(humidityChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event PressureChanged As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(pressureChangedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(pressureChangedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(pressureChangedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event BatteryLow As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(batteryLowKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(batteryLowKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(batteryLowKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalLost As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalLostKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalLostKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalLostKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event SignalRestored As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(signalRestoredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(signalRestoredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(signalRestoredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ThresholdExceeded As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(thresholdExceededKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(thresholdExceededKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(thresholdExceededKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event CalibrationRequired As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(calibrationRequiredKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(calibrationRequiredKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(calibrationRequiredKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event DataReceived As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(dataReceivedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(dataReceivedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(dataReceivedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

    Public Custom Event ErrorDetected As EventHandler(Of SensorEventArgs)
        AddHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.AddHandler(errorDetectedKey, Value)
        End AddHandler
        RemoveHandler(Value As EventHandler(Of SensorEventArgs))
            listEventDelegates.RemoveHandler(errorDetectedKey, Value)
        End RemoveHandler
        RaiseEvent(sender As Object, e As SensorEventArgs)
            CType(listEventDelegates(errorDetectedKey), EventHandler(Of SensorEventArgs))?.Invoke(sender, e)
        End RaiseEvent
    End Event

End Class
' </EventProperties>