Использование лямбда-выражений для обработчиков событий

114

В настоящее время у меня есть страница, которая объявлена ​​следующим образом:

public partial class MyPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //snip
        MyButton.Click += (o, i) =>
        {
            //snip
        }
    }
}

Я только недавно перешел на .NET 3.5 с 1.1, поэтому я привык писать обработчики событий вне Page_Load. У меня вопрос; есть ли какие-либо недостатки в производительности или подводные камни, на которые следует обратить внимание при использовании лямбда-метода для этого? Я предпочитаю его, поскольку он, безусловно, более лаконичен, но я не хочу жертвовать производительностью, чтобы использовать его. Спасибо.

Кристофер Гарсия
источник

Ответы:

117

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

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

public partial class MyPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //snip
        MyButton.Click += new EventHandler(delegate (Object o, EventArgs a) 
        {
            //snip
        });
    }
}
Эндрю Хэйр
источник
Понимаю. Итак, нет ли недостатка в наличии этих обработчиков внутри Page_Load по сравнению с наличием их вне его?
Кристофер Гарсия,
1
Превалирующим соглашением является присоединение обработчиков событий к OnInitметоду, но поскольку Clickсобытие кнопки будет вызвано после загрузки страницы, этот пример подходит.
Эндрю Хэйр
8
Важно отметить, что без сохранения ссылки на делегата вы не можете отказаться от подписки на событие.
snarf
3
"точно такой же код" немного вводит в заблуждение; по крайней мере, при ссылке на локальные переменные из включающего метода лямбда-выражения не транслируются в методы и нечто вроде закрывающего объекта, в котором хранятся текущие значения локальных переменных.
OR Mapper
66

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

MyButton.Click -= (o, i) => 
{ 
    //snip 
} 

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

Гейб
источник
3
« Вероятно, он попытается ...»? Удалит ли он когда-нибудь правильный обработчик в такой ситуации?
OR Mapper
1
@ORMapper: если лямбда захватывает переменную, она не может удалить правильный обработчик. В других случаях это зависит от компилятора.
Гейб
В самом деле? Интересно - поэтому, если я зарегистрирую две анонимные функции, которые выглядят одинаково (wlog имеет пустое тело), ​​а затем я отменю регистрацию (используя -=) другую анонимную функцию, которая также имеет пустое тело, по существу не определено, какой из двух обработчиков событий будет будут удалены, или будут ли они удалены вообще?
OR Mapper
4
@ORMapper: Да. Компилятору разрешено (но не обязательно) создавать равные делегаты, если они имеют идентичную семантику (код не должен быть одинаковым, но они должны делать то же самое) и захватывать одни и те же экземпляры переменных (а не только те же переменные, но те же экземпляры этих переменных). См. Все подробности в разделе 7.10.8 (Операторы равенства делегатов) спецификации C #.
Гейб
12
Если вы действительно хотите использовать лямбду, но вам нужно удалить событие, вы всегда можете сохранить объект в локальной переменной / поле, а затем удалить его, напримерvar event = (o, e) => doSomething(); handler += event; doSomethingElse(); handler -= event;
Вай Ха Ли
44
EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
button.Click -= handler;
Усама Вахаб Хан
источник
1
Очень полезная информация, правда не по теме (вопрос про производительность).
Стефан Гуришон
4
Не совсем не по теме, поскольку использование памяти может привести к снижению производительности.
Владиус
3
Само удаление в обработчике также может быть полезным:c# EventHandler handler = null; handler = (s, e) => { MessageBox.Show("Woho"); button.Click -= handler;}
Владиус,
2

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

Гейзенберга
источник