Добавление и удаление анонимного обработчика событий - PullRequest
53 голосов
/ 12 января 2010

Мне было интересно, сработало ли это на самом деле?

private void RegisterKeyChanged(T item) 
{
    item.OnKeyChanged += (o, k) => ChangeItemKey((T)o, k);
}

private void UnRegisterKeyChanged(T item) 
{
    item.OnKeyChanged -= (o, k) => ChangeItemKey((T)o, k);
}

Как компилятор узнает, что обработчики событий одинаковы? Это даже рекомендуется?

Ответы [ 6 ]

59 голосов
/ 12 января 2010

Есть страница MSDN, которая говорит об этом:

Как подписаться на события и отписаться от них

Примечание, в частности:

Если вам не придется отписываться от [sic] событие позже, вы можете использовать оператор присваивания (+ =) для прикрепить анонимный метод к событие.

А также:

Важно отметить, что вы не может легко отписаться от событие, если вы использовали анонимный функция подписки на него. к отписаться в этом сценарии, это необходимо вернуться к коду где Вы подписываетесь на событие, хранить анонимный метод в делегате переменной, а затем добавьте делегат событие . В общем, мы рекомендуем что вы не используете анонимный функции подписки на события, если вам придется отписаться от событие в какой-то более поздний момент в вашем код.

12 голосов
/ 10 июня 2015

Для всех, кто заинтересован, вы можете добавить и удалить анонимный обработчик событий, как этот

public class Musician
{
    public void TuneGuitar()
    {
        Metronome metronome = new Metronome();

        EventHandler<EventArgs> handler = null;
        handler = (sender, args) =>
        {
            // Tune guitar
            // ...

            // Unsubscribe from tick event when guitar sound is perfect
            metronome.Tick -= handler;
        };

        // Attach event handler
        metronome.Tick += handler;
    }
}

public class Metronome
{
    event EventHandler<EventArgs> Tick;
}

UPDATE: В C # 7.0 у нас есть поддержка локальных функций , поэтому метод TuneGuitar теперь можно записать в виде:

public void TuneGuitar()
{
    Metronome metronome = new Metronome();

    void handler = (object sender, EventArgs args) =>
    {
        // Tune guitar
        // ...

        // Unsubscribe from tick event when guitar sound is perfect
        metronome.Tick -= handler;
    };

    // Attach event handler
    metronome.Tick += handler;
}
6 голосов
/ 12 января 2010

Если вам нужно отписаться от обработчика событий, вам нужно иметь определенную ссылку на конкретного делегата. Глядя на Delegate.Equality, вы обнаружите, что делегаты не просто сравниваются с использованием равенства ссылок, однако это не имеет значения для анонимных делегатов.

Для анонимного делегата компилятор (в основном) просто создает новый «неанонимный» делегат для каждого анонимного делегата, даже если тела делегатов одинаковы. Из-за этого фреймворк не найдет делегата для отмены подписки при использовании примера кода, который вы дали.

3 голосов
/ 12 января 2010

Боюсь, это не сработает, поскольку два лямбда-выражения (и делегата), которые вы объявили, на самом деле разные объекты и возвращают разные ссылки. Следовательно, удаление обработчика (-=) всегда будет неудачным.

Распространенным решением этой проблемы (где вам нужно удалить обработчик) является простой рефакторинг выражения lamba в правильный метод. Альтернативой является сохранение переменной класса для делегата обработчика событий, добавление и удаление этого, хотя я лично не фанат этого. (Это больше хлопот, чем просто создание нормального метода, если что-нибудь.)

2 голосов
/ 12 января 2010

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

1 голос
/ 12 января 2010

Если вы свяжетесь с документом для Delegate.Equality, вы обнаружите, что они не сравниваются по ссылке.

...