Recherche…


Introduction

Un événement est une notification indiquant que quelque chose s'est produit (tel qu'un clic de souris) ou, dans certains cas, est sur le point de se produire (par exemple, une modification de prix).

Les classes peuvent définir des événements et leurs instances (objets) peuvent déclencher ces événements. Par exemple, un bouton peut contenir un événement Click qui est déclenché lorsqu'un utilisateur a cliqué dessus.

Les gestionnaires d'événements sont alors des méthodes appelées lorsque leur événement correspondant est déclenché. Un formulaire peut contenir un gestionnaire d'événements Clicked pour chaque bouton qu'il contient, par exemple.

Paramètres

Paramètre Détails
EventArgsT Le type qui dérive de EventArgs et contient les paramètres de l'événement.
Nom de l'événement Le nom de l'évènement.
HandlerName Le nom du gestionnaire d'événements.
SenderObject L'objet qui appelle l'événement.
EventArguments Une instance du type EventArgsT qui contient les paramètres d'événement.

Remarques

En levant un événement:

  • Vérifiez toujours si le délégué est null . Un délégué nul signifie que l'événement n'a pas d'abonnés. Augmenter un événement sans abonnés entraînera une NullReferenceException .
6,0
  • Copiez le délégué (par exemple, EventName ) dans une variable locale (par exemple, eventName ) avant de rechercher la nullité / la eventName l'événement. Cela évite les conditions de concurrence dans les environnements multithread:

Faux :

    if(Changed != null)      // Changed has 1 subscriber at this point
                             // In another thread, that one subscriber decided to unsubscribe
        Changed(this, args); // `Changed` is now null, `NullReferenceException` is thrown.

À droite :

    // Cache the "Changed" event as a local. If it is not null, then use
    // the LOCAL variable (handler) to raise the event, NOT the event itself.
    var handler = Changed;
    if(handler != null)
        handler(this, args);
6,0
  • Utilisez l'opérateur null-conditionnel (?.) Pour générer la méthode au lieu de vérifier par null le délégué des abonnés dans une instruction if : EventName?.Invoke(SenderObject, new EventArgsT());
  • Lorsque vous utilisez Action <> pour déclarer des types de délégué, la signature du gestionnaire de méthode / événement anonyme doit être identique au type de délégué anonyme déclaré dans la déclaration d'événement.

Déclarer et soulever des événements

Déclarer un événement

Vous pouvez déclarer un événement sur une class ou une struct utilisant la syntaxe suivante:

public class MyClass
{
    // Declares the event for MyClass
    public event EventHandler MyEvent;

    // Raises the MyEvent event
    public void RaiseEvent()
    {
        OnMyEvent();
    }
}    

Il existe une syntaxe étendue pour la déclaration des événements, dans laquelle vous détenez une instance privée de l'événement et définissez une instance publique à l'aide de la fonction add et set accessors. La syntaxe est très similaire aux propriétés C #. Dans tous les cas, la syntaxe illustrée ci-dessus doit être préférée, car le compilateur émet du code pour garantir que plusieurs threads peuvent ajouter et supprimer en toute sécurité des gestionnaires d'événements pour l'événement de votre classe.

Élever l'événement

6,0
private void OnMyEvent()
{
    EventName?.Invoke(this, EventArgs.Empty); 
}
6,0
private void OnMyEvent()
{
    // Use a local for EventName, because another thread can modify the
    // public EventName between when we check it for null, and when we
    // raise the event.
    var eventName = EventName;

    // If eventName == null, then it means there are no event-subscribers,
    // and therefore, we cannot raise the event.
    if(eventName != null)
        eventName(this, EventArgs.Empty);

}

Notez que les événements ne peuvent être déclenchés que par le type déclarant. Les clients ne peuvent s'inscrire / se désinscrire

Pour les versions C # antérieures à 6.0, où EventName?.Invoke n'est pas pris en charge, il est EventName?.Invoke d'attribuer l'événement à une variable temporaire avant l'appel, comme illustré dans l'exemple, qui garantit la sécurité des threads. code. Si vous ne le faites pas, une NullReferenceException peut être NullReferenceException dans certains cas où plusieurs threads utilisent la même instance d'objet. Dans C # 6.0, le compilateur émet un code similaire à celui montré dans l'exemple de code pour C # 6.

