Comment effacer les abonnements aux événements en C #?
Question
Prenez la classe C # suivante:
c1 {
event EventHandler someEvent;
}
S'il existe de nombreux 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 délégués lambdas / 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.
La solution
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'appels vide, de manière efficace.
En dehors de la classe, vous ne pouvez pas faire cela - les événements exposent en gros "subscribe". et " se désabonner " et c'est tout.
Il est utile de savoir ce que font en réalité les événements de type champ: ils créent une variable et à la fois. Dans la classe, vous finissez par référencer la variable. Depuis l’extérieur, vous référencez l’événement.
Consultez mon article sur les événements et les délégués pour plus d'informations.
Autres conseils
Ajoutez une méthode à c1 pour définir "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
Définir l'événement sur null dans 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.
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.
}
Vous pouvez y parvenir en utilisant les méthodes Delegate.Remove ou Delegate.RemoveAll.
Commentaire ennuyeux conceptuel prolongé.
Je préfère utiliser le mot "gestionnaire d'événements". 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" s'appelle un " message " ou "signal", et même avoir un "message" mot-clé et 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épondre, 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 réponse.
Voici 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 à l'aide du modèle (new Foo ()) {/*...*/}
pour désabonner tous les membres de la liste d'appels. .
Supprimez tous les événements, en supposant qu'il s'agisse d'une "Action". type:
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}