Rezygnacja z anonimowej metody w C#

Czy można zrezygnować z anonimowej metody Z wydarzenia?

Jeśli subskrybuję takie wydarzenie:

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

MyEvent += MyMethod;

Mogę zrezygnować z subskrypcji w ten sposób:

MyEvent -= MyMethod;

Ale jeśli subskrybuję metodą anonimową:

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

Czy można zrezygnować z tej anonimowej metody? Jeśli tak, to w jaki sposób?

Author: Eric, 2008-10-08

11 answers

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

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

Po prostu zachowaj odniesienie do delegata.

 203
Author: Jacob Krall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 19:50:55

Jedną z technik jest zadeklarowanie zmiennej przechowującej metodę anonimową, która byłaby dostępna wewnątrz samej metody anonimowej. Zadziałało to dla mnie, ponieważ pożądanym zachowaniem było wypisanie się po zakończeniu wydarzenia.

Przykład:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;
 137
Author: J c,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 15:34:51

Z pamięci, Specyfikacja jawnie nie gwarantuje zachowania w żaden sposób, jeśli chodzi o równoważność delegatów utworzonych za pomocą metod anonimowych.

Jeśli chcesz zrezygnować z subskrypcji, powinieneś użyć "normalnej" metody lub zatrzymać delegata w innym miejscu, aby móc zrezygnować z subskrypcji z dokładnie tym samym delegatem, którego użyłeś do subskrypcji.

 18
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 15:26:56

W 3.0 można skrócić do:

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
 17
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-23 01:53:51

Zamiast odwoływać się do dowolnego delegata, możesz użyć swojej klasy, aby przekazać listę wywołania zdarzenia rozmówcy. Zasadniczo możesz napisać coś takiego (zakładając, że MyEvent jest zadeklarowany wewnątrz MyClass):

public class MyClass 
{
  public event EventHandler MyEvent;

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

Możesz więc uzyskać dostęp do całej listy wywołań spoza MyClass i zrezygnować z subskrypcji dowolnego programu obsługi. Na przykład:

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

Napisałem pełny post o tym tecnique tutaj .

 9
Author: hemme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-23 23:11:50

Rodzaj lamerskiego podejścia:

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

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. nadpisuje metody dodawania/usuwania zdarzeń.
  2. Zachowaj listę tych procedur obsługi zdarzeń.
  3. w razie potrzeby wyczyść je wszystkie i ponownie dodaj pozostałe.

To może nie działać lub być najbardziej efektywną metodą, ale powinno wykonać zadanie.

 6
Author: casademora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 15:37:24

Ponieważ C# 7.0 local functions feature została wydana, podejście zasugerowane przez J c staje się naprawdę schludne.

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

Więc, szczerze mówiąc, nie masz anonimowej funkcji jako zmiennej tutaj. Ale przypuszczam, że motywacja do korzystania z niego w Twoim przypadku może być zastosowana do lokalnych funkcji.

 3
Author: mazharenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-15 05:30:06

Jeśli chcesz mieć możliwość kontrolowania wypisania się, musisz przejść trasę wskazaną w zaakceptowanej odpowiedzi. Jeśli jednak po prostu martwisz się o wyczyszczenie referencji, gdy twoja klasa subskrybująca wychodzi poza zakres, istnieje inne (nieco zawiłe) rozwiązanie, które wymaga użycia słabych referencji. Właśnie zamieściłem pytanie i odpowiedź na ten temat.

 2
Author: Benjol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:18

Jedno proste rozwiązanie:

Wystarczy przekazać zmienną eventhandle jako parametr do siebie. Zdarzenie jeśli masz przypadek, że nie możesz uzyskać dostępu do oryginalnej utworzonej zmiennej z powodu wielowątkowości, możesz użyć tego:

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
Author: Manuel Marhold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-12 14:11:21

Jeśli chcesz odnieść się do jakiegoś obiektu z tym delegatem, możesz użyć delegata.CreateDelegate (Type, Object target, MethodInfo methodInfo) . NET rozważ, że delegat jest równy przez target i methodInfo

 0
Author: user3217549,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-21 03:19:30

Jeśli najlepszym sposobem jest zachowanie odniesienia na subskrybowanym eventhandlerze, można to osiągnąć za pomocą słownika.

W tym przykładzie muszę użyć anonimowej metody, aby dołączyć parametr Mergecolumn dla zestawu DataGridViews.

Użycie metody mergecolumn z parametrem enable ustawionym na true włącza zdarzenie, podczas gdy używanie go z false wyłącza.

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
Author: Larry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-13 06:40:33