Olayları tanımlarken neden "olay" anahtar kelimesine ihtiyacımız var?


109

Olayları tanımlarken neden "event" anahtar kelimesine ihtiyacımız olduğunu anlamıyorum, "event" anahtar kelimesini kullanmadan sadece delegeleri kullanarak aynı şeyi yapabiliyoruz.

Örneğin

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

Burada ikinci satırdan "olay" anahtar sözcüğünü kaldırırsam, temsilciyi çağırarak da olayı başlatabilirim. Herhangi biri bana bu etkinlik anahtar kelimesine neden ihtiyaç duyulduğunu söyleyebilir mi?


ok, olay anahtar kelimesini kullanmıyorsanız, bu olaya sınıf nesnesini kullanarak erişebilen herhangi biri onu objClass.SelectedIndexChanged = null gibi NULL olarak ayarlayın. bu, temel kodunuzu çökertecektir. olay anahtar sözcüğü, kullanıcıyı + = kullanarak temsilciye benzer bir şey atamaya zorlar.
Sumit Kapadia

Yanıtlar:


142

Alan benzeri olaylar ve temsilci türlerinin genel alanları benzer görünür , ancak aslında çok farklıdır.

Bir olay temelde bir mülk gibidir - bir çift ekleme / kaldırma yöntemidir (bir mülkün get / set yerine). Alan benzeri bir olay bildirdiğinizde (yani, bitleri ekleme / kaldırma işlemini kendiniz belirtmediğiniz bir olay), genel bir olay ve özel bir destek alanı yaratılır. Bu, etkinliği özel olarak oluşturmanıza, ancak genel aboneliğe izin vermenize olanak tanır. Herkese açık bir temsilci alanıyla, herkes başkalarının olay işleyicilerini kaldırabilir, olayı kendileri yükseltebilir, vb - bu bir kapsülleme felaketi.

Etkinlikler (ve temsilciler) hakkında daha fazla bilgi için bu konuyla ilgili makalemi okuyun . (Bir noktada, alan benzeri olayları çok az değiştiren C # 4 için bunu güncellemem gerekiyor. Yine de özü hala doğru.)


17
Bu, MSDN'nin resmi tek satırlık açıklamasından bin kat daha iyidir: 'Etkinlik anahtar sözcüğü, bir yayıncı sınıfındaki bir olayı bildirmek için kullanılır.'
cowlinator

37

Etkinlik anahtar kelimesi 3 farklı şey yapar:

  1. Arayüzlerde normal alanlar tanımlayamasanız bile, arayüzde bir olay tanımlayabilirsiniz.
  2. =Ve ()işleçlerinin (atama ve çağırma) görünürlüğünü özel olarak değiştirir, böylece yalnızca içeren sınıf olayı çağırabilir veya içinde bulunan tüm yöntemleri geçersiz kılabilir. -=Ve +=operatörler hala dışarıdan bir etkinlikte sınıfını (onlar erişim değiştirici bir sonraki etkinliğe yazdığı olsun) tanımlayarak çağrılabilir.
  3. Ayrıca olayları geçersiz kılabilir -=ve +=olaylara göre davranabilirsiniz.

2
siz> MSDN. Teşekkürler.
M.Azyoksul

26

Diğer cevaplar gayet iyi; Sadece düşünmek için başka bir şey eklemek istiyorum.

Sorunuz, "temsilci türü alanlarımız varken neden olaylara ihtiyacımız var?" Bu soruyu genişleteceğim: Temsilci tipi alanlarınız varsa neden yöntemlere, özelliklere, olaylara, örnek oluşturuculara veya sonlandırıcılara ihtiyacınız var? Bir türdeki değerler ve temsilciler içeren alanlardan başka bir şeye neden ihtiyacınız var ? Neden sadece söylemiyorsun

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

?

Yöntemlere, özelliklere veya olaylara ihtiyacınız yok . Size bunları veriyoruz çünkü yöntem, özellik ve olay tasarım modelleri önemli ve kullanışlı ve bunları dilde uygulamak için standart, belgelenmiş, açık bir yola sahip olmayı hak ediyor.


4
Vaov! C # neden sevdiğimi hatırlatıyor! Çalıştığım tüm diller arasında doğru kompaktlık, esneklik ve okunabilir semantik dengesine sahip. Karşılaştırılabilir tek dil, Object Pascal'dır.
ATL_DEV

1
@ATL_DEV: Bunun bir nedeni var. C # dilinin mimarı Anders Hejlsberg, daha önce Object Pascal'a dayanan bir dil olan Delphi'nin mimarıydı.
Eric Lippert

9

Kısmen gereklidir, çünkü eventanahtar kelimeyi atlarsanız, kapsüllemeyi bozar. Yalnızca bir genel çok noktaya yayın temsilcisiyse, herkes onu çağırabilir, boşa ayarlayabilir veya kurcalayabilir. Çağrılan bir sınıf MailNotifiervarsa ve çağrılan bir olayı varsa MailReceived, diğer türlerin bu olayı çağırarak tetikleyebilmesi anlamsızdır mailNotifier.MailReceived();

Öte yandan, yalnızca onu tanımlayan türden "alan benzeri" olaylara müdahale edebilir ve bunları çağırabilirsiniz.

Etkinlik çağrınızı gizli tutmak istiyorsanız, böyle bir şey yapmanızı engelleyecek hiçbir şey yoktur:

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(...);
   }       
}

