2009-03-08 3 views
20

Je cherche à passer un événement à une fonction d'aide. Cette fonction va attacher une méthode à l'événement. Cependant, j'ai du mal à passer correctement l'événement. J'ai essayé de passer un EventHandler<TEventArgs>. Il compile, mais les événements ne sont pas attachés (mais sont toujours ajoutés, il semble qu'une copie du gestionnaire d'événement est faite).Comment puis-je transmettre un événement à une fonction en C#?

Par exemple, si j'ai ceci:

public event EventHandler<EventArgs> MyEvent; 

Et la fonction d'aide:

public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo) 
{ 
    eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); }; 
} 

Et l'appelant:

MyHelperFunction(MyEvent); 
MyEvent(null, new EventArgs()); // Does nothing. 
+0

@Strager: Pourriez-vous élaborer un peu sur les détails de la façon dont vous utilisiez cela? J'ai trouvé cette question très intéressante mais j'ai du mal à voir le cas d'utilisation. –

+0

@John Feminella, je créais des fonctions d'aide, et on attend de façon synchrone un événement. Ils sont principalement utilisés pour réduire la réutilisation de code pour plusieurs méthodes WaitFor (par exemple WaitForConnected) dans mes classes de mise en réseau (qui fonctionnent de manière asynchrone). – strager

Répondre

17

La raison pour laquelle cela ne fonctionne pas est + = lorsqu'elle est appliquée à un délégué crée un nouveau délégué qui est la combinaison de l'ancien et le nouveau. Cela ne modifie pas le délégué existant. Pour que cela fonctionne, vous devrez passer le délégué par référence.

public static void Helper(ref EventHandler<EventArgs> e) 
{ 
    e+= (x,y) => {}; 
} 

La raison pour laquelle cela fonctionne en dehors de la méthode est que le LHS est toujours le champ réel. So + = créera un nouveau délégué et assignera de nouveau au champ de membre.

+0

Intéressant. Donc je suppose que + = recrée le délégué (comme e = e + func). Merci de votre aide! J'ai passé plusieurs heures à essayer de déboguer mon code (en le blâmant sur le filetage) alors qu'en fait un événement n'était pas déclenché alors qu'il aurait dû l'être. – strager

+0

@strager, Lorsque vous rencontrez des situations comme celle-ci, c'est une bonne idée de sortir Reflector. Cela déconstruira certaines constructions syntaxiques trompeuses et vous montrera ce qui se passe réellement. – JaredPar

+4

Un problème avec l'utilisation de ref: J'obtiens l'erreur suivante si j'utilise Helper (ref myClassInstance.MyEvent): L'événement MyEvent ne peut apparaître que sur le côté gauche de + = ou - = (sauf s'il est utilisé dans le type MyClass) . Comment puis-je contourner cela? – strager

2

juste deviner: Avez-vous essayé le passer comme ref?

public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo) 

MyHelperFunction(ref MyEvent); 
1

J'ai une solution où j'ai deux interfaces. La première interface a des méthodes pour lier certains événements, tandis que l'autre interface a des méthodes d'événement qui peuvent être liées à ces événements.

Les méthodes de liaison de la première interface prennent comme paramètre la seconde interface, ce qui permet de lier les événements aux méthodes d'événement de n'importe quelle classe qui implémente la seconde interface.

Est-ce compréhensible, ou préférez-vous du code? :)

+1

Code, s'il vous plaît ... –

2

Ce n'est pas vraiment agréable, mais vous pouvez utiliser la réflexion pour le faire.

public EventMonitor(object eventObject, string eventName) 
    { 
     _eventObject = eventObject; 
     _waitEvent = eventObject.GetType().GetEvent(eventName); 

     _handler = new EventHandler(SetEvent); 
     _waitEvent.AddEventHandler(eventObject, _handler); 
    } 

Où événementObjet est l'objet contenant l'événement et nomEvènement est le nom de l'événement. SetEvent est votre gestionnaire d'événements.

J'ai aussi une méthode Éliminez comme ceci:

public void Dispose() 
    { 
     _waitEvent.RemoveEventHandler(_eventObject, _handler); 
    } 
+0

Avez-vous testé cela comme fonctionnel? – Kairan

+0

