2013-05-24 19 views
8

Co chcę zrobić, to zasadniczo usunąć funkcję ze zdarzenia, nie znając nazwy funkcji.Jak usunąć siebie z obsługi zdarzeń?

Mam FileSystemWatcher. Jeśli plik zostanie utworzony/zmieniony, sprawdza jego nazwę. Jeśli pasuje, przenosi go do określonej lokalizacji. Jednak jeśli plik jest zablokowany, tworzy lambdę, która dołącza się do zdarzenia znacznika czasu, czekając, aż plik nie zostanie zablokowany. Gdy nie jest, przesuwa plik, a następnie usuwa się z obsługi zdarzenia. Widziałem wiele sposobów, aby to zrobić, jak utrzymywanie instancji lub tworzenie nazwanej metody. Nie mogę zrobić żadnego z nich tutaj. Jakie są moje opcje?

+0

Istnieje kilka wzorów, które obecnie nie pozwalają zapisać się do wydarzeń w sprzężonym * * loosley sposób. Sprawdź [to] (http://martinfowler.com/eaaDev/EventAggregator.html). Opisuje sposób zasubskrybowania/wypisania się z agregatora zdarzeń (w tym przypadku dotyczy on ekranów). Rzeczywisty agregator tworzy odwołania między obiektami. Istnieje wiele różnych implementacji. Ten, który preferuję, jest zaimplementowany w [Caliburn.Micro] (http://www.mindscapehq.com/blog/index.php/2012/02/01/caliburn-micro-part-4-the-event-aggregator/) . –

Odpowiedz

19

Nie ma prostej metody, aby to osiągnąć.

Preferowane podejście:

ja nie rozumiem, dlaczego nie można zapisać delegata. Nie musisz zapisywać instancji jako pola. Może to być lokalna zmienna, która jest opanowana przez anonimowego obsługi zdarzeń:

EventHandler<TypeOfEventArgs> handler = null; 
handler = (s, e) => 
{ 
    // Do whatever you need to do here 

    // Remove event: 
    foo.Event -= handler; 
} 

foo.Event += handler; 

Nie mogę myśleć o jednym scenariuszu, gdzie nie można używać.

Alternatywne podejście bez zapisywania delegata:

Jednakże, jeśli masz taki scenariusz, to się dość trudne.
Musisz znaleźć delegata, który został dodany jako przewodnik do zdarzenia. Ponieważ go nie zapisałeś, dość trudno jest go uzyskać. Nie ma potrzeby uzyskiwania delegata aktualnie wykonywanej metody.

Nie można używać GetInvocationList() na razie obaj, ponieważ dostęp do zdarzenia poza klasą jest zdefiniowany w ogranicza się do dodawania i usuwania teleskopowe, tj += i -=.

Utworzenie nowego uczestnika nie jest również możliwe. Chociaż można uzyskać dostęp do obiektu MethodInfo definiującego metodę anonimową, nie można uzyskać dostępu do instancji klasy, dla której została zadeklarowana metoda. Ta klasa jest generowana automatycznie przez kompilator, a wywołanie metody anonimowej powoduje zwrócenie wartości instancja klasy, którą twoja normalna metoda jest zdefiniowana w.

Jedynym sposobem, jaki udało mi się znaleźć, jest znalezienie pola - jeśli takie jest -, które zdarzenie używa i zadzwoń pod numer GetInvocationList(). Poniższy kod demonstruje to z manekina klasy:

void Main() 
{ 
    var foo = new Foo(); 
    foo.Bar += (s, e) => { 
     Console.WriteLine("Executed"); 

     var self = new StackFrame().GetMethod(); 
     var eventField = foo.GetType() 
          .GetField("Bar", BindingFlags.NonPublic | 
              BindingFlags.Instance); 
     if(eventField == null) 
      return; 
     var eventValue = eventField.GetValue(foo) as EventHandler; 
     if(eventValue == null) 
      return; 
     var eventHandler = eventValue.GetInvocationList() 
            .OfType<EventHandler>() 
            .FirstOrDefault(x => x.Method == self) 
           as EventHandler; 
     if(eventHandler != null) 
      foo.Bar -= eventHandler; 
    }; 

    foo.RaiseBar(); 
    foo.RaiseBar(); 
} 

public class Foo 
{ 
    public event EventHandler Bar; 
    public void RaiseBar() 
    { 
     var handler = Bar; 
     if(handler != null) 
      handler(this, EventArgs.Empty); 
    } 
} 

Należy pamiętać, że ciąg "Bar" który jest przekazywany do GetField musi być dokładna nazwa pola , który jest używany przez zdarzenie. Powoduje to dwa problemy:

  1. Pole może mieć różną nazwę, np. podczas korzystania z jawnej implementacji zdarzenia. Musisz ręcznie znaleźć nazwę pola.
  2. Może nie być żadnego pola. Dzieje się tak, jeśli zdarzenie korzysta z jawnej implementacji zdarzenia i po prostu przekazuje je innemu zdarzeniu lub przechowuje delegatów w inny sposób.

Wniosek:

Alternatywne podejście polega na szczegóły wykonania, więc nie należy go używać, jeśli można tego uniknąć.

+0

Zapomniałem o zamknięciach! Whoops! Dzięki za odpowiedź, poszłaś nawet dalej niż ja. –

+0

Myślę, że to trochę niesprawiedliwe, że napisałeś negatywny komentarz do odpowiedzi Mrocznego Falcona, który poprzedza twoją, a następnie dałeś zasadniczo tę samą radę w swoim * preferowanym * podejściu. –

+0

@BenVoigt Cóż, jego komentarz był słuszny, technicznie nie jest to odpowiedź, nawet jeśli jest to możliwe, jest to preferowane rozwiązanie. Zauważ też, że nie podzielił odpowiedzi Mrocznej, tylko skomentował, że nie spełnia on technicznie podanych kryteriów. Inna kluczowa różnica polega na tym, że Daniel uwzględnił rozwiązanie, które * spełnia * kryteria określone w pytaniu. – Servy

0

kroki, aby usunąć obsługi zdarzeń z wyrażeniem lambda:

public partial class Form1 : Form 
{ 
    private dynamic myEventHandler; 
    public Form1() 
    { 
     InitializeComponent(); 
    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 
     myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there")); 
     this.button1.Click += myEventHandler; 
    } 

    private void button1_Click(object sender, EventArgs e, string additionalInfo) 
    { 
     MessageBox.Show(additionalInfo); 
     button1.Click -= myEventHandler; 
    } 
} 
Powiązane problemy