Thursday, December 17, 2009

Events


Events

To recap: in object-oriented languages, objects expose encapsulated functions called methods. Methods are encapsulated functions which run when they are invoked.
Sometimes, however, we think of the process of method invocation more grandly. In such a case, the method invocation is termed an 'event', and the running of the method is the 'handling' of the event. An archetypal example of an event is a user's selection of a button on a graphical user interface; this action may trigger a number of methods to 'handle' it.
What distinguishes events from other method invocations is not, however, that they must be generated externally. Any internal change in the state of a program can be used as an event. Rather, what distinguishes events is that they are backed by a particular 'subscription-notification' model. An arbitrary class must be able to 'subscribe to' (or declare its interest in) a particular event, and then receive a 'notification' (ie. have one of its methods run) whenever the event occurs.
Delegates - in particular multicast delegates - are essential in realizing this subscription-notification model. The following example describes how Class 2 subscribes to an event issued by Class 1.
1. Class 1 is an issuer of E-events. It maintains a public multicast delegate D.
2. Class 2 wants to respond to E-events with its event-handling method M. It therefore adds onto D a reference to M.
3. When Class 1 wants to issue an E-event, it calls D. This invokes all of the methods which have subscribed to the event, including M.
The 'event' keyword is used to declare a particular multicast delegate (in fact, it is usual in the literature to just identify the event with this delegate). The code below shows a class EventIssuer, which maintains an event field myEvent. We could instead have declared the event to be a property instead of a field  . To raise the myEvent event, the method onMyEvent is called (note that we are checking in this method to see if myEvent is null - trying to trigger a null event gives a run-time error).
1.
public class EventIssuer
2.
{
3.
    public delegate void EventDelegate(object from, EventArgs args);
4.
    public event EventDelegate myEvent;
5.
6.
    protected virtual void onMyEvent(EventArgs args)
7.
    {
8.
        if (myEvent!=null)
9.
            myEvent(this, args);
10.
    }
11.

A class which wanted to handle the events issued by an EventIssuer ei with its method handleEvents would then subscribe to these events with the code:
ei.myEvent += new EventIssuer.EventDelegate(handleEvents);

Good Practice For Events

The code above demonstrates some points about event-handling which are not enforced by the language architecture, but are used throughout the .Net framework as good practice.
1. When you want to raise an event in code, you don't tend to trigger the class's event object directly. Rather, you call a 'protected, virtual' method to trigger it (cf. the onMyEvent method above).
2. By convention, when events are raised they pass two objects to their subscribers. The first is a reference to the class raising the event; the second is an instance of the System.EventArgs class which contains any arbitrary data about the event.
3. If an event is not interested in passing data to subscribers, then its defining delegate will still reference an EventArgs object (but a null value will be passed by the event). If an event should pass data to its subscribers, however, then it is standard to use a specific class which derives from the EventArgs class to hold this data.
4. When you write a class which inherits from an event-raising base class, you can 'intercept' an event by overriding the method used to raise it. The following code illustrates such an intercept - classes which subscribe to the event will never receive notifications about it.
1.
    protected override void onMyEvent(EventArgs args)
2.
    {
3.
         Console.WriteLine("hello");
4.
    }
5.

If you want subscribers to continue to receive notifications despite such an 'intercepting' method, however, then you can call the base class method as in the following:
1.
    protected override void onMyEvent(EventArgs args)
2.
    {
3.
         Console.WriteLine("hello");
4.
         base.onMyEvent(args);
5.
    }
6.




No comments:

Post a Comment