• No results found

Events

In document C++ CLI Standard pdf (Page 101-105)

18. Classes and members

18.5 Events

An event is a member that enables an Object or class to provide notifications. Clients can add a delegate to an event, so that the Object will invoke that delegate. Events are declared using event-definitions:

event-definition:

attributesopt event-modifiers event-type identifier

5

function-modifiersopt override-specifieropt { accessor-specification }

attributesopt event-modifiers event-type identifier

function-modifiersopt override-specifieropt ;

event-modifiers: event-modifier 10 event-modifiers event-modifier event-modifier: event static virtual 15

An event-definition can include a set of attributes (§28), property-modifiers (§18.4.1, §18.4.3), function- modifiers (§18.2.3, §18.4.3), and an override-specifier (§18.3.1). It must include the event-modifier event.

The event-type of an event definition shall be a delegate type, and that type shall be at least as accessible as the event itself. identifier designates the name of the event.

The production event-type has not yet been defined. The syntactic category of this element needs to be 20

reviewed.[[#140]]

The accessor-specification declares the accessor functions (§18.5.2) of the event. The accessor functions specify the executable statements associated with adding handlers to, and removing handlers from, the event, as well as raising that event.

An event-definition ending with a semicolon (as opposed to a brace-delimited accessor-specification) 25

defines a trivial event (§18.5.4). The three accessor functions for a trivial event are supplied automatically by the compiler along with a private backing store. An event-definition ending with a brace-delimited accessor-specification defines a non-trivial event.

[Example: The following example shows how event handlers are attached to instances of the Button class: public delegate void EventHandler(Object^ sender,

30

EventArgs^ e);

public ref struct Button : Control { event EventHandler^ Click;

};

public ref class LoginDialog : Form

35 { Button^ OkButton; Button^ CancelButton; public: LoginDialog() { 40

OkButton = gcnew Button(…);

OkButton->Click += gcnew EventHandler(&OkButtonClick);

CancelButton = gcnew Button(…);

CancelButton->Click += gcnew EventHandler(&CancelButtonClick);

}

45

void OkButtonClick(Object^ sender, EventArgs^ e) {

// Handle OkButton->Click event

}

void CancelButtonClick(Object^ sender, EventArgs^ e) {

// Handle CancelButton->Click event

50

} };

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

events. end example]

The address of an event accessor function can be taken and bound to a suitably typed pointer-to-member function (subject to the usual C++ rules, such as that the calling code must have access to the function’s name). However, it is not possible to bind a pointer-to-member Object to an event. [Note: An event is a 5

group of one or more accessor functions, not an Object. end note]

18.5.1 Static and instance events

When an event declaration includes a static modifier, the event is said to be a static event. When no static modifier is present, the event is said to be an instance event.

18.5.2 Accessor functions 10

The accessor-specification for an event specifies the executable statements associated with adding handlers to, and removing handlers from, the event, as well as raising that event.

The accessor-specification for an event shall contain no more than three function-definitions:

It is a bit strange to define grammar productions for these functions. We probably should either make these terms (and change the style accordingly) or just call them the add function, remove function, and raise 15

function.[[#141]]

• one for a function called add, herein called the add-accessor-function,

• one for a function called raise, herein called the raise-accessor-function, and

• one for a function called remove, herein called the remove-accessor-function.

A non-trivial event shall contain both an add-accessor-function and a remove-accessor-function. If that 20

event has no raise-accessor-function, one is not supplied automatically by the compiler.

A program is ill-formed if it contains an event having only one of add-accessor-function and remove- accessor-function.

add-accessor-function and remove-accessor-function shall each take one parameter, of type event-type, and their return type shall be void.

25

The parameter list of raise-accessor-function shall correspond exactly to the parameter list of event-type, and its return type shall be the return type of event-type.

[Note: Trivial envents are generally better to use because use of the non-trivial form requires consideration of thread safety. end note]

When an event is invoked, the raise function is called. 30

[Example: … end example] [[Ed]]

18.5.3 Virtual, sealed, abstract, and override accessor functions

A virtual event declaration specifies that the accessor functions of that event are virtual. The virtual

modifier applies to all accessor functions of an event.

An abstract event declaration specifies that the accessor functions of the event are virtual, but does not

35

provide an actual implementation of the accessor functions. Instead, non-abstract derived classes are required to provide their own implementation for the accessor functions by overriding the event.

An event declaration that includes both the abstract and override modifiers specifies that the event is

abstract and overrides a base event. The accessor functions of such an event are also abstract.

[Note: Having an abstract event makes the enclosing class abstract. end note] The accessor functions of an 40

Classes and members

new event. Instead, it simply specializes the implementations of the accessor functions of an existing virtual event.

An overriding event declaration can include the sealed modifier. Use of this modifier prevents a derived class from further overriding the event. The accessor functions of a sealed event are also sealed.

An event with the new modifier introduces a new event that does not override an event from a base class. 5

Make sure the complete specification is provided in the clause for the new modifier.[[#142]] Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessor functions behave exactly like virtual, sealed, override and abstract functions.

When a trivial event overrides an event, the trivial event’s raise is implicitly declared and defined.

18.5.4 Trivial events 10

A trivial event is defined by an event-definition ending with a semicolon (as opposed to a brace-delimited accessor-specification). [Example: ref struct S { event SomeDelegateType^ E; }; 15 end example]

Within the class that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event must be trivial. Such an event can be used in any context that permits a field. The field contains a delegate, which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains nullptr.

20

[Example: In the example

public delegate void EventHandler(Object^ sender,

EventArgs^ e);

public ref class Button : Control { public:

25

event EventHandler^ Click; void Reset() { Click = nullptr; } protected: 30 void OnClick(EventArgs^ e) {

Click(this, e); // raise tests for nullptr }

};

Click is used as a field within the Button class. As the example demonstrates, the field can be examined, 35

modified. The OnClick function in the Button class “raises” the Click event.

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 += gcnew EventHandler(…);

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

40

b->Click –= gcnew EventHandler(…);

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

When compiling a trivial event, the compiler automatically creates storage to hold the delegate, and creates accessor functions for the event that add event handlers to, and remove them from, the delegate field. The compiler also automatically generates a raise accessor function. The access-specifier for the generated add

45

and remove accessor functions is the same as that for the whole event. The access-specifier for the generated raise accessor function is protected. In order to be thread-safe, the addition and removal operations shall

be done while holding the lock on the containing Object for an instance event, or the type Object for a static event. Such a lock is specified using the attribute

MethodImpl(MethodImplOptions::Synchronized). The compiler-generated raise accessor function shall not have this attribute.

[Note: Thus, an instance event declaration of the form:

delegate int D(int); ref class X {

5

public:

event D^ Ev; };

could be compiled to something equivalent to:

ref class X {

10

D^ __Ev; // field to hold the delegate

public:

event D^ Ev {

[MethodImpl(MethodImplOptions::Synchronized)]

void add(D^ value) {

15

__Ev += value;

}

[MethodImpl(MethodImplOptions::Synchronized)]

void remove(D^ value) {

__Ev -= value;

20

} protected:

int raise(int arg) { return __Ev(arg); } }

};

25

Within the class X, references to Ev are compiled to reference the hidden field __Ev instead. (The name

“__Ev” is arbitrary; the hidden field could have any name or no name at all.)

Similarly, a static event declaration of the form:

delegate int D(int); ref class X {

30

public:

static event D^ Ev; };

could be compiled to something equivalent to:

ref class X {

35

static D^ __Ev; // field to hold the delegate

public:

static event D^ Ev {

[MethodImpl(MethodImplOptions::Synchronized)]

void add(D^ value) {

40

__Ev += value;

}

[MethodImpl(MethodImplOptions::Synchronized)]

void remove(D^ value) {

__Ev -= value;

45

}

protected:

int raise(int arg) { return __Ev(arg); } }

};

50

Classes and members

18.5.5 Event invocation

Events having a programmer-supplied or compiler-generated raise accessor function can be invoked using function call syntax. Specifically, an event E can be invoked using E(delegate-argument-list), which results

in the raise accessor function’s being called with delegate-argument-list as its argument list.

Events without a raise accessor function cannot be invoked using function call syntax. Instead, the delegate’s 5

Invoke function must be called directly.

In document C++ CLI Standard pdf (Page 101-105)