Как удалить обработчик события lambda

Возможные дубликаты:
Отменить анонимный метод в С#
Как мне отменить регистрацию & анонимный & rsquo; обработчик событий

Недавно я обнаружил, что я могу использовать lambdas для создания простых обработчиков событий. Например, я могу подписаться на событие click:

button.Click += (s, e) => MessageBox.Show("Woho");

Но как вы отмените подписку?

Ответ 1

Спецификация С# явно заявляет (IIRC), что если у вас есть две анонимные функции (анонимные методы или лямбда-выражения), она может или не может создавать равных делегатов из этого кода. (Два делегата равны, если они имеют равные цели и ссылаются на одни и те же методы.)

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

EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
...
button.Click -= handler;

(Я не могу найти соответствующий бит спецификации, но я был бы очень удивлен, увидев, что компилятор С# агрессивно пытается создать равных делегатов. Конечно, было бы неразумно полагаться на него.)

Если вы не хотите этого делать, вам нужно извлечь метод:

public void ShowWoho(object sender, EventArgs e)
{
     MessageBox.Show("Woho");
}

...

button.Click += ShowWoho;
...
button.Click -= ShowWoho;

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

EventHandler handler = null;
handler = (sender, args) =>
{
    button.Click -= handler; // Unsubscribe
    // Add your one-time-only code here
}
button.Click += handler;

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

button.Click += Delegates.AutoUnsubscribe<EventHandler>((sender, args) =>
{
    // One-time code here
}, handler => button.Click -= handler);

Даже это было бы сложно реализовать внутри Delegates.AutoUnsubscribe, потому что вам нужно было бы создать новый EventHandler (который был бы просто аргументом общего типа). Возможность, но грязная.