web-dev-qa-db-fra.com

Comment effacer les abonnements aux événements en C #?

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.

135
programmer

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.

174
Jon Skeet

Ajouter une méthode à c1 qui définira 'un événement' sur null ...

class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = null;}
}
29
programmer
class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = delegate{};}
}

Il vaut mieux utiliser delegate {} que null

7
Feng

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.
    }
6
Cary

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.

6

Vous pouvez y parvenir en utilisant les méthodes Delegate.Remove ou Delegate.RemoveAll.

5
Micah

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.

3
umlcat

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.

1
Jalal

Supprimez tous les événements, supposez qu'il s'agit d'un type "Action":

Delegate[] dary = TermCheckScore.GetInvocationList();

if ( dary != null )
{
    foreach ( Delegate del in dary )
    {
        TermCheckScore -= ( Action ) del;
    }
}
0
Googol