Export (0) Print
Expand All

10 Events

Visual Studio .NET 2003

The common language runtime supports the publish-subscribe events model. An event source can notify one or more subscribed recipients of an event that has occurred.

The event mechanism is built upon common language runtime multicast delegates and describes events via metadata. The event metadata describes:

  • The event type.
  • Events raised by classes.
  • Methods of a class that add or remove an event handler.

Common language runtime events are based on delegates.

This example describes common language runtime events.

Example

// __events.cpp
// compile with: /clr /LD
#using <mscorlib.dll>
using namespace System;

__delegate void ClickEventHandler(int, double);
__delegate void DblClickEventHandler(String*);

__gc class EventSource {
public:
   __event ClickEventHandler* OnClick;  // declare the event OnClick
   __event DblClickEventHandler* OnDblClick;  // declare OnDblClick

   void FireEvents() {
      OnClick(7, 3.14159);
      OnDblClick("Hello");
   }
};

The compiler will generate code that is essentially equivalent to:

#using <mscorlib.dll>
using namespace System;

__delegate void ClickEventHandler(int, double);
__delegate void DblClickEventHandler(String*);

__gc class EventSource {
private:
   ClickEventHandler* OnClick;
   DblClickEventHandler* OnDblClick;

public:
   // subscribe to OnClick
   __event void add_OnClick(ClickEventHandler* eh) {
      OnClick = static_cast<ClickEventHandler *> (Delegate::Combine(eh, OnClick));
   }

   // unsubscribe to OnClick
   __event void remove_OnClick(ClickEventHandler* eh) {
      OnClick = static_cast<ClickEventHandler *> (Delegate::Remove(eh, OnClick));
   }

   // subscribe
   __event void add_OnDblClick(DblClickEventHandler* eh) {
      OnDblClick = static_cast<DblClickEventHandler *> (Delegate::Combine(OnDblClick, eh));
   }

   // unsubscribe
   __event void remove_OnDblClick(DblClickEventHandler* eh) {
      OnDblClick = static_cast<DblClickEventHandler *> (Delegate::Remove(OnDblClick, eh));
   }

protected:  // prevent external invocations of raise
   // generate notification
   void raise_OnClick(int x, double y) {
      if (OnClick)
         OnClick->Invoke(x, y);
   }

   void raise_OnDblClick(String* s) {
      if (OnDblClick)
         OnDblClick->Invoke(s);
   }

   void FireEvents() {
      raise_OnClick(7, 3.14159);
      raise_OnDblClick("Hello");
   }

   EventSource() {
      OnClick = 0;
      OnDblClick = 0;
   }
};

Characteristics

  • The accessibility of a generated raise method is never public.
  • The raise method generated for an event is protected by default. It is private if the event is declared to be private.
  • The raise method generated for an event is virtual only if the event is declared to be virtual.
  • If an event is declared in a __gc interface (Section 6), associated add and remove methods only are generated.

Example

// __events2.cpp
// compile with: /clr /LD
#using <mscorlib.dll>
__delegate int Del(int, float);
__gc __interface I {
public:
   __event Del *E;
};

is converted to

__gc __interface I {
__event void add_E(Del*);
__event void remove_E(Del*);
// No raise method is generated
};

The user can override any of the default accessibilities of add, remove, and raise. This is achieved by omitting the declaration of the event, and explicitly declaring add, remove, and raise.

You can add handlers for an event with the += operator and you can remove handlers for an event with the -= operator. These operators are an alternative to using __hook and __unhook.

In the example below, these three methods are declared.

Example

// __events3.cpp
// compile with: /clr
#using <mscorlib.dll>

public __delegate void f(int);

public __gc struct E {
   f* _E;
public:
   void handler(int i) { System::Console::WriteLine(i); }
   E() { _E = 0; }

   __event void add_E1(f* d) { _E += d; }

   static void Go() {
      E* pE = new E;
      pE->E1 += new f(pE, &E::handler);
      pE->E1(17); // prints 17
      pE->E1 -= new f(pE, &E::handler);
      pE->E1(17); // no output
   }

private:
   __event void raise_E1(int i) {
      if (_E)
         _E(i);
   }

protected:
   __event void remove_E1(f* d) {
      _E -= d;
   }
};

int main() {
   E::Go();
}

Output

17

The user can also define a public method that calls raise.

Example

// __events4.cpp
// compile with: /clr /LD
#using <mscorlib.dll>
using namespace System;
__delegate void f(int);
__gc struct E {
public:
   __event f * E1;
   void fire_tag_E1(int idx) {
      E1(idx); // calls raise_E1(idx)
   };
};

Client code can use the operator overloads to add handlers to the event queue.

Example

// __events5.cpp
// compile with: /clr
#using <mscorlib.dll>
using namespace System;
#include <stdio.h>

__delegate void ClickEventHandler(int, double);
__delegate void DblClickEventHandler(String*);

__gc class EventSource {
public:
   __event ClickEventHandler* OnClick;
   __event DblClickEventHandler* OnDblClick;

   void FireEvents() {
      OnClick(7, 3.14159);
      OnDblClick(S"Started");
   }
};

__gc struct EventReceiver {
public:
   void Handler1(int x, double y) {
      printf("Click(x=%d,y=%lf)\n", x, y);
   };

   void Handler2(String* s) {
      printf("DblClick(s=%s)\n", s->ToCharArray());
   }

   void Handler3(String* s) {
      printf("DblClickAgain(s=%s)\n", s->ToCharArray());
   }

   void AddHandlers(EventSource* pES) {
      pES->OnClick += 
         new ClickEventHandler(this,&EventReceiver::Handler1);
      pES->OnDblClick += 
         new DblClickEventHandler(this,&EventReceiver::Handler2);
      pES->OnDblClick += 
         new DblClickEventHandler(this, &EventReceiver::Handler3);
   }

   void RemoveHandlers(EventSource* pES) {
      pES->OnClick -= 
         new ClickEventHandler(this, &EventReceiver::Handler1);
      pES->OnDblClick -= 
         new DblClickEventHandler(this, &EventReceiver::Handler2);
      pES->OnDblClick -= 
         new DblClickEventHandler(this, &EventReceiver::Handler3);
   }
};

int main() {
   EventSource* pES = new EventSource;
   EventReceiver* pER = new EventReceiver;

   // add handlers
   pER->AddHandlers(pES);
   
   pES->FireEvents();

   // remove handlers
   pER->RemoveHandlers(pES);
}

Output

Click(x=7,y=3.141590)
DblClick(s=Started)
DblClickAgain(s=Started)
Show:
© 2014 Microsoft