2013-05-24 6 views
8

Was ich tun möchte, ist im Grunde eine Funktion aus einem Ereignis zu entfernen, ohne den Namen der Funktion zu kennen.Wie entfernen Sie sich von einem Event-Handler?

Ich habe eine FileSystemWatcher. Wenn eine Datei erstellt/umbenannt wird, überprüft sie ihren Namen. Wenn es übereinstimmt, wird es an einen bestimmten Ort verschoben. Wenn die Datei jedoch gesperrt ist, erzeugt sie ein Lambda, das an das Tick-Ereignis eines Timers angehängt wird und wartet, bis die Datei nicht gesperrt ist. Wenn dies nicht der Fall ist, wird die Datei verschoben und anschließend aus dem Ereignishandler entfernt. Ich habe viele Möglichkeiten gesehen, wie ich die Instanz beibehalten oder eine benannte Methode erstellen kann. Ich kann keinen von denen hier tun. Was sind meine Möglichkeiten?

+0

Es gibt ein paar Muster ist da draußen, die Sie auf Ereignisse in einem * Loosley gekoppelt * Podcatcher ermöglichen. Schauen Sie sich [this] an (http://martinfowler.com/eaaDev/EventAggregator.html). Es beschreibt, wie man einen Event-Aggregator abonniert/abbestellt (in diesem Fall handelt es sich um Bildschirme). Der eigentliche Aggregator erstellt die Referenzen zwischen Objekten. Es gibt viele verschiedene Implementierungen. Die von mir bevorzugte Version ist in [Caliburn.Micro] implementiert (http://www.mindscapehq.com/blog/index.php/2012/02/01/caliburn-micro-part-4-the-event-aggregator/) . –

Antwort

19

Es gibt keine einfache Methode, dies zu erreichen.

Preferred Ansatz:

Ich sehe nicht, warum Sie nicht den Delegierten speichern. Sie müssen die Instanz nicht als ein Feld speichern. Es kann eine lokale Variable sein, die von Ihrer anonymen Ereignisbehandlungsroutine erfaßt wird:

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

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

foo.Event += handler; 

ich nicht von einem einzigen Szenario denken kann, wo Sie diese nicht nutzen können.

Alternative Ansatz ohne Speichern der Delegat:

Wenn Sie jedoch ein solches Szenario haben, ist es ziemlich schwierig zu bekommen.
Sie müssen den Delegaten finden, der dem Ereignis als Handler hinzugefügt wurde. Weil Sie es nicht gespeichert haben, ist es ziemlich schwer, es zu erhalten. Es gibt keine this, um einen Delegaten der aktuell ausgeführten Methode abzurufen.

Sie können nicht GetInvocationList() auf den Fall entweder verwenden, da ein Ereignis außerhalb der Klasse Zugriff auf sie in beschränkt sich auf das Hinzufügen und Entfernen Handler definiert wird, das heißt += und -=.

Das Erstellen eines neuen Delegaten ist ebenfalls nicht möglich. Während Sie Zugriff auf das Objekt MethodInfo erhalten können, das Ihre anonyme Methode definiert, können Sie keinen Zugriff auf die Instanz der Klasse erhalten, in der diese Methode deklariert ist. Diese Klasse wird automatisch vom Compiler generiert und der Aufruf this innerhalb der anonymen Methode gibt das zurück Instanz der Klasse, in der Ihre normale Methode definiert ist.

Der einzige Weg, den ich gefunden habe, ist zu finden, das Feld - falls vorhanden - dass das Ereignis verwendet und GetInvocationList() darauf aufrufen. Der folgende Code zeigt dies mit einer Dummy-Klasse:

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

Bitte beachten Sie, dass die Zeichenfolge "Bar", den GetField übergeben wird der genaue Name des Feldes sein muss, die durch das Ereignis verwendet wird. Dies führt zu zwei Problemen:

  1. Das Feld kann anders benannt werden, z. bei Verwendung einer expliziten Ereignisimplementierung. Sie müssen den Feldnamen manuell herausfinden.
  2. Möglicherweise gibt es kein Feld. Dies passiert, wenn das Ereignis eine explizite Ereignisimplementierung verwendet und nur an ein anderes Ereignis delegiert oder die Delegierten auf andere Weise speichert.

Fazit:

Der alternative Ansatz stützt sich auf Details der Implementierung, so verwenden Sie es nicht, wenn Sie es vermeiden können.

+0

Ich habe die Schließungen vergessen! Hoppla! Danke für die Antwort, du bist sogar noch weiter gegangen als ich. –

+0

Ich denke, es ist ein wenig unfair, dass du einen negativen Kommentar zu Dark Falcon 's Antwort verfasst hast, der deiner Vergangenheit vorausging, und dann im Wesentlichen denselben Ratschlag in deinem * bevorzugten * Ansatz gegeben hast. –

+0

@BenVoigt Nun, sein Kommentar war richtig, es ist technisch keine Antwort, auch wenn es möglich ist, ist es die bevorzugte Lösung. Beachten Sie auch, dass er Darks Antwort nicht abgelehnt hat, sondern nur kommentiert hat, dass es die angegebenen Kriterien nicht erfüllt. Ein weiterer wichtiger Unterschied besteht darin, dass Daniel eine Lösung einschloss, die die Kriterien in der Frage * erfüllt *. – Servy

0

Steps Ereignishandler mit Lambda-Ausdruck zu entfernen:

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