Prenez la classe C # suivante:
c1 {
event EventHandler someEvent;
}
S'il y a beaucoup d'abonnements à l'événement someEvent
de c1
Et que je souhaite les effacer tous, quel est le meilleur moyen d'y parvenir? Notez également que les abonnements à cet événement pourraient être/sont des lambdas/délégués anonymes.
Actuellement, ma solution consiste à ajouter une méthode ResetSubscriptions()
à c1
Qui définit someEvent
sur null. Je ne sais pas si cela a des conséquences invisibles.
Dans la classe, vous pouvez définir la variable (masquée) sur null. Une référence nulle est la manière canonique de représenter une liste d'invocation vide, de manière efficace.
En dehors de la classe, vous ne pouvez pas faire cela - les événements exposent essentiellement "subscribe" et "unsubscribe", et c'est tout.
Il est utile de savoir ce que les événements de type champ sont en train de faire: ils créent une variable et un événement en même temps. Dans la classe, vous finissez par référencer la variable. De l'extérieur, vous référencez l'événement.
Voir mon article sur les événements et les délégués pour plus d'informations.
Ajouter une méthode à c1 qui définira 'un événement' sur null ...
class c1
{
event EventHandler someEvent;
ResetSubscriptions() {someEvent = null;}
}
class c1
{
event EventHandler someEvent;
ResetSubscriptions() {someEvent = delegate{};}
}
Il vaut mieux utiliser delegate {} que null
La meilleure pratique pour effacer tous les abonnés est de définir la someEvent sur null en ajoutant une autre méthode publique si vous souhaitez exposer cette fonctionnalité à l'extérieur. Cela n'a aucune conséquence invisible. La condition préalable est de ne pas oublier de déclarer SomeEvent avec le mot clé 'event'.
Veuillez consulter le livre - C # 4.0 en bref, page 125.
Quelqu'un ici a proposé d'utiliser la méthode Delegate.RemoveAll
. Si vous l'utilisez, l'exemple de code peut suivre le formulaire ci-dessous. Mais c'est vraiment stupide. Pourquoi ne pas simplement SomeEvent=null
Dans la fonction ClearSubscribers()
?
public void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null.
}
Définir l'événement sur null à l'intérieur de la classe fonctionne. Lorsque vous supprimez une classe, vous devez toujours définir l'événement sur null, le GC a des problèmes avec les événements et risque de ne pas nettoyer la classe supprimée si elle comporte des événements en suspens.
Vous pouvez y parvenir en utilisant les méthodes Delegate.Remove ou Delegate.RemoveAll.
Commentaire ennuyeux conceptuel étendu.
J'utilise plutôt le mot "gestionnaire d'événement" au lieu de "événement" ou "délégué". Et utilisé le mot "événement" pour d'autres choses. Dans certains langages de programmation (VB.NET, Object Pascal, Objective-C), "événement" est appelé "message" ou "signal" et comporte même un mot clé "message" et une syntaxe de sucre spécifique.
const
WM_Paint = 998; // <-- "question" can be done by several talkers
WM_Clear = 546;
type
MyWindowClass = class(Window)
procedure NotEventHandlerMethod_1;
procedure NotEventHandlerMethod_17;
procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
procedure DoClearEventHandler; message WM_Clear;
end;
Et, pour répondre à ce "message", un "gestionnaire d'événements" répond, qu'il s'agisse d'un seul délégué ou de plusieurs délégués.
Résumé: "événement" est la "question", "gestionnaire (s) d'événements" sont la ou les réponses.
Ceci est ma solution:
public class Foo : IDisposable
{
private event EventHandler _statusChanged;
public event EventHandler StatusChanged
{
add
{
_statusChanged += value;
}
remove
{
_statusChanged -= value;
}
}
public void Dispose()
{
_statusChanged = null;
}
}
Vous devez appeler Dispose()
ou utiliser using(new Foo()){/*...*/}
pattern pour vous désabonner de tous les membres de la liste d'appels.
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}