Używanie wyrażeń lambda dla programów obsługi zdarzeń

114

Obecnie mam następującą stronę zadeklarowaną:

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

Dopiero niedawno przeniosłem się na .NET 3.5 z 1.1, więc jestem przyzwyczajony do pisania programów obsługi zdarzeń poza Page_Load. Moje pytanie brzmi; Czy są jakieś wady wydajności lub pułapki, na które powinienem uważać, używając do tego metody lambda? Wolę to, ponieważ jest na pewno bardziej zwięzłe, ale nie chcę poświęcać wydajności, aby go używać. Dzięki.

Christopher Garcia
źródło

Odpowiedzi:

117

Nie ma to wpływu na wydajność, ponieważ kompilator przetłumaczy wyrażenie lambda na równoważnego delegata. Wyrażenia lambda to nic innego jak funkcja języka, którą kompilator tłumaczy na dokładnie ten sam kod, z którym zwykle pracujesz.

Kompilator przekonwertuje Twój kod na coś takiego:

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
        });
    }
}
Andrew Hare
źródło
Widzę. Więc czy nie ma też żadnej wady posiadania tych programów obsługi wewnątrz Page_Load w porównaniu z posiadaniem ich poza nim?
Christopher Garcia,
1
Dominującą konwencją jest dołączanie programów obsługi zdarzeń w OnInitmetodzie, ale ponieważ Clickzdarzenie przycisku zostanie zgłoszone po załadowaniu strony, ten przykład jest w porządku.
Andrew Hare,
8
Należy pamiętać, że bez zachowania odniesienia do delegata nie można anulować subskrypcji wydarzenia.
snarf
3
„dokładnie ten sam kod” jest nieco mylące; przynajmniej w przypadku odwoływania się do zmiennych lokalnych z otaczającej metody, wyrażenia lambda nie są tłumaczone na metody i coś w rodzaju obiektu zamknięcia, który przechowuje bieżące wartości zmiennych lokalnych.
LUB Mapper
66

Pod względem wydajności jest to to samo, co nazwana metoda. Duży problem występuje, gdy wykonujesz następujące czynności:

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

Prawdopodobnie spróbuje usunąć inną lambdę, pozostawiając tam oryginalną. Lekcja jest taka, że ​​wszystko jest w porządku, chyba że chcesz również mieć możliwość usunięcia modułu obsługi.

Gabe
źródło
3
Prawdopodobnie spróbuje…”? Czy kiedykolwiek usunie właściwą obsługę w takiej sytuacji?
LUB Mapper
1
@ORMapper: Jeśli lambda przechwytuje zmienną, nie może usunąć poprawnej procedury obsługi. W innych okolicznościach zależy to od kompilatora.
Gabe
Naprawdę? Interesujące - więc jeśli zarejestruję dwie anonimowe funkcje, które wyglądają tak samo (wlog ma puste ciało), a następnie wyrejestruję (używając -=) inną anonimową funkcję, która również ma puste ciało, zasadniczo nie jest zdefiniowane, który z dwóch programów obsługi zdarzeń będzie być usunięte, czy w ogóle którykolwiek z nich zostanie usunięty?
LUB Mapper
4
@ORMapper: Tak. Kompilator może (ale nie musi) tworzyć równych delegatów, jeśli mają identyczną semantykę (kod nie musi być taki sam, ale muszą robić to samo) i przechwytywać te same instancje zmiennych (nie tylko te same zmienne, ale te same wystąpienia tych zmiennych). Zobacz sekcję 7.10.8 (Delegowanie operatorów równości) specyfikacji języka C #, aby uzyskać wszystkie szczegóły.
Gabe
12
Jeśli naprawdę chcesz użyć lambdy, ale musisz usunąć zdarzenie, zawsze możesz zatrzymać obiekt w lokalnej zmiennej / polu, a następnie usunąć to, np.var event = (o, e) => doSomething(); handler += event; doSomethingElse(); handler -= event;
Wai Ha Lee
44
EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
button.Click -= handler;
usama wahab khan
źródło
1
Bardzo przydatne informacje, choć nie na temat (pytanie dotyczy wydajności).
Stéphane Gourichon
4
Niezupełnie poza tematem, ponieważ użycie pamięci może prowadzić do obniżenia wydajności.
Vladius
3
Usunięcie się w samym c# EventHandler handler = null; handler = (s, e) => { MessageBox.Show("Woho"); button.Click -= handler;}
treserze
2

Brak implikacji wydajnościowych, o których jestem świadomy lub kiedykolwiek się z nimi spotkałem, o ile wiem, że to po prostu „cukier składniowy” i kompiluje się do tego samego, co użycie składni delegata itp.

heisenberg
źródło