34

I've come to recently understand that a C# 'event' really is. It isn't really anything, honestly. To sum up my findings: The event keyword is simply a modifier that only applies to delegates.

So, all the 'magic' of an event are the operations of a delegate. That's it. I've read through a lot of the Microsoft documentation, but there no sentence that summarizes in that manner so succinctly. To continue with my findings, delegate, class, and struct are all on the same 'level'. They are ways to define a 'object'. I don't mean 'object' as in the type, but an encapsulated concept of 'something'. Like how the word 'object' is used when saying object oriented programming.

Anyway, 'objects' have certain modifiers. For example, sealed, readonly, virtual, static, etc... This list can be found here. In the case of a delegate, it has the extra one called event. Event makes it so that when a delegate is declared as part of a class, it only exposes the add and remove methods as per the access modifier given to the event. These methods are defined in a similar nature to get and set of a property. Other operations of the delegate (assignment, read-access, method invocation, etc) are only allowed within the class in which the event delegate was declared. The other thing I find interesting, is that all delegates have the methods Invoke, BeginInvoke, and EndInvoke, yet you cannot navigate to view them within Visual Studio, nor could I find the documentation describing them...

Okay. So after knowing all that, what is the advantage of using the event keyword other than for modifying how the delegate can be accessed? It seems like, in many cases, I'd be better off simply declaring a delegate without the event keyword. A situation I ran into recently is that I wanted to create an abstract base class that contained 2 events. Any class that derived from this base class should be able to use the events like they are their own, similar to any other object of the class that is exposed to the derived class (aka, non-private, unless the derived class is in another assembly, and the object was declared internal).

Basically, I wanted the derived classes to use these events as their own. The only way to do this was to expose the backing variable of the events as protected, so the derived classes could raise the events. Looking at the code, this seemed pretty stupid since I was basically defining the delegate twice; once as a protected field, and the other as the public event. I thought,

Wouldn't I be better off making a class called Event that has an out parameter of an Action in the constructor? The action that is returned is equivalent to Raise that many have made as an extension method for delegates, where it checks to see if the delegate is null, and then invokes the delegate. The only public methods on Event would be Add and Remove for appending delegates and removing them from the underlying delegate (+=, -=). Classes could have these events as properties, such as,

public Event SomethingHappened { get; private set; }

so that only that class can re-assign the event. Or a public readonly field would be just as effective. The out parameter that is returned from the constructor is stored by the class and called when the class wants to raise the event. I know it is a hokey workaround, but it'll get the job done, and allows the events to not only be passed as arguments, but allow derived classes to call the Raise method if the base class defines it as protected.

TLDR:

What is the advantage of using the event keyword other than for modifying how the delegate can be accessed?

