2010-01-12 5 views
46

Je me demandais si cela fonctionnait réellement?Ajout et suppression de gestionnaire d'événements anonymes

private void RegisterKeyChanged(T item) 
{ 
    item.OnKeyChanged += (o, k) => ChangeItemKey((T)o, k); 
} 

private void UnRegisterKeyChanged(T item) 
{ 
    item.OnKeyChanged -= (o, k) => ChangeItemKey((T)o, k); 
} 

Comment le compilateur sait-il que les gestionnaires d'événements sont identiques? Est-ce même recommandé?

+0

double possible de [désinscrire méthode anonyme en C#] (https://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c-sharp) –

Répondre

55

Il y a une page MSDN qui parle de ceci:

How to Subscribe to and Unsubscribe from Events

note en particulier:

Si vous ne devez résilier votre abonnement à [sic] un événement plus tard, vous pouvez utilisez l'opérateur d'affectation d'ajout (+ =) à pour attacher une méthode anonyme à l'événement .

Et aussi:

Il est important de noter que vous ne pouvez pas vous désabonner facilement d'un événement si vous avez utilisé une fonction anonyme pour y souscrire. Pour désabonnement dans ce scénario, il est nécessaire de revenir au code où vous êtes abonné à l'événement, stocker la méthode anonyme dans une variable délégué , puis ajoutez le délégué à l'événement. En général, nous recommandons que vous n'utilisez pas les fonctions anonymes pour vous abonner aux événements si vous devrez vous désinscrire de l'événement à un moment ultérieur dans votre code .

+0

Cela signifie-t-il que ce qui est dans OP ne fonctionne pas comme prévu? Cela signifie-t-il que le délégué anonyme, s'il n'est pas stocké au point d'abonnement, est presque impossible à désabonner? –

+0

Évidemment, oui. –

1

Si vous vérifiez avec le document pour Delegate.Equality, vous découvrirez qu'ils ne sont pas comparés par référence.

3

Je ne crois pas que cela fonctionnera. Si vous avez vraiment besoin de vous désinscrire à partir d'un événement, vous devez spécifier un gestionnaire d'événements explicite dont vous pouvez vous désinscrire ultérieurement au lieu d'un délégué anonyme.

2

Cela ne marchera pas J'ai peur, puisque les deux expressions lambda (et délégués) que vous avez déclarées sont en fait des objets différents, et retournent des références différentes. Par conséquent, la suppression du gestionnaire (-=) échouera toujours.

La solution courante à ce problème (où vous devez supprimer le gestionnaire) est simplement de refactoriser l'expression de lamba en une méthode appropriée. Une alternative consiste à conserver une variable de classe pour le délégué du gestionnaire d'événements, et à ajouter et supprimer ce dernier, bien que je n'en sois personnellement pas fan. (C'est plus compliqué que de simplement créer une méthode normale, le cas échéant.)

+0

Remarque, il y a une question assez similaire et une réponse à , où Reed Copsey suggère la même solution que je fais . – Noldorin

5

Si vous devez vous désabonner d'un gestionnaire d'événements, vous devez avoir une référence précise à un délégué concret. En regardant Delegate.Equality, vous constaterez que les délégués ne sont pas seulement comparés en utilisant l'égalité de référence, mais cela n'a pas d'importance pour les délégués anonymes.

Pour un délégué anonyme, le compilateur (fondamentalement) crée juste un nouveau délégué "non anonyme" pour chaque délégué anonyme, même si les corps de délégué sont les mêmes. Pour cette raison, le framework ne trouvera pas le délégué à désabonner lorsque vous utiliserez l'exemple de code que vous avez donné.

8

Pour toute personne intéressée, vous pouvez ajouter et supprimer un gestionnaire d'événements anonyme comme celui-ci

public class Musician 
{ 
    public void TuneGuitar() 
    { 
     Metronome metronome = new Metronome(); 

     EventHandler<EventArgs> handler = null; 
     handler = (sender, args) => 
     { 
      // Tune guitar 
      // ... 

      // Unsubscribe from tick event when guitar sound is perfect 
      metronome.Tick -= handler; 
     }; 

     // Attach event handler 
     metronome.Tick += handler; 
    } 
} 

public class Metronome 
{ 
    event EventHandler<EventArgs> Tick; 
} 

MISE À JOUR: En C# 7.0 nous avons des supports pour local functions donc la méthode TuneGuitar peut maintenant être écrite comme:

public void TuneGuitar() 
{ 
    Metronome metronome = new Metronome(); 

    void handler = (object sender, EventArgs args) => 
    { 
     // Tune guitar 
     // ... 

     // Unsubscribe from tick event when guitar sound is perfect 
     metronome.Tick -= handler; 
    }; 

    // Attach event handler 
    metronome.Tick += handler; 
} 
Questions connexes