I understand the purpose of events, especially within the context of creating user interfaces. I think this is the prototype for creating an event:
public void EventName(object sender, EventArgs e);
What do event handlers do, why are they needed, and how do I to create one?
I agree with KE50 except that I view the 'event' keyword as an alias for 'ActionCollection' since the event holds a collection of actions to be performed (ie. the delegate).
That is actually the declaration for an event handler - a method that will get called when an event is fired. To create an event, you'd write something like this:
And then you can subscribe to the event like this:
With OnMyEvent() defined like this:
Whenever
Foo
fires offMyEvent
, then yourOnMyEvent
handler will be called.You don't always have to use an instance of
EventArgs
as the second parameter. If you want to include additional information, you can use a class derived fromEventArgs
(EventArgs
is the base by convention). For example, if you look at some of the events defined onControl
in WinForms, orFrameworkElement
in WPF, you can see examples of events that pass additional information to the event handlers.To understand event handlers, you need to understand delegates. In C#, you can think of a delegate as a pointer (or a reference) to a method. This is useful because the pointer can be passed around as a value.
The central concept of a delegate is its signature, or shape. That is (1) the return type and (2) the input arguments. For example, if we create a delegate
void MyDelegate(object sender, EventArgs e)
, it can only point to methods which returnvoid
, and take anobject
andEventArgs
. Kind of like a square hole and a square peg. So we say these methods have the same signature, or shape, as the delegate.So knowing how to create a reference to a method, let's think about the purpose of events: we want to cause some code to be executed when something happens elsewhere in the system - or "handle the event". To do this, we create specific methods for the code we want to be executed. The glue between the event and the methods to be executed are the delegates. The event must internally store a "list" of pointers to the methods to call when the event is raised.* Of course, to be able to call a method, we need to know what arguments to pass to it! We use the delegate as the "contract" between the event and all the specific methods that will be called.
So the default
EventHandler
(and many like it) represents a specific shape of method (again, void/object-EventArgs). When you declare an event, you are saying which shape of method (EventHandler) that event will invoke, by specifying a delegate:(*This is the key to events in .NET and peels away the "magic" - an event is really, under the covers, just a list of methods of the same "shape". The list is stored where the event lives. When the event is "raised", it's really just "go through this list of methods and call each one, using these values as the parameters". Assigning an event handler is just a prettier, easier way of adding your method to this list of methods to be called).
Just to add to the existing great answers here - building on the code in the accepted one, which uses a
delegate void MyEventHandler(string foo)
...Because the compiler knows the delegate type of the SomethingHappened event, this:
Is totally equivalent to:
And handlers can also be unregistered with
-=
like this:For completeness' sake, raising the event can be done like this, only in the class that owns the event:
The thread-local copy of the handler is needed to make sure the invocation is thread-safe - otherwise a thread could go and unregister the last handler for the event immediately after we checked if it was
null
, and we would have a "fun"NullReferenceException
there.C# 6 introduced a nice short hand for this pattern. It uses the null propagation operator.
publisher: where the events happen. Publisher should specify which delegate the class is using and generate necessary arguments, pass those arguments and itself to the delegate.
subscriber: where the response happen. Subscriber should specify methods to respond to events. These methods should take the same type of arguments as the delegate. Subscriber then add this method to publisher's delegate.
Therefore, when the event happen in publisher, delegate will receive some event arguments (data, etc), but publisher has no idea what will happen with all these data. Subscribers can create methods in their own class to respond to events in publisher's class, so that subscribers can respond to publisher's events.