การใช้นิพจน์แลมบ์ดาสำหรับตัวจัดการเหตุการณ์


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 เทียบกับการมีตัวจัดการภายนอกหรือไม่?
Christopher Garcia

1
หลักการที่ใช้กันอยู่คือการแนบตัวจัดการเหตุการณ์ในOnInitวิธีการ แต่เนื่องจากClickเหตุการณ์ของปุ่มจะถูกยกขึ้นหลังจากที่โหลดหน้าเว็บตัวอย่างนี้จึงใช้ได้
Andrew Hare

8
สิ่งสำคัญที่ควรทราบว่าหากไม่มีการอ้างอิงถึงผู้รับมอบสิทธิ์คุณจะไม่สามารถยกเลิกการสมัครรับกิจกรรมได้
snarf

3
"รหัสเดียวกันทุกประการ" ทำให้เข้าใจผิดเล็กน้อย อย่างน้อยเมื่ออ้างอิงตัวแปรโลคัลจากวิธีการปิดนิพจน์แลมบ์ดาจะไม่ถูกแปลเป็นวิธีการและบางอย่างเช่นอ็อบเจ็กต์ปิดที่เก็บค่าปัจจุบันของตัวแปรโลคัล
หรือผู้ทำแผนที่

66

ประสิทธิภาพที่ชาญฉลาดเหมือนกับวิธีการที่ตั้งชื่อ ปัญหาใหญ่คือเมื่อคุณทำสิ่งต่อไปนี้:

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

มันอาจจะพยายามลบแลมด้าตัวอื่นทิ้งอันเดิมไว้ที่นั่น ดังนั้นบทเรียนก็คือมันเป็นเรื่องปกติเว้นแต่คุณจะต้องการเอาตัวจัดการออกไปด้วย


3
"มันอาจจะพยายาม ... "? มันจะเคยเอาตัวจัดการที่ถูกต้องในสถานการณ์ดังกล่าวหรือไม่
หรือผู้ทำแผนที่

1
@ORMapper: หากแลมบ์ดาจับตัวแปรก็ไม่สามารถลบตัวจัดการที่ถูกต้องได้ ในสถานการณ์อื่น ๆ มันขึ้นอยู่กับคอมไพเลอร์
Gabe

จริงๆ? น่าสนใจ - ดังนั้นหากฉันลงทะเบียนฟังก์ชันที่ไม่ระบุชื่อสองฟังก์ชันที่มีลักษณะเหมือนกัน (wlog มีเนื้อความว่างเปล่า) จากนั้นฉันก็ยกเลิกการลงทะเบียน (ใช้-=) ฟังก์ชันนิรนามอื่นที่มีเนื้อความว่างเปล่าโดยพื้นฐานแล้วจะไม่ได้กำหนดว่าตัวจัดการเหตุการณ์ใดในสองตัวจะ จะถูกลบออกหรือว่าจะถูกลบออกเลย?
หรือผู้ทำแผนที่

4
@ORMapper: ใช่ คอมไพเลอร์ได้รับอนุญาตให้ (แต่ไม่จำเป็นต้อง) สร้างผู้รับมอบสิทธิ์ที่เท่าเทียมกันหากมีความหมายเหมือนกัน (รหัสไม่จำเป็นต้องเหมือนกัน แต่ต้องทำสิ่งเดียวกัน) และจับอินสแตนซ์ตัวแปรเดียวกัน (ไม่ใช่แค่ ตัวแปรเดียวกัน แต่มีอินสแตนซ์เดียวกันของตัวแปรเหล่านั้น) ดูหัวข้อ 7.10.8 (ตัวดำเนินการความเท่าเทียมกันของตัวแทน) ของข้อมูลจำเพาะ C # สำหรับรายละเอียดทั้งหมด
Gabe

12
หากคุณต้องการใช้ lambda จริงๆ แต่ต้องการลบเหตุการณ์คุณสามารถเก็บวัตถุไว้ในตัวแปร / ฟิลด์ในเครื่องได้เสมอจากนั้นจึงลบสิ่งนั้นออกเช่น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;

1
ข้อมูลที่มีประโยชน์มากแม้ว่าจะไม่อยู่ในหัวข้อก็ตาม (คำถามเกี่ยวกับประสิทธิภาพ)
Stéphane Gourichon

4
ไม่ตรงประเด็นเนื่องจากการใช้หน่วยความจำอาจนำไปสู่การดาวน์เกรดประสิทธิภาพ
Vladius

3
การถอดตัวเองออกในตัวจัดการเองอาจมีประโยชน์เช่นกัน:c# EventHandler handler = null; handler = (s, e) => { MessageBox.Show("Woho"); button.Click -= handler;}
Vladius

2

ไม่มีผลกระทบด้านประสิทธิภาพที่ฉันรับรู้หรือเคยพบมาก่อนเท่าที่ฉันรู้ว่ามันเป็นเพียงแค่ "syntactic sugar" และรวบรวมลงไปในลักษณะเดียวกับการใช้ไวยากรณ์ของตัวแทน ฯลฯ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.