Mengapa kita membutuhkan kata kunci "peristiwa" saat menentukan peristiwa?

109

Saya tidak mengerti mengapa kita membutuhkan kata kunci "event" saat mendefinisikan event, padahal kita bisa melakukan hal yang sama tanpa menggunakan kata kunci "event", hanya dengan menggunakan delegasi.

misalnya

public delegate void CustomEventHandler(int a, string b);
public event CustomEventHandler customEvent;
customEvent += new CustomEventHandler(customEventHandler);
customEvent(1,"a"); // Raising the event

Di sini jika saya menghapus kata kunci "peristiwa" dari baris kedua, maka saya juga dapat meningkatkan acara dengan memanggil delegasi. Adakah yang bisa memberi tahu saya mengapa kata kunci acara ini diperlukan?

remaja
sumber
ok jika Anda tidak menggunakan kata kunci acara siapa pun yang dapat mengakses acara itu menggunakan objek kelas set ke NULL seperti objClass.SelectedIndexChanged = null. ini akan merusak kode dasar Anda. kata kunci acara memaksa pengguna untuk menetapkan sesuatu yang mirip dengan mendelegasikan menggunakan + =.
Sumit Kapadia

Jawaban:

142

Acara serupa lapangan dan bidang publik dari jenis delegasi terlihat serupa, tetapi sebenarnya sangat berbeda.

Acara pada dasarnya seperti properti - ini adalah sepasang metode tambah / hapus (bukan get / set properti). Ketika Anda mendeklarasikan acara seperti bidang (yaitu acara di mana Anda tidak menentukan sendiri tambahkan / hapus bit), acara publik dibuat, dan bidang dukungan pribadi. Ini memungkinkan Anda mengangkat acara secara pribadi, tetapi mengizinkan langganan publik. Dengan bidang delegasi publik, siapa pun dapat menghapus penangan acara orang lain, mengangkat acara itu sendiri, dll - ini adalah bencana enkapsulasi.

