4.4 Events
Event handling is essentially a process in which one object can
notify other objects that an event has occurred. This process is
largely encapsulated by multicast delegates, which have this ability
built in.
4.4.1 Defining a Delegate for an Event
The .NET Framework provides
many event-handling delegates, but you can
write your own. For example:
delegate void MoveEventHandler(object source, MoveEventArgs e);
By convention, the delegate's first parameter
denotes the source of the event, and the delegate's
second parameter derives from System.EventArgs and
contains data about the event.
4.4.2 Storing Data for an Event with EventArgs
The EventArgs class may be
derived
from to include information relevant to a particular event:
public class MoveEventArgs : EventArgs {
public int newPosition;
public bool cancel;
public MoveEventArgs(int newPosition) {
this.newPosition = newPosition;
}
}
4.4.3 Declaring and Firing an Event
A class or struct can declare
an
event by applying the event modifier to a delegate field. In this
example, the slider class has a Position property
that fires a Move event whenever its
Position changes:
class Slider {
int position;
public event MoveEventHandler Move;
public int Position {
get { return position; }
set {
if (position != value) { // if position changed
if (Move != null) { // if invocation list not empty
MoveEventArgs args = new MoveEventArgs(value);
Move(this, args); // fire event
if (args.cancel)
return;
}
position = value;
}
}
}
}
The event keyword promotes encapsulation by
ensuring that only the += and
-= operations can be performed on the delegate.
Other classes may act on the event, but only the
Slider can invoke the delegate (fire the event) or
clear the delegate's invocation list.
4.4.4 Acting on an Event with an Event Handler
We are able to act on an event by adding an event handler to it. An event
handler is a delegate that wraps the method we want invoked when the
event is fired.
In this example, we want our Form to act on
changes made to a Slider's
Position. This is done by creating a
MoveEventHandler delegate that wraps our
event-handling method (the slider_Move method).
This delegate is added to the Move
event's existing list of
MoveEventHandlers (which is initially empty).
Changing the position on the slider fires the Move
event, which invokes our slider_Move method:
using System;
class Form {
static void Main( ) {
Slider slider = new Slider( );
// register with the Move event
slider.Move += new MoveEventHandler(slider_Move);
slider.Position = 20;
slider.Position = 60;
}
static void slider_Move(object source, MoveEventArgs e) {
if(e.newPosition < 50)
Console.WriteLine("OK");
else {
e.cancel = true;
Console.WriteLine("Can't go that high!");
}
}
}
Typically, the Slider class is extended so that it
fires the Move event whenever its
Position is changed by a mouse movement, keypress,
etc.
4.4.5 Event Accessors
attributes? unsafe? access-modifier?
[
[[sealed | abstract]? override] |
new? [virtual | static]?
]?
event delegate type event-property accessor-name
{
attributes? add statement-block
attributes? remove statement-block
}
 |
Abstract accessors don't specify
an implementation, so they replace an add/remove block with a
semicolon.
|
|
Similar to the way properties provide controlled
access to fields, event accessors provide controlled access to an
event. Consider the following field declaration:
public event MoveEventHandler Move;
Except for the underscore prefix added to the field (to avoid a name
collision), this is semantically identical to:
private MoveEventHandler _Move;
public event MoveEventHandler Move {
add {
_Move += value;
}
remove {
_Move -= value;
}
}
The ability to specify a custom implementation of add and remove
handlers for an event allows a class to proxy an event generated by
another class, thus acting as a relay for an event rather than the
generator of that event. Another advantage of this technique is to
eliminate the need to store a delegate as a field, which can be
costly in terms of storage space. For instance, a class with 100
event fields would store 100 delegate fields, even though maybe only
4 of those events are actually assigned. Instead, you can store these
delegates in a dictionary, and add and remove the delegates from that
dictionary (assuming the dictionary holding 4 elements uses less
storage space than 100 delegate references).
 |
The add and remove parts of an
event are compiled to add_XXX and
remove_XXX methods.
|
|
|