[ Pobierz całość w formacie PDF ]
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;
194 Copyright Microsoft Corporation 1999-2000. All Rights Reserved.
Chapter 10 Classes
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
Copyright Microsoft Corporation 1999-2000. All Rights Reserved. 195
C# LANGUAGE REFERENCE
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.
Implementation note
In the .NET runtime, when a class declares an event member X of a delegate type T, it is an error for the same class to also
declare a method with one of the following signatures:
void add_X(T handler);
void remove_X(T handler);
The .NET runtime reserves these signatures for compatibility with programming languages that do not provide operators
or other language constructs for attaching and removing event handlers. Note that this restriction does not imply that a C#
program can use method syntax to attach or remove event handlers. It merely means that events and methods that follow
this pattern are mutually exclusive within the same class.
When a class declares an event member, the C# compiler automatically generates the add_X and remove_X methods
mentioned above. For example, the declaration
class Button
{
public event EventHandler Click;
}
can be thought of as
class Button
{
private EventHandler Click;
public void add_Click(EventHandler handler) {
Click += handler;
}
public void remove_Click(EventHandler handler) {
Click -= handler;
}
}
The compiler furthermore generates an event member that references the add_X and remove_X methods. From the point
of view of a C# program, these mechanics are purely implementation details, and they have no observable effects other
than the add_X and remove_X signatures being reserved.
10.8 Indexers
Indexers permit instances of a class to be indexed in the same way as arrays. Indexers are declared using
indexer-declarations:
indexer-declaration:
attributesopt indexer-modifiersopt indexer-declarator { accessor-declarations }
indexer-modifiers:
indexer-modifier
indexer-modifiers indexer-modifier
indexer-modifier:
new
public
protected
internal
private
indexer-declarator:
type this [ formal-index-parameter-list ]
type interface-type . this [ formal-index-parameter-list ]
196 Copyright Microsoft Corporation 1999-2000. All Rights Reserved.
Chapter 10 Classes
formal-index-parameter-list:
formal-index-parameter
formal-index-parameter-list , formal-index-parameter
formal-index-parameter:
attributesopt type identifier
An indexer-declaration may include set of attributes (§17), a new modifier (§10.2.2), and a valid combination
of the four access modifiers (§10.2.3).
The type of an indexer declaration specifies the element type of the indexer introduced by the declaration.
Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this.
For an explicit interface member implementation, the type is followed by an interface-type, a . , and the
keyword this. Unlike other members, indexers do not have user-defined names.
The formal-index-parameter-list specifies the parameters of the indexer. The formal parameter list of an indexer
corresponds to that of a method (§10.5.1), except that at least one parameter must be specified, and that the ref
and out parameter modifiers are not permitted.
The type of an indexer and each of the types referenced in the formal-index-parameter-list must be at least as
accessible as the indexer itself (§3.3.4).
The accessor-declarations, which must be enclosed in { and } tokens, declare the accessors of the indexer.
The accessors specify the executable statements associated with reading and writing indexer elements.
Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer
element is not classified as a variable. Thus, it is not possible to pass an indexer element as a ref or out
parameter.
[ Pobierz całość w formacie PDF ]