@Kairan - Beats moi. C'était il y a plus de 3 ans et demi. – Ray

1

Comme beaucoup l'ont souligné, le passage d'un événement à une méthode est pas possible ou pas simple.

  1. S'il vous plaît clarifier les choses, mais je soupçonne que l'usage prévu ressemblera à quelque chose comme:

    void Register() 
    { 
        var super = new SuperHandler(); 
    
        //not valid syntax: 
        super.HandleEvent(MyEvent1); 
        super.HandleEvent(MyEvent2); 
        super.HandleEvent(MyEvent3); 
        super.HandleEvent(MyEvent4); 
    } 
    

    Vous pouvez accomplir cela en faisant simplement vos gestionnaires d'événements génériques destinés accessibles au public (ou à l'intérieur, si vous le désirez) :

    public static class GenericHandler 
    { 
        public static void HandleAnyEvent(object sender, EventArgs e) 
        { 
         //handle 
        } 
    } 
    
    public class SomeClass 
    { 
        void RegisterEvents() 
        { 
         var r = new EventRaiser(); 
    
         r.ImportantThingHappened += GenericHandler.HandleAnyEvent; 
        } 
    } 
    

    dans cet exemple, mon fourre-tout gestionnaire est dans une classe statique, mais vous pouvez tout aussi bien utiliser une classe non statique. Aussi, je vois que dans votre exemple vous avez fait la méthode générique (TEventArgs). Étant donné que tous les dérivés EventHandler (tels que CancelEventHandler) correspondent à la classe EventHandler de base, vous n'avez pas besoin d'implémenter de génériques (ce qui ne serait pas utile).

  2. Si la logique d'enregistrement est complexe ou si vous devez garder le gestionnaire d'événements privé, vous pouvez utiliser Événements d'interface. Cela peut ne pas atteindre votre objectif de réduire la quantité de code, mais cela vous permettra de créer une classe capable de gérer de façon prévisible tous les événements d'un type spécifique.

    interface IRaiseEvents 
    { 
        event EventHandler ConnectionCreated; 
        event EventHandler ConnectionLost; 
    } 
    
    public class SuperHandler 
    { 
        void RegisterEvents(IRaiseEvents raiser) 
        { 
         raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected."); 
         raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected."); 
        } 
    } 
    
3

Tout est venu avec cette petite aide. Si c'est votre événement que vous avez créé vous-même, vous pouvez utiliser un wrapper comme celui-ci. Vous pouvez utiliser vos opérateurs + = pour attacher les gestionnaires comme d'habitude, mais pouvez passer l'encapsuleur et même élever l'événement d'ailleurs.

public class GenericEvent<T> where T:EventArgs 
{ 
    public event EventHandler<T> Source = delegate { }; 

    public void Raise(object sender, T arg = default(T)) 
    { 
     Source(sender, arg); 
    } 

    public void Raise(T arg = default(T)) 
    { 
     Source(this, arg); 
    } 

    public void AddHandler(EventHandler<T> handler) 
    { 
     Source += handler; 
    } 

    public void RemoveHandler(EventHandler<T> handler) 
    { 
     Source -= handler; 
    } 

    public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler) 
    { 
     genericEvent.AddHandler(handler); 
     return genericEvent; 
    } 
} 

Créer l'événement comme:

public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>(); 

Joindre les gestionnaires:

MyEvent += (s,e) => {}; 

Raise événement:

MyEvent.Raise(); 
+1

Une bonne classe simple. Pour le rendre plus complexe, je suggère d'utiliser une liste d'événements d'événements souscrits. Il se désinscrit de tous les événements souscrits, si l'objet GenericEvent est supprimé. Cela peut signifier que GenericEvent implémente l'interface IDisposable. –

1

passe quelque chose comme action e = e => monevenement + = e; Et appel de la méthode avec le gestionnaire? Il a l'avantage de travailler avec des classes .NET.

+0

Vous avez raison. C'est probablement ce que je ferais maintenant si je devais résoudre ce problème. =] J'ai posé la question il y a quatre ans (!) Quand j'avais peu de compréhension de C#. Merci d'avoir ajouté votre réponse, cependant; Je suis sûr que ce sera utile pour quelqu'un dans le futur! – strager