215 votes

Un gestionnaire d'événement a-t-il déjà été ajouté ?

Existe-t-il un moyen de savoir si un gestionnaire d'événement a été ajouté à un objet ? Je suis en train de sérialiser une liste d'objets dans/hors de l'état de session pour que nous puissions utiliser l'état de session basé sur SQL... Lorsqu'un objet de la liste voit une propriété modifiée, il doit être signalé, ce dont le gestionnaire d'événement s'occupait correctement auparavant. Cependant, lorsque les objets sont désérialisés, le gestionnaire d'événement n'est pas reçu.

Dans un accès de contrariété légère, j'ai juste ajouté le gestionnaire d'événement à la propriété Get qui accède à l'objet. Il est appelé maintenant, ce qui est génial, sauf qu'il est appelé environ 5 fois, donc je pense que le gestionnaire est ajouté à chaque fois que l'objet est accédé.

Il est suffisamment sûr pour être simplement ignoré, mais je préfère le rendre encore plus propre en vérifiant si le gestionnaire a déjà été ajouté afin de ne le faire qu'une seule fois.

Est-ce possible ?

EDIT : Je n'ai pas nécessairement le contrôle total des gestionnaires d'événements qui sont ajoutés, donc la simple vérification de null n'est pas suffisante.

0 votes

255voto

alfonso Points 8420

Je me suis récemment trouvé dans une situation similaire où je devais enregistrer un gestionnaire pour un événement une seule fois. J'ai découvert qu'il est possible de désenregistrer d'abord, puis d'enregistrer à nouveau, même si le gestionnaire n'est pas enregistré du tout :

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

Notez que si vous effectuez cette opération à chaque fois que vous enregistrez votre gestionnaire, celui-ci ne sera enregistré qu'une seule fois. Cela me semble être une bonne pratique :)

10 votes

Cela semble risqué ; si un événement est déclenché après avoir supprimé le gestionnaire et avant de le remettre en place, il sera manqué.

34 votes

Bien sûr. Vous voulez dire que ce n'est pas thread safe. Mais cela ne peut être un problème qu'en cas d'exécution de plusieurs threads ou autres, ce qui n'est pas habituel. Dans la plupart des cas, cela devrait suffire pour des raisons de simplicité.

0 votes

Je pense que c'est la meilleure façon de le faire, il suffit de s'entraîner à chaque fois que vous ajoutez ou soustrayez de toute façon, alors vous pouvez être plus sûr. Sinon, vous vous frapperez la tête contre le mur en découvrant que la même méthode est appelée deux fois.

138voto

Blair Conrad Points 56195

En dehors de la classe de définition, comme @Telos le mentionne, vous ne pouvez utiliser EventHandler que sur le côté gauche d'une classe += ou un -= . Ainsi, si vous avez la possibilité de modifier la classe de définition, vous pourriez fournir une méthode pour effectuer le contrôle en vérifiant si le gestionnaire d'événement est null - Si c'est le cas, alors aucun gestionnaire d'événement n'a été ajouté. Si ce n'est pas le cas, c'est peut-être le cas et vous pouvez parcourir en boucle les valeurs dans le champ Delegate.GetInvocationList . Si l'un d'eux est égal au délégué que vous voulez ajouter en tant que gestionnaire d'événement, alors vous savez qu'il est là.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

Et cela pourrait facilement être modifié pour devenir "ajouter le gestionnaire s'il n'est pas là". Si vous n'avez pas accès à l'intérieur de la classe qui expose l'événement, vous devrez peut-être explorer les possibilités suivantes -= y += comme suggéré par @Lou Franco.

Cependant, il serait peut-être préférable de réexaminer la façon dont vous mettez en service et déclassez ces objets, pour voir si vous ne pouvez pas trouver un moyen de suivre ces informations vous-même.

9 votes

Cela ne compile pas, EventHandler ne peut être qu'à gauche de += ou -=.

2 votes

Suppression du vote négatif après une explication supplémentaire. L'état SQL est en train de détruire l'idée entière ici... :(

1 votes

Merci Blair et SO recherche, juste ce que je cherchais (dommage que tu ne puisses pas le faire en dehors de la classe).

18voto

Lou Franco Points 48823

Si c'est le seul gestionnaire, vous pouvez vérifier si l'événement est nul, s'il ne l'est pas, le gestionnaire a été ajouté.

Je pense que vous pouvez sans risque appeler -= sur l'événement avec votre handler même s'il n'est pas ajouté (sinon, vous pourriez l'attraper) -- pour vous assurer qu'il n'est pas là avant de l'ajouter.

3 votes

Cette logique s'interrompra dès que l'événement sera traité ailleurs.

8voto

Jason Jackson Points 11563

Cet exemple montre comment utiliser la méthode GetInvocationList() pour récupérer les délégués de tous les gestionnaires qui ont été ajoutés. Si vous cherchez à savoir si un gestionnaire (fonction) spécifique a été ajouté, vous pouvez utiliser le tableau.

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

Vous pouvez examiner diverses propriétés de la propriété Method du délégué pour voir si une fonction spécifique a été ajoutée.

Si vous cherchez à savoir s'il n'y a qu'un seul attaché, vous pouvez simplement tester la nullité.

1 votes

GetInvocationList() n'est pas un membre de ma classe. En fait, je n'arrive pas à trouver cette méthode sur aucun objet ou handler auquel j'ai accès...

0 votes

J'ai aussi essayé, mais apparemment on ne peut accéder à l'événement que de l'intérieur de la classe. Je fais cela de manière générique, et comme d'autres l'ont mentionné, les gestionnaires d'événements sont probablement perdus de toute façon. Merci pour cette clarification !

5voto

CodeChef Points 622

Si je comprends bien votre problème, vous avez peut-être des problèmes plus importants. Vous avez dit que d'autres objets peuvent s'abonner à ces événements. Lorsque l'objet est sérialisé et désérialisé, les autres objets (ceux dont vous n'avez pas le contrôle) perdent leurs gestionnaires d'événements.

Si vous n'êtes pas inquiet à ce sujet, garder une référence à votre gestionnaire d'événement devrait suffire. En revanche, si vous craignez que d'autres objets perdent leur gestionnaire d'événements, vous devriez repenser votre stratégie de mise en cache.

1 votes

D'oh ! Je n'avais même pas pensé à cela... bien que cela aurait dû être évident vu que mon problème initial était que mon propre handler s'était perdu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X