ฉันรู้ว่าคำถามนี้มีอายุมากกว่า 10 ปี แต่ดูเหมือนว่าฉันไม่เพียง แต่มีคำตอบที่ชัดเจนที่สุดที่ไม่ได้รับการตอบสนอง แต่นั่นอาจจะไม่ชัดเจนจากคำถามจริงๆซึ่งเป็นความเข้าใจที่ดีว่าเกิดอะไรขึ้นภายใต้ฝาครอบ นอกจากนี้ยังมีคำถามอื่น ๆ เกี่ยวกับการผูกสายและสิ่งที่เกี่ยวข้องกับผู้ได้รับมอบหมายและ lambdas (เพิ่มเติมในภายหลัง)
ก่อนอื่นให้จัดการกับช้างที่มีน้ำหนัก 800 ปอนด์ / กอริลลาในห้องเมื่อเลือกeventvs Action<T>/ Func<T>:
- ใช้แลมบ์ดาเพื่อดำเนินการหนึ่งคำสั่งหรือวิธีการ ใช้
eventเมื่อคุณต้องการรุ่นของ pub / sub ที่มีหลาย statement / lambdas / ฟังก์ชั่นที่จะทำงาน (นี่คือความ
แตกต่างที่สำคัญทันทีจากไม้ตี)
- ใช้แลมบ์ดาเมื่อคุณต้องการรวบรวมคำสั่ง / ฟังก์ชั่นเพื่อแสดงออกต้นไม้ ใช้ผู้ได้รับมอบหมาย / เหตุการณ์เมื่อคุณต้องการมีส่วนร่วมในการผูกปลายแบบดั้งเดิมมากขึ้นเช่นที่ใช้ในการสะท้อนและ COM interop
เป็นตัวอย่างของเหตุการณ์ให้วางสายเหตุการณ์ที่ง่ายและ 'มาตรฐาน' โดยใช้แอปพลิเคชันคอนโซลขนาดเล็กดังนี้:
public delegate void FireEvent(int num);
public delegate void FireNiceEvent(object sender, SomeStandardArgs args);
public class SomeStandardArgs : EventArgs
{
public SomeStandardArgs(string id)
{
ID = id;
}
public string ID { get; set; }
}
class Program
{
public static event FireEvent OnFireEvent;
public static event FireNiceEvent OnFireNiceEvent;
static void Main(string[] args)
{
OnFireEvent += SomeSimpleEvent1;
OnFireEvent += SomeSimpleEvent2;
OnFireNiceEvent += SomeStandardEvent1;
OnFireNiceEvent += SomeStandardEvent2;
Console.WriteLine("Firing events.....");
OnFireEvent?.Invoke(3);
OnFireNiceEvent?.Invoke(null, new SomeStandardArgs("Fred"));
//Console.WriteLine($"{HeightSensorTypes.Keyence_IL030}:{(int)HeightSensorTypes.Keyence_IL030}");
Console.ReadLine();
}
private static void SomeSimpleEvent1(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent1)}:{num}");
}
private static void SomeSimpleEvent2(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent2)}:{num}");
}
private static void SomeStandardEvent1(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent1)}:{args.ID}");
}
private static void SomeStandardEvent2(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent2)}:{args.ID}");
}
}
ผลลัพธ์จะมีลักษณะดังนี้:

