Perché abbiamo bisogno della parola chiave "evento" durante la definizione degli eventi?


109

Non capisco perché abbiamo bisogno della parola chiave "evento" durante la definizione degli eventi, quando possiamo fare la stessa cosa senza utilizzare la parola chiave "evento", semplicemente utilizzando i delegati.

per esempio

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

Qui se rimuovo la parola chiave "evento" dalla seconda riga, allora posso anche sollevare l'evento invocando il delegato. Qualcuno può dirmi perché è necessaria questa parola chiave dell'evento?


ok se non usi la parola chiave dell'evento chiunque possa accedere a quell'evento usando l'oggetto classe, impostalo su NULL come objClass.SelectedIndexChanged = null. questo bloccherà il codice sottostante. La parola chiave evento impone all'utente di assegnare qualcosa di simile al delegato utilizzando + =.
Sumit Kapadia

Risposte:


142

Gli eventi di tipo campo e i campi pubblici dei tipi di delegato sembrano simili, ma in realtà sono molto diversi.

Un evento è fondamentalmente come una proprietà: è una coppia di metodi di aggiunta / rimozione (invece del get / set di una proprietà). Quando dichiari un evento simile a un campo (cioè uno in cui non specifichi tu stesso i bit di aggiunta / rimozione) viene creato un evento pubblico e un campo di supporto privato. Ciò ti consente di aumentare l'evento in privato, ma consentire la sottoscrizione pubblica. Con un campo delegato pubblico, chiunque può rimuovere i gestori di eventi di altre persone, sollevare l'evento da solo, ecc. - È un disastro di incapsulamento.

Per ulteriori informazioni su eventi (e delegati) leggi il mio articolo su questo argomento . (Ad un certo punto ho bisogno di aggiornarlo per C # 4, che cambia leggermente gli eventi di tipo campo. Il succo è comunque corretto.)


17
Questo è mille volte meglio della spiegazione ufficiale di una riga di MSDN: "La parola chiave evento viene utilizzata per dichiarare un evento in una classe editore".
cowlinator

37

La parola chiave evento fa 3 cose diverse:

  1. È possibile definire un evento in un'interfaccia, anche se non è possibile definire campi regolari nelle interfacce.
  2. Cambia la visibilità degli operatori =e ()(assegnazione e invocazione) in privata, in modo che solo la classe contenitore possa richiamare l'evento o sovrascrivere tutti i metodi in esso contenuti. Gli operatori -=e +=possono ancora essere invocati su un evento dall'esterno della classe che lo definisce (ottengono il modificatore di accesso che hai scritto accanto all'evento).
  3. Puoi anche ignorare il modo -=e +=comportarti sugli eventi.

2
tu> MSDN. Grazie.
M. Azyoksul

26

Le altre risposte vanno bene; Vorrei solo aggiungere qualcos'altro a cui pensare.

La tua domanda è "perché abbiamo bisogno di eventi quando abbiamo campi di tipo delegato?" Vorrei estendere la domanda: perché sono necessari metodi, proprietà, eventi, costruttori di istanze o finalizzatori se si dispone di campi di tipo delegato? Perché hai bisogno di qualcosa di diverso dai campi che contengono valori e delegati in un tipo? Perché non basta dire

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

?

Non hai bisogno di metodi, proprietà o eventi. Ti diamo questa roba perché il metodo, la proprietà e gli schemi di progettazione degli eventi sono importanti e utili e meritano di avere un modo standard, documentato e chiaro per implementarli nel linguaggio.


4
Wow! Mi ricorda perché amo il c #! Di tutti i linguaggi con cui ho lavorato, ha il giusto equilibrio tra compattezza, flessibilità e semantica leggibile. L'unico linguaggio comparabile è Object Pascal.
ATL_DEV

1
@ATL_DEV: C'è una ragione per questo. L'architetto del linguaggio C #, Anders Hejlsberg, era in precedenza l'architetto di Delphi, un linguaggio basato su Object Pascal.
Eric Lippert

9

In parte è necessario perché se si omette la eventparola chiave, si rompe l'incapsulamento. Se è solo un delegato multicast pubblico, chiunque può invocarlo, impostarlo su null o manometterlo. Se una classe chiamata MailNotifieresiste e ha un evento chiamato MailReceived, non ha senso che altri tipi siano in grado di attivare quell'evento tramite la chiamata mailNotifier.MailReceived();

D'altra parte, puoi solo immischiarti e invocare eventi "tipo campo" dal tipo che lo ha definito.

Se desideri mantenere privata la chiamata dell'evento, non c'è nulla che ti impedisca di fare qualcosa del genere:

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

... ma questo è un intero carico di codice solo per (più o meno) fare ciò che gli eventi simili a campi ci danno già.


Sarebbe anche più difficile da usare per cose come i designer ... fondamentalmente faresti affidamento su convenzioni di denominazione per i metodi, invece di esserci metadati pubblici che dicono "questo è un evento".
Jon Skeet

3

Gli eventi hanno vantaggi distinti rispetto ai campi delegato. Gli eventi possono essere definiti nelle interfacce in contrasto con i campi, aggiungendo astrazione al codice e, cosa ancora più importante: gli eventi possono essere chiamati solo dall'interno della classe di definizione. Nel tuo caso, chiunque potrebbe chiamare l'evento, magari distruggendo il tuo codice.

Vedi questo post del blog per ulteriori informazioni.


2
Non dovresti davvero confrontare eventi e delegati : confronta eventi e campi pubblici con un tipo di delegato . E no, il framework non richiede che gli eventi abbiano quella firma. Puoi creare un evento di qualsiasi tipo di delegato che preferisci.
Jon Skeet,

3

delegate è un tipo di riferimento. Eredita MulticastDelegate . l'evento è un modificatore. eventoè un modificatore speciale per delegato. Modifica l'accessibilità di alcune funzioni / metodi, ad esempio il metodo Invoke. Dopo essere stato modificato dall'evento modificatore, un'istanza del delegato diventa un nuovo concetto "Evento". Quindi Event è solo un delegato modificato. Non è possibile modificare direttamente il riferimento o richiamare un evento all'esterno della classe in cui è stato definito l'evento, ma è possibile modificare il riferimento o richiamare una normale istanza di delegato. Event fornisce una protezione aggiuntiva, in modo che Event abbia più funzioni di sicurezza. Quando si è fuori dalla classe in cui è stato definito l'evento, è possibile eseguire due tipi di operazioni sull'evento, "+ =" e "- =". Ma puoi accedere a tutti i campi pubblici, proprietà, metodi ecc. Di una normale istanza di delegato. Ecco un esempio:

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
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.