Apa perbedaan antara delegasi dan acara? Tidak keduanya memiliki referensi ke fungsi yang dapat dieksekusi?
Apa perbedaan antara delegasi dan acara? Tidak keduanya memiliki referensi ke fungsi yang dapat dieksekusi?
Jawaban:
Sebuah acara deklarasi menambahkan lapisan abstraksi dan perlindungan pada delegasi misalnya. Perlindungan ini mencegah klien dari delegasi mengatur ulang delegasi dan daftar doa dan hanya memungkinkan menambah atau menghapus target dari daftar doa.
Untuk memahami perbedaan Anda dapat melihat 2 contoh ini
Contoh dengan Delegasi (dalam hal ini, Aksi - itu adalah semacam delegasi yang tidak mengembalikan nilai)
public class Animal
{
public Action Run {get; set;}
public void RaiseEvent()
{
if (Run != null)
{
Run();
}
}
}
Untuk menggunakan delegasi, Anda harus melakukan sesuatu seperti ini:
Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();
Kode ini berfungsi dengan baik tetapi Anda dapat memiliki beberapa titik lemah.
Misalnya, jika saya menulis ini:
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;
dengan baris kode terakhir, saya telah menimpa perilaku sebelumnya hanya dengan satu yang hilang +
(saya telah menggunakan =
alih-alih +=
)
Kelemahan lain adalah bahwa setiap kelas yang menggunakan Animal
kelas Anda dapat meningkatkan RaiseEvent
hanya dengan menyebutnya animal.RaiseEvent()
.
Untuk menghindari titik-titik lemah ini Anda dapat menggunakan events
c #.
Kelas Hewan Anda akan berubah dengan cara ini:
public class ArgsSpecial : EventArgs
{
public ArgsSpecial (string val)
{
Operation=val;
}
public string Operation {get; set;}
}
public class Animal
{
// Empty delegate. In this way you are sure that value is always != null
// because no one outside of the class can change it.
public event EventHandler<ArgsSpecial> Run = delegate{}
public void RaiseEvent()
{
Run(this, new ArgsSpecial("Run faster"));
}
}
untuk memanggil acara
Animal animal= new Animal();
animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
Perbedaan:
Catatan:
EventHandler dinyatakan sebagai delegasi berikut:
public delegate void EventHandler (object sender, EventArgs e)
Dibutuhkan pengirim (tipe objek) dan argumen acara. Pengirim adalah null jika berasal dari metode statis.
Contoh ini, yang menggunakan EventHandler<ArgsSpecial>
, bisa juga ditulis menggunakan EventHandler
.
Rujuk di sini untuk dokumentasi tentang EventHandler
RaiseEvent
selama metode panggilan memiliki akses ke instance animal
dalam kode yang menggunakan acara?
animal.Run(this, new ArgsSpecial("Run faster");
?
Selain sifat sintaksis dan operasional, ada juga perbedaan semantik.
Delegasi, secara konseptual, templat fungsi; yaitu, mereka menyatakan suatu kontrak yang harus dipatuhi oleh suatu fungsi untuk dipertimbangkan dari "tipe" delegasi.
Acara mewakili ... baik, acara. Mereka dimaksudkan untuk memperingatkan seseorang ketika sesuatu terjadi dan ya, mereka mematuhi definisi delegasi tetapi mereka bukan hal yang sama.
Bahkan jika mereka persis hal yang sama (secara sintaksis dan dalam kode IL) masih akan tetap ada perbedaan semantik. Secara umum saya lebih suka memiliki dua nama yang berbeda untuk dua konsep yang berbeda, bahkan jika mereka diimplementasikan dengan cara yang sama (yang tidak berarti saya suka memiliki kode yang sama dua kali).
Berikut ini adalah tautan lain yang bagus untuk dirujuk. http://csharpindepth.com/Articles/Chapter2/Events.aspx
Secara singkat, pengambilan dari artikel - Acara adalah enkapsulasi atas delegasi.
Kutipan dari artikel:
Misalkan acara tidak ada sebagai konsep di C # /. NET. Bagaimana kelas lain berlangganan suatu acara? Tiga opsi:
Variabel delegasi publik
Variabel delegasi yang didukung oleh properti
Variabel delegasi dengan metode AddXXXHandler dan RemoveXXXHandler
Opsi 1 jelas mengerikan, untuk semua alasan normal kami membenci variabel publik.
Opsi 2 sedikit lebih baik, tetapi memungkinkan pelanggan untuk secara efektif menimpa satu sama lain - itu akan terlalu mudah untuk menulis someInstance.MyEvent = eventHandler; yang akan menggantikan penangan acara yang ada daripada menambahkan yang baru. Selain itu, Anda masih perlu menulis properti.
Opsi 3 pada dasarnya adalah acara yang memberi Anda, tetapi dengan konvensi yang dijamin (dihasilkan oleh kompiler dan didukung oleh bendera tambahan di IL) dan implementasi "gratis" jika Anda senang dengan semantik yang diberikan acara seperti lapangan kepada Anda. Berlangganan dan berhenti berlangganan dari acara dienkapsulasi tanpa mengizinkan akses sewenang-wenang ke daftar penangan acara, dan bahasa dapat membuat segalanya lebih mudah dengan memberikan sintaksis untuk deklarasi dan berlangganan.
public Delegate
variabel akan mengekspos "data", tetapi Delegate
sejauh pengetahuan saya, OOP tidak pernah menyebut konsep apa pun seperti (itu bukan "objek" atau "pesan") ,.
AddXXXHandler
metode Anda sendiri dengan private Delegate
variabel mungkin merupakan pilihan yang baik. Dalam hal ini Anda dapat memeriksa untuk melihat apakah pawang sudah diatur, dan bereaksi dengan tepat. Ini juga bisa menjadi pengaturan yang baik jika Anda membutuhkan objek yang memegang Delegate
untuk dapat menghapus semua penangan ( event
tidak memberi Anda cara untuk melakukan ini).
CATATAN: Jika Anda memiliki akses ke C # 5.0 Unleashed , baca "Batasan Penggunaan Utusan Delegasi" di Bab 18 berjudul "Acara" untuk memahami lebih baik perbedaan di antara keduanya.
Selalu membantu saya untuk memiliki contoh yang sederhana dan konkret. Jadi, ini satu untuk komunitas. Pertama saya tunjukkan bagaimana Anda bisa menggunakan delegasi sendirian untuk melakukan apa yang dilakukan Acara untuk kami. Lalu saya tunjukkan bagaimana solusi yang sama akan bekerja dengan contoh EventHandler
. Dan kemudian saya jelaskan mengapa kita TIDAK ingin melakukan apa yang saya jelaskan pada contoh pertama. Posting ini terinspirasi oleh sebuah artikel oleh John Skeet.
Contoh 1: Menggunakan delegasi publik
Misalkan saya memiliki aplikasi WinForms dengan satu kotak drop-down. Drop-down terikat ke List<Person>
. Di mana Orang memiliki properti Id, Nama, Nama Panggilan, Warna Rambut. Pada formulir utama adalah kontrol pengguna kustom yang menunjukkan properti orang itu. Ketika seseorang memilih seseorang di drop-down label di pembaruan kontrol pengguna untuk menunjukkan properti orang yang dipilih.
Inilah cara kerjanya. Kami memiliki tiga file yang membantu kami menyatukan ini:
Berikut adalah kode yang relevan untuk masing-masing kelas:
class Mediator
{
public delegate void PersonChangedDelegate(Person p); //delegate type definition
public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
{
if (PersonChangedDel != null)
{
PersonChangedDel(p);
}
}
}
Inilah kontrol pengguna kami:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.PersonChangedDel += DetailView_PersonChanged;
}
void DetailView_PersonChanged(Person p)
{
BindData(p);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Akhirnya kami memiliki kode berikut di Form1.cs kami. Di sini kita Memanggil OnPersonChanged, yang memanggil kode apa saja yang berlangganan delegasi.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}
Baik. Jadi begitulah cara Anda membuatnya bekerja tanpa menggunakan acara dan hanya menggunakan delegasi . Kami hanya menempatkan delegasi publik ke dalam kelas - Anda dapat membuatnya statis atau tunggal, atau apa pun. Bagus.
NAMUN, NAMUN, NAMUN, kami tidak ingin melakukan apa yang baru saja saya jelaskan di atas. Karena bidang publik buruk karena banyak, banyak alasan. Jadi apa saja pilihan kita? Seperti yang dijelaskan oleh John Skeet, berikut adalah opsi kami:
PersonChangedDel = null
, menghapus semua langganan lainnya. Masalah lain yang tetap ada di sini adalah karena pengguna memiliki akses ke delegasi, mereka dapat meminta target dalam daftar doa - kami tidak ingin pengguna eksternal memiliki akses kapan harus meningkatkan acara kami.Opsi ketiga ini pada dasarnya adalah apa yang diberikan oleh suatu peristiwa kepada kita. Ketika kami mendeklarasikan EventHandler, itu memberi kami akses ke delegasi - tidak secara publik, bukan sebagai properti, tetapi karena hal ini kami sebut acara yang baru saja menambah / menghapus pengakses.
Mari kita lihat seperti apa program yang sama, tetapi sekarang menggunakan Acara alih-alih delegasi publik (Saya juga mengubah Mediator kami menjadi singleton):
Contoh 2: Dengan EventHandler alih-alih delegasi publik
Penengah:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
public void OnPersonChanged(object sender, Person p)
{
var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
Perhatikan bahwa jika Anda F12 pada EventHandler, itu akan menunjukkan kepada Anda bahwa definisi tersebut hanyalah sebuah delegasi umum dengan objek "pengirim" tambahan:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
Kontrol Pengguna:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
}
void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
{
BindData(e.Person);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Akhirnya, inilah kode Form1.cs:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}
Karena EventHandler ingin dan EventArgs sebagai parameter, saya membuat kelas ini hanya dengan satu properti di dalamnya:
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
Semoga itu menunjukkan kepada Anda sedikit tentang mengapa kami memiliki acara dan bagaimana mereka berbeda - tetapi secara fungsional sama - seperti delegasi.
The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events
. Di versi terbaru Mediator
, Anda masih dapat menelepon OnPersonChange
kapan pun Anda memiliki referensi ke singleton. Mungkin Anda harus menyebutkan bahwa Mediator
pendekatan itu tidak mencegah perilaku tertentu, dan lebih dekat ke bus peristiwa.
Anda juga dapat menggunakan acara dalam deklarasi antarmuka, tidak demikian untuk delegasi.
Action a { get; set; }
di dalam definisi antarmuka.
Benar-benar kesalahpahaman antara acara dan delegasi !!! Delegasi menentukan JENIS (seperti class
, atau interface
tidak), sedangkan acara hanya semacam ANGGOTA (seperti bidang, properti, dll). Dan, sama seperti anggota lainnya, acara juga memiliki tipe. Namun, dalam hal acara, jenis acara harus ditentukan oleh delegasi. Misalnya, Anda TIDAK BISA mendeklarasikan peristiwa dari jenis yang ditentukan oleh antarmuka.
Sebagai penutup, kita dapat melakukan Pengamatan berikut : jenis acara HARUS ditentukan oleh delegasi . Ini adalah hubungan utama antara acara dan delegasi dan dijelaskan dalam bagian II.18 Mendefinisikan peristiwa dari ECMA-335 (CLI) Partisi Saya ke VI :
Dalam penggunaan umum, TypeSpec (jika ada) mengidentifikasi delegasi yang tanda tangannya cocok dengan argumen yang diteruskan ke metode api acara.
Namun, fakta ini TIDAK menyiratkan bahwa suatu peristiwa menggunakan bidang delegasi dukungan . Sebenarnya, suatu peristiwa dapat menggunakan bidang dukungan dari berbagai jenis struktur data pilihan Anda. Jika Anda mengimplementasikan suatu acara secara eksplisit dalam C #, Anda bebas untuk memilih cara Anda menyimpan penangan acara (perhatikan bahwa penangan acara adalah contoh dari jenis acara , yang pada gilirannya adalah wajib merupakan jenis delegasi --- dari Pengamatan sebelumnya) ). Tetapi, Anda bisa menyimpan event handler tersebut (yang merupakan contoh delegasi) dalam struktur data seperti a List
atau a Dictionary
atau yang lain, atau bahkan dalam bidang delegasi pendukung. Tapi jangan lupa bahwa TIDAK wajib Anda menggunakan bidang delegasi.
Peristiwa di .net adalah kombinasi yang ditunjuk dari metode Tambahkan dan metode Hapus, yang keduanya mengharapkan beberapa jenis delegasi tertentu. Baik C # maupun vb.net dapat membuat kode secara otomatis untuk metode tambah dan hapus yang akan menentukan delegasi untuk menahan langganan acara, dan menambah / menghapus delegasi yang diteruskan ke / dari delegasi berlangganan tersebut. VB.net juga akan menghasilkan kode secara otomatis (dengan pernyataan RaiseEvent) untuk memohon daftar langganan jika dan hanya jika tidak kosong; untuk beberapa alasan, C # tidak menghasilkan yang terakhir.
Perhatikan bahwa walaupun umum untuk mengelola langganan acara menggunakan delegasi multicast, itu bukan satu-satunya cara untuk melakukannya. Dari perspektif publik, calon pelanggan acara perlu tahu cara memberi tahu objek bahwa ia ingin menerima acara, tetapi tidak perlu tahu mekanisme apa yang akan digunakan penerbit untuk mengangkat acara tersebut. Perhatikan juga bahwa sementara siapa pun yang mendefinisikan struktur data acara di .net tampaknya berpikir harus ada sarana publik untuk meningkatkannya, baik C # maupun vb.net tidak menggunakan fitur itu.
Untuk mendefinisikan tentang acara dengan cara sederhana:
Acara adalah REFERENSI untuk seorang delegasi dengan dua batasan
Di atas dua adalah titik lemah untuk delegasi dan ditangani jika terjadi. Contoh kode lengkap untuk menunjukkan perbedaan dalam fiddler ada di sini https://dotnetfiddle.net/5iR3fB .
Alihkan komentar antara Event dan Delegasi dan kode klien yang memanggil / menetapkan nilai untuk didelegasikan untuk memahami perbedaannya
Ini kode inline.
/*
This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
Event is an delegate reference with two restrictions for increased protection
1. Cannot be invoked directly
2. Cannot assign value to delegate reference directly
Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/
public class RoomTemperatureController
{
private int _roomTemperature = 25;//Default/Starting room Temperature
private bool _isAirConditionTurnedOn = false;//Default AC is Off
private bool _isHeatTurnedOn = false;//Default Heat is Off
private bool _tempSimulator = false;
public delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
// public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above),
public RoomTemperatureController()
{
WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
}
private void InternalRoomTemperatuerHandler(int roomTemp)
{
System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
}
//User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
public bool TurnRoomTeperatureSimulator
{
set
{
_tempSimulator = value;
if (value)
{
SimulateRoomTemperature(); //Turn on Simulator
}
}
get { return _tempSimulator; }
}
public void TurnAirCondition(bool val)
{
_isAirConditionTurnedOn = val;
_isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
}
public void TurnHeat(bool val)
{
_isHeatTurnedOn = val;
_isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
}
public async void SimulateRoomTemperature()
{
while (_tempSimulator)
{
if (_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned On
if (_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned On
System.Console.WriteLine("Temperature :" + _roomTemperature);
if (WhenRoomTemperatureChange != null)
WhenRoomTemperatureChange(_roomTemperature);
System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
}
}
}
public class MySweetHome
{
RoomTemperatureController roomController = null;
public MySweetHome()
{
roomController = new RoomTemperatureController();
roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
//roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
//roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
roomController.SimulateRoomTemperature();
System.Threading.Thread.Sleep(5000);
roomController.TurnAirCondition (true);
roomController.TurnRoomTeperatureSimulator = true;
}
public void TurnHeatOrACBasedOnTemp(int temp)
{
if (temp >= 30)
roomController.TurnAirCondition(true);
if (temp <= 15)
roomController.TurnHeat(true);
}
public static void Main(string []args)
{
MySweetHome home = new MySweetHome();
}
}
Delegasi adalah pointer fungsi tipe-aman. Acara adalah implementasi dari pola desain penerbit-pelanggan menggunakan delegasi.
Jika Anda memeriksa Bahasa Intermediate, Anda akan tahu .net compiler convert delegate to class disegel di IL dengan beberapa fungsi built-in, seperti invoke, beginInvoke, endInvoke, dan delegate class yang diwarisi dari kelas lain, mungkin disebut "SystemMulticast". Saya kira Event adalah kelas anak dari Delegasi dengan beberapa properti tambahan.
Perbedaan antara instance acara dan delegasi adalah, Anda tidak dapat menjalankan acara di luar deklarasi. Jika Anda mendeklarasikan acara di kelas A, Anda hanya dapat menjalankan acara ini di kelas A. Jika Anda mendeklarasikan delegasi di Kelas A, Anda dapat menggunakan delegasi ini di mana saja. Saya pikir ini adalah perbedaan utama di antara mereka