Menggunakan ekspresi lambda untuk penangan acara

114

Saat ini saya memiliki halaman yang dinyatakan sebagai berikut:

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

Saya baru saja pindah ke .NET 3.5 dari 1.1, jadi saya terbiasa menulis penangan acara di luar Page_Load. Pertanyaanku adalah; apakah ada kekurangan atau kekurangan performa yang harus saya perhatikan saat menggunakan metode lambda untuk ini? Saya lebih suka, karena pasti lebih ringkas, tapi saya tidak ingin mengorbankan performa untuk menggunakannya. Terima kasih.

Christopher Garcia
sumber

Jawaban:

117

Tidak ada implikasi kinerja karena kompilator akan menerjemahkan ekspresi lambda Anda menjadi delegasi yang setara. Ekspresi lambda tidak lebih dari fitur bahasa yang diterjemahkan oleh compiler ke dalam kode yang sama persis dengan yang biasa Anda gunakan.

Kompiler akan mengubah kode yang Anda miliki menjadi seperti ini:

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
sumber
Saya melihat. Jadi, apakah juga tidak ada kerugian memiliki penangan ini di dalam Page_Load dibandingkan memiliki penangan di luarnya?
Christopher Garcia
1
Ketentuan yang berlaku adalah melampirkan penangan peristiwa dalam OnInitmetode, tetapi karena peristiwa tombol Clickakan dimunculkan setelah halaman dimuat, contoh ini baik-baik saja.
Andrew Hare
8
Penting untuk diperhatikan bahwa tanpa mempertahankan referensi ke delegasi, Anda tidak dapat berhenti berlangganan acara.
snarf
3
"kode yang sama persis" agak menyesatkan; setidaknya saat mereferensikan variabel lokal dari metode penutup, ekspresi lambda tidak diterjemahkan ke dalam metode dan sesuatu seperti objek penutup yang menyimpan nilai saat ini dari variabel lokal.
ATAU Mapper
66

Dari segi kinerja, ini sama dengan metode bernama. Masalah besarnya adalah saat Anda melakukan hal berikut:

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

Ini mungkin akan mencoba untuk menghapus lambda yang berbeda, meninggalkan yang asli di sana. Jadi pelajarannya adalah baik-baik saja kecuali jika Anda juga ingin bisa melepaskan penangannya.

Gabe
sumber
3
"Ini akan mungkin mencoba untuk ..."? Apakah akan pernah menghapus penangan yang benar dalam situasi seperti itu?
ATAU Mapper
1
@ORMapper: Jika lambda menangkap variabel, itu tidak dapat menghapus penangan yang benar. Dalam keadaan lain, terserah kompilator.
Gabe
Betulkah? Menarik - jadi, jika saya mendaftarkan dua fungsi anonim yang terlihat sama (wlog memiliki badan kosong), dan kemudian saya membatalkan pendaftaran (menggunakan -=) fungsi anonim lain yang juga memiliki badan kosong, pada dasarnya tidak ditentukan mana dari dua penangan kejadian yang akan dihapus, atau apakah ada yang akan dihapus sama sekali?
ATAU Mapper
4
@ORMap: Ya. Kompilator diperbolehkan (tetapi tidak harus) membuat delegasi yang sama jika mereka memiliki semantik identik (kode tidak harus sama, tetapi mereka harus melakukan hal yang sama) dan menangkap contoh variabel yang sama (tidak hanya variabel yang sama, tetapi contoh yang sama dari variabel tersebut). Lihat bagian 7.10.8 (Mendelegasikan operator kesetaraan) dari spesifikasi C # untuk semua detailnya.
Gabe
12
Jika Anda benar-benar ingin menggunakan lambda tetapi perlu menghapus acara tersebut, Anda selalu dapat menyimpan objek dalam variabel / bidang lokal lalu menghapusnya, misalnyavar 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
sumber
1
Informasi yang sangat berguna, meskipun diluar topik (pertanyaannya adalah tentang kinerja).
Stéphane Gourichon
4
Tidak benar-benar di luar topik karena penggunaan memori dapat menyebabkan penurunan kinerja.
Vladius
3
Menghapus dirinya sendiri di pawang itu sendiri juga dapat membantu:c# EventHandler handler = null; handler = (s, e) => { MessageBox.Show("Woho"); button.Click -= handler;}
Vladius
2

Tidak ada implikasi kinerja yang saya sadari atau pernah temui, sejauh yang saya tahu itu hanya "gula sintaksis" dan mengkompilasi ke hal yang sama seperti menggunakan sintaks delegasi, dll.

heisenberg
sumber