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é?
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.
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;
}
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é.
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.)
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.
Si vous vérifiez avec le document pour Delegate.Equality, vous découvrirez qu'ils ne sont pas comparés par référence.