Michael Yanni
  • 1,476
  • 4
  • 17
  • 29
  • 6
    `"other than for modifying how the delegate can be accessed"` That's not enough for you? That's reason enough to use it for me. Also, it helps to convey the semantic intent of the member much more effectively. – Servy Aug 22 '13 at 16:23
  • Your question is an answer to many other questions. +1 (not that I didn't know this, just saying) – Federico Berasategui Aug 22 '13 at 16:26
  • @HighCore I basically just rant/summarized what I've been reading recently. :-P I always thought that keyword did more than it really does. But, I'm trying to figure out if it has any other 'real' advantages that I couldn't create myself in custom classes using the already existent modifiers. – Michael Yanni Aug 22 '13 at 16:29
  • 5
    See http://csharpindepth.com/Articles/Chapter2/Events.aspx It's a bit like comparing properties with fields. – Jon Skeet Aug 22 '13 at 16:30
  • 1
    See this http://stackoverflow.com/a/10484998/430661 as well. – Alex Paven Aug 22 '13 at 16:30
  • @MichaelYanni The point of using the keyword is merely that in langauge that didn't have it (i.e. Java) you did the same thing through classes similar (ish) to what you're describing. It was such a common thing to do that they baked it into the language. It's not that you can't mimic the behavior yourself, it's that it just saves you some typing. Just like auto-properties save you from typing getters and setters. – Servy Aug 22 '13 at 16:31
  • 5
    This question appears to be off-topic because it is not a question. It is a rant. – John Kraft Aug 22 '13 at 16:33
  • @AlexPaven Wow! Does using event on a delegate actually modify how the add/remove operators on a delegate work? See... that's the kind of stuff I was looking for. – Michael Yanni Aug 22 '13 at 16:33
  • @JohnKraft It is a question. Did you not see the big bold question in large text at the end? It's a perfectly legitimate question to ask, and it is asked in a constructive (i.e. non-ranty) manor. – Servy Aug 22 '13 at 16:34
  • 1
    @JohnKraft The point of the 'rant' part is to show that I have enough knowledge about delegates, and I'm looking for what advantages does the event keyword itself give over simply using delegates without the keyword. – Michael Yanni Aug 22 '13 at 16:37
  • 3
    It is not a modifier, it is an *accessor* that limits the number of things external code can do with a delegate. Just add and remove event handlers, nothing else. Just like a property is an accessor for a field. It stops badly behaving code from whacking the event subscriptions made by other code. – Hans Passant Aug 22 '13 at 16:40
  • Try looking at this. http://stackoverflow.com/questions/563549/difference-between-events-and-delegates-and-its-respective-applications – TomF Aug 22 '13 at 16:50

3 Answers3

26

What is the advantage of using the event keyword other than for modifying how the delegate can be accessed?

That is the primary advantage of using the event keyword. You use an event over just a raw delegate to prevent the delegate from being invoked or cleared from outside the scope of the class it is defined in, because in the case of events it is the responsibility of that class to invoke the event. External entities shouldn't be invoking it directly (they can and should be invoking the event indirectly), nor should they "care" about whether there are any other event handlers or be involved in touching them (by, for example, assigning an entirely new delegate to the field).

The specific case of wanting to allow sub-classes to fire the event is most commonly solved by having the class that defines the event creating a protected method that does nothing but fire the event. Such methods will, by convention, have the same name as the event but with "On" prefixing it.

Yes, you could create your own type that logically represents an event, is a wrapper for a delegate, and limits the functions that can be performed on that event to those that "should" be able to perform them (possibly using slightly different rules than the C# event keyword uses. This is something that is frequently used in other languages that don't have an event keyword (or possibly even delegates). The C# designers simply realized that this was a very common pattern, and felt that it was worth the energy to add the keyword to the language to help minimize the boilerplate code required to create a logical "event".

Another benefit of using the event keyword, as opposed to just having some type of delegate as a property, is that you make your intentions much clearer. If I see just a delegate property the implication is generally that it represents one method. Yes, all delegates in C# are multicast delegates, so that's not true, but it's unusual for people to leverage that functionality outside of events. People think that an Action represents one action, not a list of actions. Events also have special treatment with respect to the C# documentation. They are all listed separately, they have different icons in visual studio, etc. This all helps make the intentions and semantics of the member much clearer to someone using the class at a glance.

Finally, the event keyword ensures that there is synchronization between multiple threads, which isn't performed by the Delegate class. If multiple threads go to add handlers to an event at the same time, the event keyword ensures both are added. If you just publicly expose a delegate it's possible for one to overwrite the other due to a race condition and have one handler end up dropped on the floor. If you roll your own Event class you could provide this functionality, but it is both more boilerplate code and something that's pretty darn easy to mess up (either resulting in leaving race conditions in, or excessive synchronization resulting in lost performance).

Servy
  • 202,030
  • 26
  • 332
  • 449
  • 1
    Thanks for the info about synchronization. I'd still be happier if the events could be fired by the derived classes, though. I find it breaks OOP because of that restriction. Example: Base class Animal has an event called WakeUp. It is raised when the Animal wakes up. Derived class Monkey would wake up under different conditions than derived class Dog, so they should be able to fire this event based on their logic, and not that of Animal. It is ridiculous that every event for Animal would need to make a method so that Monkey and Dog can raise those events. – Michael Yanni Aug 23 '13 at 14:16
  • 1
    @MichaelYanni The current implementation gives you the power to allow sub-classes to fire the event through the definition of a protected method. It is giving *you* the control. If it instead always allowed what you describe then what happens when someone wants to prevent subclasses from firing an event? They're be no real way to do it. – Servy Aug 23 '13 at 14:19
  • They added 'add' and 'remove' for event. Add a third called 'raise'. Then, add the ability for the event accessors to have access modifiers. Their default is obviously public for 'add' and 'remove'. Default for 'raise' could be private. You could change it, though, which adds a small amount of customization to events. Also, the default operation for 'raise' is Invoke if that wasn't obvious. – Michael Yanni Aug 23 '13 at 14:29
  • @MichaelYanni That would be more work than what it has currently. It takes all of 5 seconds to make a new method with an accessibility of anything other than private. No more than it would take with your syntax, and in the vast majority of cases private is the desired option, so it's not like most people are spending that time. There are *much* more valuable features I'd rather the C# team spent their time on. – Servy Aug 23 '13 at 14:31
  • 1
    Agreed. I just don't like repeating myself in code. I call methods like that 'filler methods'. Basically, a waste of code space. – Michael Yanni Aug 23 '13 at 17:42
  • 1
    @MichaelYanni But it's not a waste; it's explicitly indicating that you're exposing some content at a higher level than it normally would be. *That's worth calling out, making explicit, and drawing attention to*. It is not wasting time for the person writing it, nor for someone reading through the code. – Servy Aug 23 '13 at 17:50
  • You seem to be missing the point. If I could explicitly tell it to do that with a single keyword, that would be preferred. The fact that it is a method could allow for other things beyond simply raising the event to take place. New programmers like to shove code into places where it shouldn't go. The worst part about it is that you have to make a method for each event. If I could make a generic way for derived classes to raise the event, that'd be great. A super simplistic way would be an enum where each value represents an event, and they send one to a method that raises the correct event. – Michael Yanni Aug 23 '13 at 18:07
3

Basically, I wanted the derived classes to use these events as their own. The only way to do this was to expose the backing variable of the events as protected, so the derived classes could raise the events.

The usual way to handle this is not to expose the field, but to expose a method to raise the event.

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
  var handler = PropertyChanged;
  if (handler != null)
    handler(this, e);
}

This not only allows derived classes to raise the event, but also allows derived classes to do something before any subscribed handlers actually get called.

To answer your actual question, though:

What is the advantage of using the event keyword other than for modifying how the delegate can be accessed?

One advantage not yet mentioned (I think):

public event PropertyChangedEventHandler PropertyChanged;

can be changed to

public event PropertyChangedEventHandler PropertyChanged
{
  add { /* custom code here */ }
  remove { /* custom code here */ }
}

without requiring a recompile of all users of your library. You might want something like this if you later find a reason to not simply store the handlers in a private field. This is the same advantage of an auto-implemented property over a field.

Servy
  • 202,030
  • 26
  • 332
  • 449
0

I think you can easly compare 'event' keyword to accessors but you have some other benefits coming from 'event' keyword:

  • easier to read, everyone knows what 'event' means
  • less work, you don't need to create special functions to subscribe and unsubscribe from delegate variable
  • adding to or removing from delegate that has 'event' keyword has 'lock' (synchronization) added.
  • IDE / frameworks can interpret 'events' and help you
Morfidon
  • 1,469
  • 1
  • 18
  • 34