C# Language
Événements
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 uneNullReferenceException
.
- Copiez le délégué (par exemple,
EventName
) dans une variable locale (par exemple,eventName
) avant de rechercher la nullité / laeventName
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);
- 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
private void OnMyEvent()
{
EventName?.Invoke(this, EventArgs.Empty);
}
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:
- Cliquez sur le bouton Événements dans la fenêtre des propriétés du contrôle (Éclair)
- Double-cliquez sur le nom de l'événement:
- 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).