... ancak bu, alan benzeri olayların bize zaten verdiği şeyi (aşağı yukarı) yapmak için tam bir kod yükü.


Tasarımcı gibi şeyler için kullanmak da daha zor olurdu ... "Bu bir olaydır" diyen genel meta veriler yerine, temelde yöntemler için adlandırma kurallarına güveniyor olacaksınız.
Jon Skeet

3

Olayların, temsilci alanlarına kıyasla farklı avantajları vardır. Olaylar, alanların aksine, koda soyutlama ekleyerek ve daha da önemlisi arayüzlerde tanımlanabilir: Olaylar yalnızca tanımlayıcı sınıfın içinden çağrılabilir. Sizin durumunuzda, herhangi biri olayı arayabilir ve muhtemelen kodunuzu yok edebilir.

Daha fazla bilgi için bu blog gönderisine bakın .


2
Olayları ve temsilcileri gerçekten karşılaştırmamalısınız - olayları ve genel alanları bir temsilci türüyle karşılaştırın . Ve hayır, çerçeve gelmez o imzaya sahip Olayları gerektirir. İstediğiniz herhangi bir temsilci türünde bir etkinlik oluşturabilirsiniz.
Jon Skeet

3

delege bir referans türüdür. MulticastDelegate'i devralır . olay bir Değiştiricidir. Etkinliktemsilci için özel bir değiştiricidir. Bazı işlev / yöntem erişilebilirliğini değiştirir, örneğin Invoke yöntemi. Değiştirici olay tarafından değiştirildikten sonra, bir temsilci örneği yeni bir kavram olan "Olay" haline gelir. Yani Etkinlik yalnızca bir değiştirilmiş delegedir. Referansı doğrudan değiştiremez veya Etkinliğin tanımlandığı sınıfın dışında bir Olayı çağıramazsınız, ancak referansı değiştirebilir veya normal bir temsilci örneğini çağırabilirsiniz. Olay ek koruma sağlar, böylece Olay daha fazla güvenlik özelliğine sahip olur. Etkinliğin tanımlandığı sınıfın dışında olduğunuzda, Etkinliğe "+ =" ve "- =" olmak üzere iki tür işlem yapmanıza izin verilir. Ancak normal bir temsilci örneğinin tüm genel alanına, özelliklerine, yöntemlerine vb. Erişebilirsiniz. İşte bir örnek:

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
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.