web-dev-qa-db-fra.com

Ajout et suppression d'un gestionnaire d'événements anonyme

Je me demandais si cela fonctionnait vraiment?

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é?

53
HaxElit

Il y a une page MSDN qui en parle:

Comment s'inscrire et se désinscrire des événements

A noter en particulier:

Si vous n'aurez pas à vous désabonner ultérieurement de [sic], vous pouvez utiliser l'opérateur d'affectation d'addition (+ =) 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 vous y abonner. Pour vous désinscrire dans ce scénario, il est nécessaire de revenir au code dans lequel vous vous abonnez à l'événement, de stocker la méthode anonyme dans une variable de délégué, puis d'ajouter le délégué à l'événement. En général, nous vous recommandons de ne pas utiliser de fonctions anonymes pour vous abonner à des événements si vous devez vous désabonner de l'événement à un moment ultérieur de votre code.

59
Ryan Lundy

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 fonctions locales 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;
}
12
sb.olofsson

Si vous devez vous désinscrire d'un gestionnaire d'événements, vous devrez avoir une référence définitive à un délégué concret. Regarder 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 crée (en gros) simplement un nouveau délégué "non anonyme" pour chaque délégué anonyme, même si les corps du délégué sont les mêmes. Pour cette raison, le framework ne trouvera pas le délégué à désinscrire lorsque vous utilisez l'exemple de code que vous avez donné.

6
Dan Herbert

Cela ne fonctionnera pas, je le crains, car les deux expressions lambda (et les délégués) que vous avez déclarées sont en fait des objets différents et renvoient 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) consiste simplement à refactoriser l'expression 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 à l'ajouter et à la supprimer, même si je ne suis personnellement pas fan de celle-ci. (C'est plus compliqué que de simplement créer une méthode normale, le cas échéant.)

3
Noldorin

Je ne pense pas que cela fonctionnera. Si vous devez vraiment vous désinscrire d'un événement, vous devez spécifier un gestionnaire d'événement explicite à partir duquel vous pourrez ultérieurement vous désinscrire au lieu d'un délégué anonyme.

2
Dan Auclair

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

1
Codism