2009-03-08 15 views
20

Chciałbym przekazać zdarzenie do funkcji pomocnika. Ta funkcja spowoduje dołączenie metody do zdarzenia. Mam jednak problemy z prawidłowym przekazaniem zdarzenia. Próbowałem przekazać EventHandler<TEventArgs>. Kompiluje się, ale zdarzenia nie są dołączone (ale nadal są dodawane, wydaje się, że tworzona jest kopia procedury obsługi zdarzeń).Jak mogę przekazać zdarzenie do funkcji w C#?

Na przykład, jeśli mam to:

public event EventHandler<EventArgs> MyEvent; 

a funkcja pomocnika:

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

a dzwoniący:

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

@Strager: Czy mógłbyś trochę sprecyzować, w jaki sposób używasz tego? To pytanie było bardzo interesujące, ale mam problem z wyświetleniem tego przypadku. –

+0

@John Feminella, tworzyłem funkcje pomocnicze i jeden z nich czekał synchronicznie na wydarzenie. Są używane głównie w celu ograniczenia ponownego użycia kodu dla kilku metod WaitFor (np. WaitForConnected) w moich klasach sieciowych (które działały asynchronicznie). – strager

Odpowiedz

17

Powodem dlaczego to nie działa, jest + = po nałożeniu na delegata tworzy nowy delegata, który jest połączeniem starego i nowego. Nie modyfikuje istniejącego uczestnika.

Aby to zadziałało, musisz przekazać delegatowi przez odniesienie.

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

Powodem tego działania poza metodą jest to, że LHS jest nadal rzeczywistym polem. Więc + = utworzy nowego delegata i przypisze z powrotem pole członka.

+0

Interesujące. Więc myślę, że + = odtwarza delegata (jak e = e + func). Dzięki za pomoc! Spędziłem kilka godzin próbując debugować mój kod (obwiniając go za wątki), podczas gdy faktycznie zdarzenie nie zostało wystrzelone, kiedy powinno. – strager

+0

@strager, Gdy natrafisz na takie sytuacje, dobrze jest wytrzeć Reflektor. Spowoduje to odrzucenie niektórych mylących konstruktów składni i pokazanie, co się właściwie dzieje. – JaredPar

+4

Problem z użyciem polecenia ref: pojawia się następujący komunikat o błędzie, jeśli używam Helpera (ref myClassInstance.MyEvent): Zdarzenie MyEvent może pojawić się tylko po lewej stronie + = lub - = (z wyjątkiem sytuacji, gdy jest używane z typu MyClass) . Jak mogę obejść ten problem? – strager

2

tylko zgadywać: Czy próbowałeś przepuszczenie jako ref?

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

MyHelperFunction(ref MyEvent); 
1

Mam rozwiązanie, w którym mam dwa interfejsy. Pierwszy interfejs ma metody wiązania określonych zdarzeń, podczas gdy drugi interfejs ma metody zdarzeń, które można powiązać z tymi zdarzeniami.

Metody wiązania pierwszego interfejsu pobierają drugi interfejs jako parametr, który umożliwia powiązanie zdarzeń z metodami zdarzeń dowolnej klasy implementującej drugi interfejs.

Czy to zrozumiałe, czy też wolisz kod? :)

+1

Kod, proszę ... –

2

To niezbyt miłe, ale możesz użyć do tego celu refleksji.

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

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

Gdzie eventObject jest obiekt zawierający zdarzenie, a EventName to nazwa imprezy. SetEvent to program obsługi zdarzeń.

Mam też sposób rozporządzać takiego:

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

Testowałeś to jako działający? – Kairan

+0

@Kairan - Bije mnie. To było ponad 3 1/2 lat temu. – Ray

1

Jak wielu wskazał, przechodząc zdarzenie sposobu jest albo nie jest możliwe lub nie jest proste.

  1. Proszę wyjaśnić, ale podejrzewam, że Twój przeznaczone użytkowanie będzie wyglądać następująco:

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

    Można to osiągnąć jedynie poprzez planowaną rodzajowe obsługi zdarzeń dostępne publicznie (lub wewnętrznie, jeśli chcecie) :

    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; 
        } 
    } 
    

    w tym przykładzie moja catch-all Wózek jest w klasie statycznej, ale można równie dobrze użyć klasy non-statyczne. Widzę również, że w twoim przykładzie stworzyłeś metodę ogólną (TEventArgs). Ponieważ wszystkie pochodne EventHandler (takie jak CancelEventHandler) pasują do podstawowego EventHandler, nie trzeba włączać generycznych (ani nie byłoby to pomocne).

  2. Jeśli logika rejestracji jest złożona lub musisz przechowywać EventHandler jako prywatny, rozważ użycie zdarzeń interfejsu. Może to nie odpowiadać zamierzonemu celowi zmniejszenia ilości kodu, ale pozwoli ci utworzyć klasę, która może w sposób przewidywalny obsłużyć wszystkie zdarzenia określonego typu.

    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

Wystarczy wymyślił tego małego pomocnika. Jeśli jest to Twoje własne utworzone Wydarzenie, możesz użyć takiego otoki. Możesz użyć operatorów + = do załączania procedur obsługi jako normalnych, ale możesz przekazać opakowanie, a nawet podnieść wydarzenie z innego miejsca.

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; 
    } 
} 

Utwórz wydarzenie jak:

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

Attach teleskopowe: Event

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

podbicie:

MyEvent.Raise(); 
+1

Dobra prosta klasa. Aby było to bardziej złożone, sugerowałbym użycie listy zdarzeń subskrybowanych zdarzeń. Anuluje subskrypcję wszystkich subskrybowanych zdarzeń, jeśli obiekt GenericEvent zostanie usunięty. Może to oznaczać, że GenericEvent implementuje interfejs IDisposable. –

1

Przełęcz coś Action e = e => myevent + = e; A wywołanie metody z obsługą? Ma zaletę pracy z klasami .NET.

+0

Masz rację. Prawdopodobnie to bym teraz zrobił, gdybym musiał rozwiązać ten problem. =] Zadałem to pytanie cztery lata temu (!), Kiedy miałem mało zrozumienia C#. Dziękuję za dodanie Twojej odpowiedzi; Jestem pewien, że będzie to pomocne dla kogoś w przyszłości! – strager

Powiązane problemy