ถ้าคุณทำเช่นเดียวกันกับAction<int>หรือAction<object, SomeStandardArgs>คุณเท่านั้นที่จะเห็นและSomeSimpleEvent2SomeStandardEvent2
แล้วเกิดอะไรขึ้นภายในของevent?
ถ้าเราขยายออกไปFireNiceEventคอมไพเลอร์กำลังสร้างสิ่งต่อไปนี้ (ฉันได้ละเว้นรายละเอียดบางอย่างเกี่ยวกับการซิงโครไนซ์เธรดที่ไม่เกี่ยวข้องกับการสนทนานี้):
private EventHandler<SomeStandardArgs> _OnFireNiceEvent;
public void add_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Combine(_OnFireNiceEvent, handler);
}
public void remove_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Remove(_OnFireNiceEvent, handler);
}
public event EventHandler<SomeStandardArgs> OnFireNiceEvent
{
add
{
add_OnFireNiceEvent(value)
}
remove
{
remove_OnFireNiceEvent(value)
}
}
คอมไพเลอร์สร้างตัวแปรผู้แทนส่วนตัวซึ่งไม่สามารถมองเห็นได้ในเนมสเปซของคลาสที่ถูกสร้างขึ้น ผู้รับมอบสิทธิ์นั้นเป็นสิ่งที่ใช้สำหรับการจัดการการสมัครสมาชิกและการมีส่วนร่วมที่ล่าช้าและส่วนต่อประสานสาธารณะที่คุ้นเคย+=และ-=ผู้ประกอบการที่เรารู้จักและชื่นชอบ:)
คุณสามารถปรับแต่งรหัสสำหรับตัวจัดการเพิ่ม / ลบได้โดยเปลี่ยนขอบเขตของFireNiceEventผู้รับมอบสิทธิ์เป็นแบบป้องกัน ตอนนี้ช่วยให้นักพัฒนาสามารถเพิ่ม hooks ที่กำหนดเองไปที่ hooks เช่นการบันทึกหรือขอความปลอดภัย สิ่งนี้สร้างขึ้นสำหรับฟีเจอร์ที่ทรงพลังบางอย่างที่ตอนนี้อนุญาตให้เข้าถึงการสมัครสมาชิกตามบทบาทของผู้ใช้เป็นต้นคุณสามารถทำกับแลมบ์ดาได้หรือไม่? (ที่จริงแล้วคุณสามารถทำได้โดยการรวบรวมต้นไม้การแสดงออกที่กำหนดเอง แต่ที่อยู่นอกเหนือขอบเขตของการตอบสนองนี้)
หากต้องการระบุจุดสองสามข้อจากคำตอบบางส่วนที่นี่:
มีจริงๆไม่แตกต่างกันใน 'เปราะบางระหว่างการเปลี่ยน args รายการในและการเปลี่ยนแปลงคุณสมบัติในชั้นเรียนที่ได้มาจากAction<T> EventArgsไม่เพียง แต่จะต้องการการเปลี่ยนแปลงคอมไพล์พวกเขาทั้งสองจะเปลี่ยนอินเทอร์เฟซสาธารณะและจะต้องมีการกำหนดเวอร์ชัน ไม่แตกต่าง.
ด้วยความเคารพซึ่งเป็นมาตรฐานอุตสาหกรรมขึ้นอยู่กับว่าจะใช้ที่ใดและเพราะเหตุใด Action<T>และมักใช้ใน IoC และ DI และeventมักใช้ในการกำหนดเส้นทางข้อความเช่นกรอบงานประเภท GUI และ MQ โปรดทราบว่าฉันพูดบ่อยๆไม่เสมอไป
ผู้ได้รับมอบหมายมีช่วงอายุที่แตกต่างจาก lambdas นอกจากนี้ยังต้องระวังการจับ ... ไม่เพียง แต่ปิด แต่ยังมีความคิดของ 'ดูสิ่งที่แมวลากเข้ามาใน' สิ่งนี้จะส่งผลกระทบต่อหน่วยความจำ / อายุการใช้งานตลอดจนการจัดการการรั่วไหล
อีกอย่างหนึ่งสิ่งที่ฉันอ้างถึงก่อนหน้านี้ ... ความคิดของการผูกปลาย คุณมักจะเห็นสิ่งนี้เมื่อใช้เฟรมเวิร์กเช่น LINQ เกี่ยวกับเมื่อแลมบ์ดากลายเป็น 'สด' นั่นแตกต่างจากการผูกมัดผู้แทนซึ่งอาจเกิดขึ้นได้มากกว่าหนึ่งครั้ง (เช่นแลมบ์ดาอยู่ที่นั่นเสมอ แต่การผูกมัดเกิดขึ้นตามความต้องการบ่อยเท่าที่ต้องการ) เมื่อเทียบกับแลมบ์ดาซึ่งเกิดขึ้นทันที - เวทย์มนตร์หายไปและวิธีการ / ทรัพย์สิน (IES) จะผูกมัดเสมอ สิ่งที่ต้องจำไว้