Déclaration d'événement standard

Déclaration d'événement:

public event EventHandler<EventArgsT> EventName;

Déclaration du gestionnaire d'événement:

public void HandlerName(object sender, EventArgsT args) { /* Handler logic */ }

S'abonner à l'événement:

Dynamiquement:

EventName += HandlerName;

Par le concepteur:

  1. Cliquez sur le bouton Événements dans la fenêtre des propriétés du contrôle (Éclair)
  2. Double-cliquez sur le nom de l'événement:

entrer la description de l'image ici

  1. Visual Studio générera le code de l'événement:
private void Form1_Load(object sender, EventArgs e)
{

}

Invoquer la méthode:

EventName(SenderObject, EventArguments);

Déclaration de gestionnaire d'événement anonyme

Déclaration d'événement:

public event EventHandler<EventArgsType> EventName;

Déclaration du gestionnaire d'événement utilisant l' opérateur lambda => et s'abonnant à l'événement:

EventName += (obj, eventArgs) => { /* Handler logic */ };

Déclaration du gestionnaire d'événement à l'aide de la syntaxe de la méthode de délégué anonyme:

EventName += delegate(object obj, EventArgsType eventArgs) { /* Handler Logic */ };

Déclaration et souscription d'un gestionnaire d'événement qui n'utilise pas le paramètre de l'événement et peut donc utiliser la syntaxe ci-dessus sans avoir à spécifier de paramètres:

EventName += delegate { /* Handler Logic */ }

Invoquer l'événement:

EventName?.Invoke(SenderObject, EventArguments);

Déclaration d'événement non standard

Les événements peuvent être de tout type délégué, pas seulement EventHandler et EventHandler<T> . Par exemple:

//Declaring an event
public event Action<Param1Type, Param2Type, ...> EventName;

Ceci est utilisé de manière similaire aux EventHandler standard:

//Adding a named event handler
public void HandlerName(Param1Type parameter1, Param2Type parameter2, ...) {
    /* Handler logic */
}
EventName += HandlerName;

//Adding an anonymous event handler
EventName += (parameter1, parameter2, ...) => { /* Handler Logic */ };

//Invoking the event
EventName(parameter1, parameter2, ...);

Il est possible de déclarer plusieurs événements du même type dans une seule instruction, similaire aux champs et aux variables locales (bien que cela puisse souvent être une mauvaise idée):

public event EventHandler Event1, Event2, Event3;

Cela déclare trois événements distincts ( Event1 , Event2 et Event3 ) tous de type EventHandler .
Remarque: Bien que certains compilateurs puissent accepter cette syntaxe dans les interfaces ainsi que dans les classes, la spécification C # (v5.0 §13.2.3) fournit une grammaire pour les interfaces qui ne l’autorise pas.

Création de EventArgs personnalisés contenant des données supplémentaires

Les événements personnalisés nécessitent généralement des arguments d'événement personnalisés contenant des informations sur l'événement. Par exemple, MouseEventArgs qui est utilisé par les événements de la souris tels que les événements MouseDown ou MouseUp , contient des informations sur l' Location ou les Buttons utilisés pour générer l'événement.

Lors de la création de nouveaux événements, pour créer un argument d'événement personnalisé:

  • Créez une classe dérivée de EventArgs et définissez les propriétés des données nécessaires.
  • Par convention, le nom de la classe doit se terminer par EventArgs .

Exemple

Dans l'exemple ci-dessous, nous créons un événement PriceChangingEventArgs pour la propriété Price d'une classe. La classe de données d'événement contient un CurrentPrice et un NewPrice . L'événement se déclenche lorsque vous attribuez une nouvelle valeur à la propriété Price et informe le consommateur que la valeur change et lui permet de connaître le prix actuel et le nouveau prix:

PriceChangingEventArgs

public class PriceChangingEventArgs : EventArgs
{
    public PriceChangingEventArgs(int currentPrice, int newPrice)
    {
        this.CurrentPrice = currentPrice;
        this.NewPrice = newPrice;
    }

