136

I don't understand why we need the event keyword while defining events, when we can do the same thing without using the event keyword, just by using delegates.

E.g.:

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

Here, if I remove the event keyword from the second line, then I can also raise the event by invoking the delegate.

Can anybody please tell me why this event keyword is needed?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
teenup
  • 7,459
  • 13
  • 63
  • 122
  • 1
    ok if you not use event keyword anybody who can access that event using class object set it to NULL like objClass.SelectedIndexChanged = null. this will crash your underlying code. event keyword enforce user to assign something similar to delegate using +=. – Sumit Kapadia Jul 05 '13 at 07:57

7 Answers7

173

Field-like events and public fields of delegate types look similar, but are actually very different.

An event is fundamentally like a property - it's a pair of add/remove methods (instead of the get/set of a property). When you declare a field-like event (i.e. one where you don't specify the add/remove bits yourself) a public event is created, and a private backing field. This lets you raise the event privately, but allow public subscription. With a public delegate field, anyone can remove other people's event handlers, raise the event themselves, etc - it's an encapsulation disaster.

For more on events (and delegates) read my article on this topic. (At some point I need to update this for C# 4, which changes field-like events very slightly. The gist of it is still correct though.)

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 31
    This is a thousand times better than MSDN's official one-line explanation: 'The event keyword is used to declare an event in a publisher class.' – cowlinator Apr 13 '16 at 18:17
  • 13
    @cowlinator "Hmmm... yes. The floor here is made out of floor." – shieldgenerator7 Dec 05 '20 at 00:24
  • I wish they had even handler.clear() or event handler.imlazycanyouaddonlyifnot already added instead of the ugly -+ += I see everywhere – rollsch Apr 04 '23 at 22:09
53

The event keyword does 3 different things:

  1. You can define an event in an interface, even though you cannot define regular fields in interfaces.
  2. It changes the visibility of the = and () operators (assignment and invocation) to private, so that only the containing class can invoke the event or override all the methods contained in it. The -= and += operators can still be invoked on an event from outside the class defining it (they get the access modifier you wrote next to the event).
  3. You can also override the way -= and += behave on events.
Oak
  • 26,231
  • 8
  • 93
  • 152
31

The other answers are fine; I'd just like to add something else to think about.

Your question is "why do we need events when we have fields of delegate type?" I would extend that question: why do you need methods, properties, events, instance constructors or finalizers if you have fields of delegate type? Why do you need anything other than fields that contain values and delegates in a type? Why not just say

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

?

You don't need methods, properties or events. We give you that stuff because the method, property and event design patterns are important and useful, and deserve to have a standard, documented, clear way to implement them in the language.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 7
    Wow! It reminds why I love c#! Of all the languages I've worked with, it has the right balance of compactness, flexibility and readable semantics. The only comparable language is Object Pascal. – ATL_DEV Feb 21 '13 at 20:51
  • 5
    @ATL_DEV: There's a reason for that. The architect of the C# language, Anders Hejlsberg, was previously the architect of Delphi, which was a language based on Object Pascal. – Eric Lippert Oct 09 '19 at 23:25
  • Fair reply. But I think the event handler is one of the weaker examples of syntactic sugar, nothing like async etc – rollsch Apr 04 '23 at 22:11
11

It's partly needed because if you omit the event keyword, it breaks encapsulation. If it's just a public multicast delegate, anyone can invoke it, set it to null or tamper with it. If a class called MailNotifier exists and it has an event called MailReceived, it makes no sense for other types to be able to fire that event via calling mailNotifier.MailReceived();

On the other hand, you can only meddle with and invoke 'field like' events from the type that defined it.

If you wanted to keep your event invocation private, there's nothing to stop you doing something like this:

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

... but that's a whole load of code just to (more or less) do what field-like events already give us.

Mark Simpson
  • 23,245
  • 2
  • 44
  • 44
  • It would also be harder for things like designers to use... you'd basically be relying on naming conventions for methods, instead of there being public metadata saying "this is an event". – Jon Skeet Jun 12 '10 at 13:07
3

Events have distinct advantages compared to delegate-fields. Events can be defined in interfaces in contrast to fields, adding abstraction to the code, and even more importantly: Events can only be called from inside the defining class. In your case, anybody could call the event, possibly destroying your code.

See this blog post for further information.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • 2
    You shouldn't really be comparing events and *delegates* - compare events and *public fields with a delegate type*. And no, the framework *doesn't* require events to have that signature. You can create an event of any delegate type you like. – Jon Skeet Jun 12 '10 at 13:12
  • I see code with public wrappers calling events externally all the time... :( – rollsch Apr 04 '23 at 22:12
3

delegate is a reference type. It inherit MulticastDelegate. event is a Modifier. event is a special modifier for delegate. It modifies some function/method accessibility, for example Invoke method. After modified by modifier event, a delegate instance become an new concept "Event". So Event is just one modified delegate. You cannot directly change reference or invoke an Event outside the class where the Event was defined, but you can change reference or invoke a normal delegate instance. Event provide additional protection, so that Event have more safety features. When you are outside the class where the event was defined, your are allow to do two kind of operations to the Event, "+=" and "-=". But you can do access all the public field, properties, methods etc. of a normal delegate instance. Here is one example:

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
  • 31
  • 3
1

Using the website sharplab.io you can actually decompile what the "event" keyword does.

Eg the following program:

using System;
using System.ComponentModel;
public class C {
    
    public event EventHandler TestChanged;
    
    public void M() {
    }
}

Decompiles to the following:

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
public class C
{
    [CompilerGenerated]
    private EventHandler m_TestChanged;

    public event EventHandler TestChanged
    {
        [CompilerGenerated]
        add
        {
            EventHandler eventHandler = this.TestChanged;
            while (true)
            {
                EventHandler eventHandler2 = eventHandler;
                EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value);
                eventHandler = Interlocked.CompareExchange(ref this.TestChanged, value2, eventHandler2);
                if ((object)eventHandler == eventHandler2)
                {
                    break;
                }
            }
        }
        [CompilerGenerated]
        remove
        {
            EventHandler eventHandler = this.TestChanged;
            while (true)
            {
                EventHandler eventHandler2 = eventHandler;
                EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value);
                eventHandler = Interlocked.CompareExchange(ref this.TestChanged, value2, eventHandler2);
                if ((object)eventHandler == eventHandler2)
                {
                    break;
                }
            }
        }
    }

    public void M()
    {
    }
}

So you can literally write the same code as the above, it is just very wordy and prone to mistake. The event keyword handles this for you. Same as many other keywords like async etc. So it is really just syntactic sugar, that is all.

For fun try decompiling other keywords using sharplab.io to see. It is a great learning experience.

rollsch
  • 2,518
  • 4
  • 39
  • 65
  • 1
    Thanks for introducing the sharplab.io. – Doan Vu Mar 26 '23 at 04:30
  • 1
    It's magical. Try dnyspy for full programs. Some of the hacks they do are amazing. Eg switch statements with strings get converted into switch statements of hash codes and many other interesting things. – rollsch Apr 04 '23 at 22:07