Untuk informasi lebih lanjut tentang acara (dan delegasi), baca artikel saya tentang topik ini . (Pada titik tertentu saya perlu memperbarui ini untuk C # 4, yang mengubah sedikit kejadian seperti lapangan. Intinya masih benar.)

Jon Skeet
sumber
17
Ini seribu kali lebih baik daripada penjelasan satu baris resmi MSDN: 'Kata kunci acara digunakan untuk mendeklarasikan acara di kelas penerbit.'
Cowlinator
37

Kata kunci acara melakukan 3 hal berbeda:

  1. Anda dapat menentukan acara di antarmuka, meskipun Anda tidak dapat menentukan bidang reguler di antarmuka.
  2. Ini mengubah visibilitas operator =dan ()(penugasan dan pemanggilan) menjadi privat, sehingga hanya kelas yang memuatnya yang dapat menjalankan acara atau mengganti semua metode yang ada di dalamnya. The -=dan +=operator masih bisa dipanggil pada sebuah acara dari luar kelas mendefinisikan itu (mereka mendapatkan akses pengubah yang Anda tulis di samping acara).
  3. Anda juga dapat mengganti cara -=dan +=berperilaku pada acara.
ek
sumber
2
Anda> MSDN. Terima kasih.
M. Azyoksul
26

Jawaban lainnya baik-baik saja; Saya hanya ingin menambahkan hal lain untuk dipikirkan.

Pertanyaan Anda adalah "mengapa kita membutuhkan acara ketika kita memiliki bidang tipe delegasi?" Saya akan memperluas pertanyaan itu: mengapa Anda memerlukan metode, properti, peristiwa, konstruktor atau finalisator contoh jika Anda memiliki bidang tipe delegasi? Mengapa Anda memerlukan hal lain selain bidang yang berisi nilai dan delegasi dalam satu tipe? Kenapa tidak katakan saja

class C
{
    private int z;
    public readonly Func<int, int> M = (int x)=>{ return x+z; }
    // ... and so on
}

?

Anda tidak membutuhkan metode, properti, atau peristiwa. Kami memberikan hal itu kepada Anda karena metode, properti, dan pola desain acara penting dan berguna, dan layak memiliki cara yang standar, terdokumentasi, dan jelas untuk menerapkannya dalam bahasa.

Eric Lippert
sumber
4
Wow! Ini mengingatkan mengapa saya suka c #! Dari semua bahasa yang pernah saya gunakan, ia memiliki keseimbangan yang tepat antara kekompakan, fleksibilitas, dan semantik yang dapat dibaca. Satu-satunya bahasa yang sebanding adalah Object Pascal.
ATL_DEV
1
@ATL_DEV: Ada alasan untuk itu. Arsitek bahasa C #, Anders Hejlsberg, sebelumnya adalah arsitek Delphi, yang merupakan bahasa berbasis Object Pascal.
Eric Lippert
9

Ini sebagian diperlukan karena jika Anda menghilangkan eventkata kunci, itu merusak enkapsulasi. Jika itu hanya delegasi multicast publik, siapa pun dapat memintanya, menyetelnya ke null, atau merusaknya. Jika kelas yang dipanggil MailNotifierada dan memiliki peristiwa yang dipanggil MailReceived, tidak masuk akal jika tipe lain dapat mengaktifkan peristiwa itu melalui pemanggilan mailNotifier.MailReceived();

Di sisi lain, Anda hanya dapat mencampuri dan menjalankan peristiwa 'field like' dari jenis yang mendefinisikannya.

Jika Anda ingin merahasiakan permintaan acara Anda, tidak ada yang dapat menghentikan Anda melakukan hal seperti ini:

public class MyClassWithNonFieldLikeEvent
{
   private CustomEventHandler m_delegate;

   public void Subscribe(CustomEventHandler handler) 
   {
      m_delegate += handler;        
   }

   public void Unsubscribe(CustomEventHandler handler)
   {          
      m_delegate -= handler;
   }

   private void DoSomethingThatRaisesEvent()
   {
      m_delegate.Invoke(...);
   }       
}

... tapi itu adalah keseluruhan kode hanya untuk (lebih atau kurang) melakukan apa yang sudah diberikan event field kepada kita.

Mark Simpson
sumber
Ini juga akan lebih sulit untuk hal-hal seperti desainer untuk menggunakan ... Anda pada dasarnya akan mengandalkan konvensi penamaan untuk metode, daripada ada metadata publik yang mengatakan "ini adalah acara".
Jon Skeet
3

Acara memiliki keunggulan berbeda dibandingkan dengan bidang delegasi. Peristiwa dapat didefinisikan dalam antarmuka berbeda dengan bidang, menambahkan abstraksi ke kode, dan bahkan yang lebih penting: Peristiwa hanya dapat dipanggil dari dalam kelas yang menentukan. Dalam kasus Anda, siapa pun dapat memanggil acara tersebut, mungkin menghancurkan kode Anda.

Lihat posting blog ini untuk informasi lebih lanjut.

Femaref
sumber
2
Anda seharusnya tidak benar-benar membandingkan acara dan delegasi - bandingkan acara dan bidang publik dengan jenis delegasi . Dan tidak, framework tidak memerlukan peristiwa untuk memiliki tanda tangan tersebut. Anda dapat membuat acara dengan jenis delegasi apa pun yang Anda suka.
Jon Skeet
3

delegasi adalah tipe referensi. Itu mewarisi MulticastDelegate . acara adalah Pengubah. peristiwaadalah pengubah khusus untuk delegasi. Ini memodifikasi beberapa aksesibilitas fungsi / metode, misalnya metode Panggil. Setelah diubah oleh peristiwa pengubah, instance delegasi menjadi konsep "Acara" baru. Jadi Acara hanyalah salah satu delegasi yang dimodifikasi. Anda tidak dapat secara langsung mengubah referensi atau meminta sebuah Acara di luar kelas tempat Acara itu ditentukan, tetapi Anda dapat mengubah referensi atau memanggil instance delegasi normal. Acara memberikan perlindungan tambahan, sehingga Acara memiliki lebih banyak fitur keamanan. Saat Anda berada di luar kelas tempat acara ditentukan, Anda diizinkan untuk melakukan dua jenis operasi ke acara tersebut, "+ =" dan "- =". Tetapi Anda dapat mengakses semua bidang publik, properti, metode, dll. Dari instance delegasi normal. Inilah salah satu contohnya:

namespace DelegateEvent
{
    //the following line behave as a class. It is indeed a reference type
    public delegate void MyDelegate(string inputs);

    //The following line is illegal. It can only be an instance. so it cannot be directly under namespace
    //public event MyDelegate MyEvent;


    public class MyClassA
    {
        public event MyDelegate MyEventA;
        public MyDelegate MyDelegateA;


        System.Threading.ManualResetEvent MyResetEvent = new System.Threading.ManualResetEvent(false);
        public void TryToDoSomethingOnMyDelegateA()
        {
            if (MyDelegateA != null)
            {
                //User can assecc all the public methods.
                MyDelegateA("I can invoke detegate in classA");         //invoke delegate
                MyDelegateA.Invoke("I can invoke detegate in classA");  //invoke delegate
                IAsyncResult result = MyDelegateA.BeginInvoke("I can invoke detegate in classA", MyAsyncCallback, MyResetEvent);    //Async invoke
                //user can check the public properties and fields of delegate instance
                System.Reflection.MethodInfo delegateAMethodInfo = MyDelegateA.Method;

                MyDelegateA = testMethod;                   //reset reference
                MyDelegateA = new MyDelegate(testMethod);   //reset reference
                MyDelegateA = null;                         //reset reference


                MyDelegateA += testMethod;                  //Add delegate
                MyDelegateA += new MyDelegate(testMethod);  //Add delegate
                MyDelegateA -= testMethod;                  //Remove delegate
                MyDelegateA -= new MyDelegate(testMethod);  //Remove delegate
            }
        }

        public void TryToDoSomethingOnMyEventA()
        {
            if (MyEventA != null)
            {
                MyEventA("I can invoke Event in classA");           //invoke Event
                MyEventA.Invoke("I can invoke Event in classA");    //invoke Event
                IAsyncResult result = MyEventA.BeginInvoke("I can invoke Event in classA", MyAsyncCallback, MyResetEvent);      //Async invoke
                //user can check the public properties and fields of MyEventA
                System.Reflection.MethodInfo delegateAMethodInfo = MyEventA.Method;


                MyEventA = testMethod;                   //reset reference
                MyEventA = new MyDelegate(testMethod);   //reset reference
                MyEventA = null;                         //reset reference


                MyEventA += testMethod;                  //Add delegate
                MyEventA += new MyDelegate(testMethod);  //Add delegate
                MyEventA -= testMethod;                  //Remove delegate
                MyEventA -= new MyDelegate(testMethod);  //Remove delegate
            }
        }

        private void MyAsyncCallback(System.IAsyncResult result)
        {
            //user may do something here
        }
        private void testMethod(string inputs)
        {
            //do something
        }

    }
    public class MyClassB
    {
        public MyClassB()
        {
            classA = new MyClassA();
        }
        public MyClassA classA;
        public string ReturnTheSameString(string inputString)
        {
            return inputString;
        }


        public void TryToDoSomethingOnMyDelegateA()
        {
            if (classA.MyDelegateA != null)
            {
                //The following two lines do the same job --> invoke the delegate instance
                classA.MyDelegateA("I can invoke delegate which defined in class A in ClassB");
                classA.MyDelegateA.Invoke("I can invoke delegate which defined in class A in ClassB");
                //Async invoke is also allowed

                //user can check the public properties and fields of delegate instance
                System.Reflection.MethodInfo delegateAMethodInfo = classA.MyDelegateA.Method;

                classA.MyDelegateA = testMethod;                   //reset reference
                classA.MyDelegateA = new MyDelegate(testMethod);   //reset reference
                classA.MyDelegateA = null;                         //reset reference


                classA.MyDelegateA += testMethod;                  //Add delegate
                classA.MyDelegateA += new MyDelegate(testMethod);  //Add delegate
                classA.MyDelegateA -= testMethod;                  //Remove delegate
                classA.MyDelegateA -= new MyDelegate(testMethod);  //Remove delegate

            }

        }
        public void TryToDoSomeThingMyEventA()
        {
            //check whether classA.MyEventA is null or not is not allowed
            //Invoke classA.MyEventA is not allowed
            //Check properties and fields of classA.MyEventA is not allowed
            //reset classA.MyEventA reference is not allowed

            classA.MyEventA += testMethod;                  //Add delegate
            classA.MyEventA += new MyDelegate(testMethod);  //Add delegate
            classA.MyEventA -= testMethod;                  //Remove delegate
            classA.MyEventA -= new MyDelegate(testMethod);  //Remove delegate
        }

        private void testMethod(string inputs)
        {
            //do something here
        }
    }
}
wuyin lyu
sumber