it-swarm.com.de

Anonyme Methode in C # abbestellen

Ist es möglich, eine anonyme Methode von einer Veranstaltung abzubestellen?

Wenn ich eine Veranstaltung wie diese abonniere:

void MyMethod()
{
    Console.WriteLine("I did it!");
}

MyEvent += MyMethod;

Ich kann mich so abmelden:

MyEvent -= MyMethod;

Wenn ich jedoch mit einer anonymen Methode abonniere:

MyEvent += delegate(){Console.WriteLine("I did it!");};

kann man diese anonyme Methode abbestellen? Wenn das so ist, wie?

203
Eric
Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

Behalten Sie einfach einen Hinweis auf den Delegierten.

208
Jacob Krall

Eine Technik besteht darin, eine Variable für die anonyme Methode zu deklarieren, die dann in der anonymen Methode selbst verfügbar wäre. Dies funktionierte für mich, weil das gewünschte Verhalten darin bestand, die Abmeldung abzubrechen, nachdem das Ereignis behandelt wurde.

Beispiel:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;
138
J c

Aus dem Arbeitsspeicher garantiert die Spezifikation das Verhalten in Bezug auf die Gleichwertigkeit von Delegaten, die mit anonymen Methoden erstellt wurden, ausdrücklich nicht.

Wenn Sie den Newsletter abbestellen müssen, sollten Sie entweder eine "normale" Methode verwenden oder den Delegaten an einem anderen Ort aufbewahren, damit Sie ihn mit genau demselben Delegaten abbestellen können, den Sie zum Abonnieren verwendet haben.

21
Jon Skeet

In 3.0 kann verkürzt werden auf:

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
17
yaniv

Anstatt einen Verweis auf einen Delegaten zu behalten, können Sie Ihre Klasse so instrumentieren, dass die Aufrufliste des Ereignisses an den Aufrufer zurückgegeben wird. Grundsätzlich können Sie so etwas schreiben (vorausgesetzt, MyEvent ist in MyClass deklariert):

public class MyClass 
{
  public event EventHandler MyEvent;

  public IEnumerable<EventHandler> GetMyEventHandlers()  
  {  
      return from d in MyEvent.GetInvocationList()  
             select (EventHandler)d;  
  }  
}

Sie können also von außerhalb von MyClass auf die gesamte Aufrufliste zugreifen und den gewünschten Handler abbestellen. Zum Beispiel:

myClass.MyEvent -= myClass.GetMyEventHandlers().Last();

Ich habe einen vollständigen Beitrag über diese Technik hier geschrieben.

9
hemme

Art von lahmem Ansatz:

public class SomeClass
{
  private readonly IList<Action> _eventList = new List<Action>();

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. Überschreiben Sie die Methoden zum Hinzufügen/Entfernen des Ereignisses.
  2. Behalten Sie eine Liste dieser Ereignishandler.
  3. Löschen Sie bei Bedarf alle und fügen Sie die anderen erneut hinzu.

Dies funktioniert möglicherweise nicht oder ist die effizienteste Methode, sollte jedoch die Arbeit erledigen.

6
casademora

Da C # 7.0 lokale Funktionen Feature veröffentlicht wurde, wird der Ansatz vorgeschlagen von J c wirklich ordentlich.

void foo(object s, MyEventArgs ev)
{
    Console.WriteLine("I did it!");
    MyEvent -= foo;
};
MyEvent += foo;

Ehrlich gesagt haben Sie hier keine anonyme Funktion als Variable. Ich denke jedoch, dass die Motivation, sie in Ihrem Fall einzusetzen, auf lokale Funktionen übertragen werden kann.

4
mazharenko

Wenn Sie die Abmeldung steuern möchten, müssen Sie die in Ihrer akzeptierten Antwort angegebene Route einschlagen. Wenn Sie jedoch nur daran interessiert sind, Referenzen zu löschen, wenn Ihre abonnierte Klasse den Gültigkeitsbereich verlässt, gibt es eine andere (etwas verschlungene) Lösung, bei der schwache Referenzen verwendet werden. Ich habe gerade eine Frage und Antwort zu diesem Thema gepostet.

2
Benjol

Eine einfache Lösung:

Übergeben Sie einfach die Eventhandle-Variable als Parameter an sich . Event Wenn Sie den Fall haben, dass Sie wegen Multithreading nicht auf die ursprünglich erstellte Variable zugreifen können, können Sie Folgendes verwenden:

MyEventHandler foo = null;
foo = (s, ev, mehi) => MyMethod(s, ev, foo);
MyEvent += foo;

void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance)
{
    MyEvent -= myEventHandlerInstance;
    Console.WriteLine("I did it!");
}
1
Manuel Marhold

Wenn der beste Weg ist, eine Referenz auf dem abonnierten eventHandler beizubehalten, kann dies mit einem Dictionary erreicht werden.

In diesem Beispiel muss ich eine anonyme Methode verwenden, um den Parameter mergeColumn für eine Gruppe von DataGridViews aufzunehmen.

Wenn Sie die MergeColumn-Methode mit dem Parameter enable auf true setzen, wird das Ereignis bei der Verwendung von false mit false deaktiviert.

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();

public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {

    if(enable) {
        subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
        dg.Paint += subscriptions[dg];
    }
    else {
        if(subscriptions.ContainsKey(dg)) {
            dg.Paint -= subscriptions[dg];
            subscriptions.Remove(dg);
        }
    }
}
0
Larry

wenn Sie mit diesem Delegaten auf ein Objekt verweisen möchten, können Sie Delegate.CreateDelegate (Typ, Objektziel, MethodInfo methodInfo)

0
user3217549