Подтвердить что ты не робот

Добавление и удаление анонимного обработчика событий

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

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

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

4b9b3361

Ответ 1

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

Как подписаться и отменить подписку на события

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

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

А также:

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

Ответ 2

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

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: В С# 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;
}

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

Вы можете использовать эту конструкцию:

    private void button_Click(object sender, RoutedEventArgs e)
    {
        RoutedEventHandler onclick;

        // subcribe
        b_second.Click += onclick = (ss, aa) => { /* Just do what you need */ };

        // unsubcribe
        b_second.Click -= onclick;
    }