Partager via


Déclarer plusieurs événements à l’aide de propriétés d’événement

Lorsqu’une classe définit de nombreux événements à l’aide de déclarations d’événements de type champ, le compilateur génère un champ de stockage pour chacun d’eux. Chaque instance de cette classe alloue de la mémoire pour tous ces champs, même pour les événements qui ne sont jamais enregistrés. Avec de nombreux événements, cette surcharge par instance gaspille la mémoire.

Pour éviter cette surcharge de mémoire, utilisez les propriétés d’événement sauvegardées par une collection de délégués. La collection doit fournir des méthodes qui définissent, accèdent et récupèrent des délégués par clé d’événement. EventHandlerList est conçu à cet effet, mais vous pouvez également utiliser une Hashtable ou une classe dérivée de DictionaryBase. Conservez les détails de l’implémentation de la collection en privé.

Chaque propriété d’événement définit un accesseur d’ajout et un accesseur de suppression. L’accesseur d’ajout ajoute le délégué d’entrée à la collection de délégués, et l’accesseur de suppression le supprime. Les deux accesseurs utilisent une clé prédéfinie pour ajouter et supprimer des instances de la collection.

Prerequisites

Familiarisez-vous avec les concepts de l’article Événements .

Définir plusieurs événements à l’aide de propriétés d’événement

Ces étapes créent une Sensor classe qui expose 10 propriétés d’événement, toutes sauvegardées par un seul EventHandlerList.

  1. Définissez une classe de données d’événement qui hérite de EventArgs.

    Ajoutez des propriétés pour chaque élément de données que vous souhaitez transmettre au gestionnaire :

    public class SensorEventArgs(string sensorId, double value) : EventArgs
    {
        public string SensorId { get; } = sensorId;
        public double Value { get; } = value;
    }
    
    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
    
  2. Déclarez un EventHandlerList champ pour stocker les délégués :

    protected EventHandlerList listEventDelegates = new();
    
    Protected listEventDelegates As New EventHandlerList()
    
  3. Déclarez une clé unique pour chaque événement.

    EventHandlerList stocke les délégués par clé. Utilisez un static readonly objet (en Visual Basic) pour chaque clé :Shared ReadOnly l’identité de l’objet garantit que chaque clé est vraiment unique :

    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();
    
    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()
    
  4. Définissez chaque événement en tant que propriété d’événement avec des accesseurs d’ajout et de suppression personnalisés.

    Chaque accesseur appelle AddHandler ou RemoveHandler sur la liste en utilisant la clé de cet événement. En C#, ajoutez également une protected virtual méthode de levée qui récupère le délégué de la liste par clé et l’appelle. Dans Visual Basic, la Custom Event déclaration inclut déjà un RaiseEvent bloc qui effectue ce qui suit :

    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);
    
    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
    

    Répétez ce modèle pour chaque événement à l’aide de sa clé correspondante.

  5. Abonnez-vous à l’événement à l’aide de l’opérateur += (en Visual Basic) AddHandler:

    sensor.TemperatureChanged += Sensor_TemperatureChanged;
    
    AddHandler sensor.TemperatureChanged, AddressOf Sensor_TemperatureChanged
    
  6. Définissez le gestionnaire d’événements.

    La signature doit correspondre au EventHandler<TEventArgs> délégué—un object expéditeur et votre classe de données d’événement comme deuxième paramètre :

    static void Sensor_TemperatureChanged(object? sender, SensorEventArgs e) =>
        Console.WriteLine($"Sensor {e.SensorId}: temperature changed to {e.Value}.");
    
    Sub Sensor_TemperatureChanged(sender As Object, e As SensorEventArgs)
        Console.WriteLine("Sensor {0}: temperature changed to {1}.", e.SensorId, e.Value)
    End Sub
    

L’exemple suivant montre l’implémentation complète Sensor de la classe :

using System.ComponentModel;

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

// 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
{
    protected EventHandlerList listEventDelegates = new();

    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();

    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);

    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);
}
Imports System.ComponentModel

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

' 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

    Protected listEventDelegates As New EventHandlerList()

    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()

    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

    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