2010-03-01 4 views
0

J'utilise une API basée sur les événements et je souhaite qu'une seule méthode de gestion des événements soit appelée une seule fois. (Notez que j'ai une autre méthode qui gère toujours l'événement, mais je veux ajouter un autre gestionnaire pour certaines situations et ne l'exécuter qu'une fois.)Comment lever un événement (en particulier avec un gestionnaire anonyme) une seule fois dans .NET?

Est-il possible/encouragé de se désabonner d'un événement dans le gestionnaire d'événements ? Par exemple.

private void OnEventRaised(object sender, EventArgs e) { 
    _eventRaisingObject.EventRaised -= OnEventRaised; 
    ... // Do normal code 
} 

De quel type de problèmes d'interconnexion dois-je m'inquiéter dans cette approche?

Deuxièmement, est-il possible d'appeler le gestionnaire d'événements une seule fois lorsqu'il s'agit d'une méthode anonyme? Par exemple.

_eventRaisingObject.EventRaised += (sender, e) => { 
    // Unsubscribe? 
    ... // Do normal code 
} 

Actuellement, je suis en utilisant une méthode anonyme et en vérifiant simplement un booléen, mais je pense que ce serait mieux (moins les frais généraux et les éventuels bugs) pour résilier votre abonnement lorsque le travail est fait - cette hypothèse est peut-être incorrecte. Un problème que je prévois avec la vérification d'un booléen est que, si la situation doit être répétée (pour que le gestionnaire d'événements anonymes soit à nouveau ajouté), le booléen partagé pourrait permettre aux méthodes anonymes d'être appelées plusieurs fois sur différents threads.

+0

Pourriez-vous ajouter un contexte au problème que vous essayez de résoudre? Je ne peux pas m'empêcher de penser qu'un modèle autre que l'abonnement à un événement pourrait mieux répondre à la facture. –

+0

@AdamRalph: Le contexte reçoit des paquets Ethernet et les analyse, mais a besoin d'analyser le paquet suivant avec une logique supplémentaire en de rares occasions. Peut-être qu'un autre modèle conviendrait mieux, mais je n'ai accès qu'aux événements soulevés par l'objet. Je ne veux pas avoir une machine d'état dans mon code d'analyse de paquets simplement pour traiter ce cas rare. – Pat

Répondre

2
EventHandler handler; 
handler = (sender, e) => { 
    _eventRaisingObject.EventRaised -= handler; 

    // Do normal code 
} 
_eventRaisingObject.EventRaised += handler; 

Fonctionne à cause des fermetures capturant à la fois handler et eventRaisingObject.

0

Vous pouvez convertir l'expéditeur en objet qui a déclenché l'événement et vous désinscrire.

void myRaisingObject_EventRaised(object sender, EventArgs e) 
{ 
    (sender as MyRaisingObject).EventRaised -= myRaisingObject_EventRaised; 
} 
+0

C'est une bonne chose à noter, mais ce n'est pas pertinent à ma question initiale. J'ai accès à l'objet qui déclenche l'événement, mais je veux savoir si c'est une pratique valide de se désinscrire depuis le gestionnaire et s'il est possible de le faire avec des méthodes anonymes. – Pat

1

La situation qui nécessite l'événement à traiter peut survenir plusieurs fois, et depuis le filetage est un problème, je recommande de mettre une plus grande connexion entre l'élevage et les conditions de manipulation.

Je ne voudrais pas ajouter et supprimer dynamiquement le gestionnaire d'événements. Cela peut certainement être fait dans le gestionnaire d'événements, mais il serait très difficile de le rendre robuste dans un environnement multithread.

Mais je n'utiliserais certainement pas de booléen pour cela, pour les mêmes raisons.

Vous pourriez envisager de créer une file d'attente. Mettre un élément dans la file d'attente pour signaler que le gestionnaire d'événements doit répondre à l'événement et retirer un élément de la file d'attente (avec un verrouillage correct des deux côtés, bien sûr) vous permet de mieux contrôler le processus .

Peut-être que cela ne fonctionne pas pour votre situation, mais c'est quelque chose à considérer.

1

La désinscription à l'intérieur du gestionnaire d'événements est possible, mais il existe une condition de concurrence explicite ici. Un autre thread peut également être en train d'appeler le gestionnaire d'événements. Obtenir le gestionnaire appelé après le désabonnement est tout à fait possible. Vous aurez besoin de sérialisation accès au délégué de l'événement:

public event EventHandler MyEvent; 
    private object eventLock = new object(); 
... 
    protected void OnMyEvent(EventArgs e) { 
    lock(eventLock) { 
     var handler = MyEvent; 
     if (handler != null) MyEvent(this, e); 
    } 
    } 

Ce n'est pas différent de protéger votre drapeau booléen, mais vous pouvez faire que beaucoup plus efficace avec la classe Interlocked.

Questions connexes