Po co nam słowo kluczowe „event” przy definiowaniu wydarzeń?


109

Nie rozumiem, dlaczego potrzebujemy słowa kluczowego „event” podczas definiowania wydarzeń, kiedy możemy zrobić to samo bez użycia słowa kluczowego „event”, po prostu używając delegatów.

na przykład

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

Tutaj, jeśli usunę słowo kluczowe „event” z drugiej linii, wtedy również mogę podnieść zdarzenie, wywołując delegata. Czy ktoś może mi powiedzieć, dlaczego to słowo kluczowe jest potrzebne?


ok, jeśli nie używasz słowa kluczowego zdarzenia, każdy, kto może uzyskać dostęp do tego zdarzenia za pomocą obiektu klasy, ustaw go na NULL, jak objClass.SelectedIndexChanged = null. spowoduje to awarię kodu źródłowego. słowo kluczowe event wymusza na użytkowniku przypisanie czegoś podobnego do delegata za pomocą + =.
Sumit Kapadia,

Odpowiedzi:


142

Wydarzenia przypominające pola i publiczne pola typów delegatów wyglądają podobnie, ale w rzeczywistości są bardzo różne.

Zdarzenie jest zasadniczo podobne do właściwości - jest to para metod dodawania / usuwania (zamiast pobierania / zbioru właściwości). Kiedy deklarujesz zdarzenie podobne do pola (tj. Takie, w którym nie określasz samodzielnie dodawania / usuwania bitów), tworzone jest zdarzenie publiczne i prywatne pole zapasowe. Dzięki temu możesz podnieść wydarzenie prywatnie, ale zezwól na publiczną subskrypcję. Z publicznym polem delegata każdy może usunąć programy obsługi zdarzeń innych osób, samodzielnie zgłosić zdarzenie itp. - jest to katastrofa związana z hermetyzacją.

Aby uzyskać więcej informacji na temat wydarzeń (i delegatów), przeczytaj mój artykuł na ten temat . (W pewnym momencie muszę zaktualizować to dla C # 4, co bardzo nieznacznie zmienia zdarzenia polowe. Istota tego jest wciąż poprawna.)


17
Jest to tysiąc razy lepsze niż oficjalne jednowierszowe wyjaśnienie MSDN: „Słowo kluczowe zdarzenia służy do zadeklarowania zdarzenia w klasie wydawcy”.
cowlinator

37

Słowo kluczowe wydarzenia ma 3 różne funkcje:

  1. Możesz zdefiniować zdarzenie w interfejsie, nawet jeśli nie możesz zdefiniować zwykłych pól w interfejsach.
  2. Zmienia widoczność operatorów =i ()(przypisanie i wywołanie) na prywatne, dzięki czemu tylko klasa zawierająca może wywołać zdarzenie lub przesłonić wszystkie zawarte w nim metody. -=I +=operatorzy nadal można powoływać się na razie z zewnątrz klasę określającą go (oni uzyskać dostęp modyfikator napisałeś obok zdarzenia).
  3. Możesz także zmienić sposób -=i +=zachowywać się podczas wydarzeń.

2
ty> MSDN. Dzięki.
M. Azyoksul

26

Pozostałe odpowiedzi są w porządku; Chciałbym tylko dodać coś innego do przemyślenia.

Twoje pytanie brzmi „po co nam wydarzenia, skoro mamy pola typu delegat?” Rozszerzyłbym to pytanie: dlaczego potrzebujesz metod, właściwości, zdarzeń, konstruktorów instancji lub finalizatorów, jeśli masz pola typu delegata? Dlaczego potrzebujesz czegoś innego niż pola, które zawierają wartości i delegatów w typie? Dlaczego po prostu nie powiedzieć

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

?

Nie potrzebujesz metod, właściwości ani zdarzeń. Dajemy Ci to, ponieważ wzorce projektowania metod, właściwości i zdarzeń są ważne i użyteczne oraz zasługują na standardowy, udokumentowany i przejrzysty sposób ich implementacji w języku.


4
Łał! Przypomina, dlaczego uwielbiam c #! Ze wszystkich języków, z którymi pracowałem, ma właściwą równowagę między zwartością, elastycznością i czytelną semantyką. Jedynym porównywalnym językiem jest Object Pascal.
ATL_DEV,

1
@ATL_DEV: Jest ku temu powód. Architekt języka C #, Anders Hejlsberg, był wcześniej architektem Delphi, który był językiem opartym na Object Pascal.
Eric Lippert

9

Jest to częściowo potrzebne, ponieważ jeśli pominiesz eventsłowo kluczowe, zepsuje hermetyzację. Jeśli jest to tylko publiczny delegat multiemisji, każdy może go wywołać, ustawić na wartość null lub manipulować nim. Jeśli klasa wywołana MailNotifieristnieje i ma wywołane zdarzenie MailReceived, nie ma sensu, aby inne typy mogły wywołać to zdarzenie przez wywołanie mailNotifier.MailReceived();

Z drugiej strony możesz wtrącać się i wywoływać zdarzenia typu „field like” tylko z typu, który je zdefiniował.

Jeśli chcesz, aby wywołanie wydarzenia było prywatne, nic nie stoi na przeszkodzie, abyś zrobił coś takiego:

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

... ale to cały ładunek kodu tylko po to, aby (mniej więcej) robić to, co już dają nam zdarzenia przypominające pola.


Byłoby też trudniej używać rzeczy takich jak projektanci ... w zasadzie polegałbyś na konwencjach nazewnictwa metod, zamiast na publicznych metadanych mówiących „to jest zdarzenie”.
Jon Skeet,

3

Zdarzenia mają wyraźne zalety w porównaniu z polami delegatów. Zdarzenia można definiować w interfejsach w przeciwieństwie do pól, dodając abstrakcję do kodu, a co ważniejsze: zdarzenia można wywoływać tylko z wnętrza definiującej klasy. W Twoim przypadku każdy mógłby wywołać wydarzenie, prawdopodobnie niszcząc Twój kod.

Więcej informacji można znaleźć w tym poście na blogu .


2
Nie powinieneś porównywać wydarzeń i delegatów - porównaj zdarzenia i pola publiczne z typem delegata . I nie, framework nie wymaga, aby zdarzenia miały tę sygnaturę. Możesz utworzyć wydarzenie dowolnego typu pełnomocnika.
Jon Skeet,

3

delegat jest typem referencyjnym. Dziedziczy MulticastDelegate . zdarzenie jest modyfikatorem. zdarzeniejest specjalnym modyfikatorem dla delegata. Modyfikuje dostępność niektórych funkcji / metod, na przykład metodę Invoke. Po zmodyfikowaniu przez zdarzenie modyfikatora instancja delegata staje się nową koncepcją „Zdarzenie”. Więc Event to tylko jeden zmodyfikowany delegat. Nie można bezpośrednio zmienić odwołania ani wywołać zdarzenia poza klasą, w której zdarzenie zostało zdefiniowane, ale można zmienić odwołanie lub wywołać normalne wystąpienie delegata. Event zapewnia dodatkową ochronę, dzięki czemu Event ma więcej funkcji bezpieczeństwa. Kiedy jesteś poza klasą, w której zdarzenie zostało zdefiniowane, możesz wykonać dwa rodzaje operacji na zdarzeniu, „+ =” i „- =”. Ale możesz uzyskać dostęp do wszystkich pól publicznych, właściwości, metod itp. Normalnej instancji delegata. Oto jeden przykład:

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
        }
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.