    public int CurrentPrice { get; private set; }
    public int NewPrice { get; private set; }
}

Produit

public class Product
{
    public event EventHandler<PriceChangingEventArgs> PriceChanging;

    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(price, value);
            OnPriceChanging(e);
            price = value;
        }
    }

    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PriceChanging;
        if (handler != null)
            handler(this, e);
    }
}

Vous pouvez améliorer l'exemple en permettant au consommateur de modifier la nouvelle valeur, puis la valeur sera utilisée pour la propriété. Pour ce faire, il suffit d'appliquer ces changements de classes.

Changez la définition de NewPrice pour qu'elle soit NewPrice :

public int NewPrice { get; set; }

Modifiez la définition de Price pour utiliser e.NewPrice comme valeur de propriété, après avoir appelé OnPriceChanging :

int price;
public int Price
{
    get { return price; }
    set
    {
        var e = new PriceChangingEventArgs(price, value);
        OnPriceChanging(e);
        price = e.NewPrice;
    }
}

Créer un événement annulable

Un événement peut être annulé par une classe lorsqu'il est sur le point d'effectuer une action pouvant être annulée, telle que l'événement FormClosing d'un Form .

Pour créer un tel événement:

  • Créez un nouvel argument d'événement dérivant de CancelEventArgs et ajoutez des propriétés supplémentaires pour les données d'événement.
  • Créez un événement à l'aide de EventHandler<T> et utilisez la nouvelle classe d'argument event cancel que vous avez créée.

Exemple

Dans l'exemple ci-dessous, nous créons un événement PriceChangingEventArgs pour la propriété Price d'une classe. La classe de données d'événement contient une Value qui permet au consommateur de connaître le nouveau. L'événement se déclenche lorsque vous affectez une nouvelle valeur à la propriété Price et informe le consommateur que la valeur change et lui permet d'annuler l'événement. Si le consommateur annule l'événement, la valeur précédente de Price sera utilisée:

PriceChangingEventArgs

public class PriceChangingEventArgs : CancelEventArgs
{
    int value;
    public int Value
    {
        get { return value; }
    }
    public PriceChangingEventArgs(int value)
    {
        this.value = value;
    }
}

Produit

public class Product
{
    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(value);
            OnPriceChanging(e);
            if (!e.Cancel)
                price = value;
        }
    }

    public event EventHandler<PriceChangingEventArgs> PropertyChanging;
    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PropertyChanging;
        if (handler != null)
            PropertyChanging(this, e);
    }
}

Propriétés de l'événement

Si une classe génère un grand nombre d'événements, le coût de stockage d'un champ par délégué peut ne pas être acceptable. Le .NET Framework fournit des propriétés d'événement pour ces cas. De cette façon, vous pouvez utiliser une autre structure de données comme EventHandlerList pour stocker les délégués d’événement:

public class SampleClass 
{
    // Define the delegate collection.
    protected EventHandlerList eventDelegates = new EventHandlerList();

    // Define a unique key for each event.
    static readonly object someEventKey = new object();

    // Define the SomeEvent event property.
    public event EventHandler SomeEvent
    {
        add
        {
            // Add the input delegate to the collection.
            eventDelegates.AddHandler(someEventKey, value);
        }
        remove
        {
            // Remove the input delegate from the collection.
            eventDelegates.RemoveHandler(someEventKey, value);
        }
    }

    // Raise the event with the delegate specified by someEventKey
    protected void OnSomeEvent(EventArgs e)
    {
        var handler = (EventHandler)eventDelegates[someEventKey];
        if (handler != null)
            handler(this, e);
    }
}

Cette approche est largement utilisée dans les frameworks d’interface graphique comme WinForms, où les contrôles peuvent comporter des dizaines voire des centaines d’événements.

Notez que EventHandlerList n'est pas thread-safe, donc si vous prévoyez d'utiliser votre classe à partir de plusieurs threads, vous devrez ajouter des instructions de verrouillage ou un autre mécanisme de synchronisation (ou utiliser un stockage assurant la sécurité des threads).



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow