NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

10.7 Events

Events permit a class to declare notifications for which clients can attach executable code in the form of event handlers. Events are declared using event-declarations:

event-declaration:
event-field-declaration
event-property-declaration
event-field-declaration:
attributesopt event-modifiersopt event type variable-declarators ;
event-property-declaration:
attributesopt event-modifiersopt event type member-name { accessor-declarations }
event-modifiers:
event-modifier
event-modifiers event-modifier
event-modifier:
new
public
protected
internal
private
static

An event declaration is either an event-field-declaration or an event-property-declaration. In both cases, the declaration may include set of attributes (§17), a new modifier (§10.2.2), a valid combination of the four access modifiers (§10.2.3), and a static modifier (§10.2.5).

The type of an event declaration must be a delegate-type (§15), and that delegate-type must be at least as accessible as the event itself (§3.3.4).

An event field declaration corresponds to a field-declaration (§10.4) that declares one or more fields of a delegate type. The readonly modifier is not permitted in an event field declaration.

An event property declaration corresponds to a property-declaration (§10.6) that declares a property of a delegate type. The member-name and accessor-declarations are equivalent to those of a property declaration, except that an event property declaration must include both a get accessor and a set accessor, and that the accessors are not permitted to include virtual, override, or abstract modifiers.

Within the program text of the class or struct that contains an event member declaration, the event member corresponds exactly to a private field or property of a delegate type, and the member can thus be used in any context that permits a field or property.

Outside the program text of the class or struct that contains an event member declaration, the event member can only be used as the left hand operand of the += and -= operators (§7.13.3). These operators are used to attach or remove event handlers to or from an event member, and the access modifiers of the event member control the contexts in which the operations are permitted.

Since += and -= are the only operations that are permitted on an event member outside the type that declares the event member, external code can append and remove handlers for an event, but cannot in any other way obtain or modify the value of the underlying event field or event property.

In the example

public delegate void EventHandler(object sender, Event e);
public class Button: Control
{
   public event EventHandler Click;
   protected void OnClick(Event e) {
      if (Click != null) Click(this, e);
   }
   public void Reset() {
      Click = null;
   }
}

there are no restrictions on usage of the Click event field within the Button class. As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. The OnClick method in the Button class "raises" the Click event. The notion of raising an event is precisely equivalent to invoking the delegate represented by the event member—thus, there are no special language constructs for raising events. Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Outside the declaration of the Button class, the Click member can only be used on the left hand side of the += and -= operators, as in

b.Click += new EventHandler(...);

which appends a delegate to the invocation list of the Click event, and

b.Click -= new EventHandler(...);

which removes a delegate from the invocation list of the Click event.

In an operation of the form x += y or x -= y, when x is an event member and the reference takes place outside the type that contains the declaration of x, the result of the operation is void (as opposed to the value of x after the assignment). This rule prohibits external code from indirectly examining the underlying delegate of an event member.

The following example shows how event handlers are attached to instances of the Button class above:

public class LoginDialog: Form
{
   Button OkButton;
   Button CancelButton;
   public LoginDialog() {
      OkButton = new Button(...);
      OkButton.Click += new EventHandler(OkButtonClick);
      CancelButton = new Button(...);
      CancelButton.Click += new EventHandler(CancelButtonClick);
   }
   void OkButtonClick(object sender, Event e) {
      // Handle OkButton.Click event
   }
   void CancelButtonClick(object sender, Event e) {
      // Handle CancelButton.Click event
   }
}

Here, the LoginDialog constructor creates two Button instances and attaches event handlers to the Click events.

Event members are typically fields, as in the Button example above. In cases where the storage cost of one field per event is not acceptable, a class can declare event properties instead of event fields and use a private mechanism for storing the underlying delegates. (In scenarios where most events are unhandled, using a field per event may not be acceptable. The ability to use a properties rather than fields allows for space vs. speed tradeoffs to be made by the developer.)

In the example

class Control: Component
{
   // Unique keys for events
   static readonly object mouseDownEventKey = new object();
   static readonly object mouseUpEventKey = new object();
   // Return event handler associated with key
   protected Delegate GetEventHandler(object key) {...}
   // Set event handler associated with key
   protected void SetEventHandler(object key, Delegate handler) {...}
   // MouseDown event property
   public event MouseEventHandler MouseDown {
      get {
         return (MouseEventHandler)GetEventHandler(mouseDownEventKey);
      }
      set {
         SetEventHandler(mouseDownEventKey, value);
      }
   }
   // MouseUp event property
   public event MouseEventHandler MouseUp {
      get {
         return (MouseEventHandler)GetEventHandler(mouseUpEventKey);
      }
      set {
         SetEventHandler(mouseUpEventKey, value);
      }
   }
}

the Control class implements an internal storage mechanism for events. The SetEventHandler method associates a delegate value with a key, and the GetEventHandler method returns the delegate currently associated with a key. Presumably the underlying storage mechanism is designed such that there is no cost for associating a null delegate value with a key, and thus unhandled events consume no storage.