Saya menyadari bahwa pertanyaan ini sudah berusia lebih dari 10 tahun, tetapi bagi saya tampaknya bukan saja jawaban yang paling jelas belum ditanggapi, tetapi mungkin juga tidak terlalu jelas dari pertanyaan, pemahaman yang baik tentang apa yang terjadi di balik selimut. Selain itu, ada pertanyaan lain tentang ikatan yang terlambat dan apa artinya sehubungan dengan delegasi dan lambda (lebih lanjut tentang itu nanti).
Pertama untuk mengatasi 800 lb gajah / gorila di dalam ruangan, kapan harus memilih eventvs Action<T>/ Func<T>:
- Gunakan lambda untuk menjalankan satu pernyataan atau metode. Gunakan
eventketika Anda menginginkan lebih dari model pub / sub dengan banyak pernyataan / lambdas / fungsi yang akan dieksekusi (ini adalah
perbedaan utama langsung dari kelelawar).
- Gunakan lambda ketika Anda ingin mengkompilasi pernyataan / fungsi untuk ekspresi pohon. Gunakan delegasi / acara ketika Anda ingin berpartisipasi dalam ikatan yang lebih tradisional seperti yang digunakan dalam refleksi dan COM interop.
Sebagai contoh dari suatu acara, mari memasang serangkaian acara sederhana dan 'standar' menggunakan aplikasi konsol kecil sebagai berikut:
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}");
}
}
Output akan terlihat sebagai berikut:

Jika Anda melakukan hal yang sama dengan Action<int>atau Action<object, SomeStandardArgs>, Anda hanya akan melihat SomeSimpleEvent2dan SomeStandardEvent2.
Jadi apa yang terjadi di dalam event?
Jika kami memperluas FireNiceEvent, kompiler sebenarnya menghasilkan yang berikut (Saya telah menghilangkan beberapa detail sehubungan dengan sinkronisasi utas yang tidak relevan dengan diskusi ini):
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)
}
}
Compiler menghasilkan variabel delegasi pribadi yang tidak terlihat oleh namespace kelas di mana ia dihasilkan. Delegasi itu adalah apa yang digunakan untuk manajemen langganan dan partisipasi yang terlambat mengikat, dan antarmuka yang menghadap publik adalah yang akrab +=dan -=operator yang kita semua kenal dan sukai:)
Anda dapat mengubahsuaikan kode untuk penambah / hapus penangan dengan mengubah cakupan FireNiceEventdelegasi menjadi terlindungi. Ini sekarang memungkinkan pengembang untuk menambahkan kait khusus ke kait, seperti penebangan atau kait keamanan. Ini benar-benar membuat untuk beberapa fitur yang sangat kuat yang sekarang memungkinkan aksesibilitas khusus untuk berlangganan berdasarkan peran pengguna, dll. (Sebenarnya Anda bisa dengan mengkompilasi pohon ekspresi kustom, tapi itu di luar cakupan respons ini).
Untuk mengatasi beberapa poin dari beberapa respons di sini:
Tidak ada perbedaan dalam 'kerapuhan' antara mengubah daftar args Action<T>dan mengubah properti dalam kelas yang diturunkan EventArgs. Entah tidak hanya akan memerlukan perubahan kompilasi, mereka berdua akan mengubah antarmuka publik dan akan memerlukan versi. Tidak ada perbedaan.
Sehubungan dengan yang merupakan standar industri, itu tergantung pada di mana ini digunakan dan mengapa. Action<T>dan seperti itu sering digunakan dalam IoC dan DI, dan eventsering digunakan dalam perutean pesan seperti kerangka kerja tipe GUI dan MQ. Perhatikan bahwa saya sering berkata , tidak selalu .
Delegasi memiliki umur yang berbeda dari lambda. Kita juga harus menyadari penangkapan ... tidak hanya dengan penutupan, tetapi juga dengan gagasan 'lihat apa yang diseret kucing'. Ini tidak memengaruhi jejak memori / masa pakai serta manajemen alias kebocoran.
Satu hal lagi, sesuatu yang saya rujuk sebelumnya ... gagasan tentang ikatan yang terlambat. Anda akan sering melihat ini ketika menggunakan kerangka kerja seperti LINQ, mengenai kapan lambda menjadi 'hidup'. Itu sangat berbeda dari pengikatan seorang delegasi yang terlambat, yang dapat terjadi lebih dari satu kali (yaitu lambda selalu ada di sana, tetapi pengikatan terjadi atas permintaan sesering yang diperlukan), berbeda dengan sebuah lambda, yang begitu terjadi, hal itu dilakukan - Keajaiban hilang, dan metode (properti) / properti akan selalu mengikat. Sesuatu yang perlu diingat.