Why do we need the "event" keyword while defining events?

C#.Net

C# Problem Overview


I don't understand why do we need the "event" keyword while defining events, when we can do the same thing without using "event" keyword, just by using the 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 also I can raise the event by invoking the delegate. Can anybody please tell me why is this event keyword needed ?

C# Solutions


Solution 1 - C#

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.)

Solution 2 - C#

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.

Solution 3 - C#

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.

Solution 4 - C#

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.

Solution 5 - C#

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.

Solution 6 - C#

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
        }
    }
}

Solution 7 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionteenupView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#OakView Answer on Stackoverflow
Solution 3 - C#Eric LippertView Answer on Stackoverflow
Solution 4 - C#Mark SimpsonView Answer on Stackoverflow
Solution 5 - C#FemarefView Answer on Stackoverflow
Solution 6 - C#wuyin lyuView Answer on Stackoverflow
Solution 7 - C#rollschView Answer on Stackoverflow