Вопрос

Можно ли отказаться от подписки анонимным методом на событие?

Если я подпишусь на подобное мероприятие, как это:

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

MyEvent += MyMethod;

Я могу отменить подписку следующим образом:

MyEvent -= MyMethod;

Но если я подпишусь, используя анонимный метод:

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

можно ли отказаться от подписки этим анонимным методом?Если да, то каким образом?

Это было полезно?

Решение

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

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

Просто держите ссылку на делегата.

Другие советы

Один из методов заключается в объявлении переменной для хранения анонимного метода, которая затем была бы доступна внутри самого анонимного метода.Это сработало для меня, потому что желаемым поведением было отказаться от подписки после обработки события.

Пример:

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

Из памяти спецификация явно не гарантирует поведения в любом случае, когда речь идет об эквивалентности делегатов, созданных анонимными методами.

Если вам нужно отписаться, вы должны использовать " нормальный " или оставьте делегата в другом месте, чтобы вы могли отказаться от подписки точно таким же делегатом, который вы использовали для подписки.

В 3.0 можно сократить до:

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

Вместо того, чтобы хранить ссылку на любого делегата, вы можете использовать свой класс для передачи списка вызовов события вызывающей стороне. По сути, вы можете написать что-то вроде этого (при условии, что MyEvent объявлен внутри MyClass):

public class MyClass 
{
  public event EventHandler MyEvent;

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

Таким образом, вы можете получить доступ ко всему списку вызовов из-за пределов MyClass и отписаться от любого желаемого обработчика. Например:

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

Я написал полный пост об этой технике здесь .

Начиная с C # 7.0 Функция локальных функций была выпущена, подход предложен J c становится действительно аккуратным.

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

Итак, если честно, у вас нет анонимной функции в качестве переменной здесь. Но я полагаю, что мотивация использовать его в вашем случае может быть применена к локальным функциям.

Какой-то неудачный подход:

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

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
<Ол>
  • Переопределите событие, добавьте / удалите методы.
  • Храните список этих обработчиков событий.
  • При необходимости удалите их все и добавьте остальные.
  • Это может не сработать или быть наиболее эффективным методом, но оно должно быть выполнено.

    Если вы хотите контролировать отмену подписки, вам нужно пройти маршрут, указанный в принятом ответе. Однако, если вы просто заинтересованы в очистке ссылок, когда ваш подписывающийся класс выходит из области видимости, то есть другое (слегка запутанное) решение, которое включает использование слабых ссылок. Я только что опубликовал вопрос и ответ по этой теме.

    Одно простое решение:

    просто передайте переменную события как параметр самому себе. Событие, если у вас есть случай, когда вы не можете получить доступ к первоначально созданной переменной из-за многопоточности, вы можете использовать это:

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

    если вы хотите сослаться на некоторый объект с этим делегатом, возможно, вы можете использовать Delegate.CreateDelegate (Type, Object target, MethodInfo methodInfo) .net считает делегат равным по target и methodInfo

    Если лучший способ - сохранить ссылку на подписанный eventHandler, это можно сделать с помощью словаря.

    В этом примере мне нужно использовать анонимный метод, чтобы включить параметр mergeColumn для набора DataGridViews.

    Использование метода MergeColumn с параметром enable, установленным в true, включает событие, а при использовании false - отключает его.

    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);
            }
        }
    }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top