< C++ .NET Delegates & Events 3 | Main | C++ .NET Delegates & Events 5 >


 

 

Delegates and Events 4

generate, fire and handle the events...

 

 

  1. More on Delegates and Events

  2. Managed Extensions for C++: the __delegate keyword

  3. Managed C++ .NET: the delegate keyword

 

More on Delegates and Events

 

The way to declare delegates and events has changed from Managed Extensions for C++ to Visual C++ 2005. The only change to the declarations of a delegate and a trivial event is the removal of the double underscore, as in the following sample. In Managed Extensions:

__delegate void ClickEventHandler(int, double);

__delegate void DblClickEventHandler(String*);

 

__gc class EventSource

{

   __event ClickEventHandler* OnClick;

   __event DblClickEventHandler* OnDblClick;

};

In the new syntax:

delegate void ClickEventHandler(int, double);

delegate void DblClickEventHandler(String^);

 

ref class EventSource

{

   event ClickEventHandler^ OnClick;

   event DblClickEventHandler^ OnDblClick;

};

 

Events (and delegates) are reference types, which are more apparent in the new syntax due to the presence of the hat (^). Events support an explicit declaration syntax as well as the trivial form. In the explicit form, the user specifies the add(), raise(), and remove() methods associated with the event. Only the add() and remove() methods are required; the raise() method is optional. Under Managed Extensions, if the user chooses to provide these methods, she must not provide an explicit event declaration, although she must decide on a name for the event that is not present. Each individual method is specified in the form add_EventName, raise_EventName, and remove_EventName, as in the following example taken from the Managed Extensions specification:

// explicit implementations of add, remove, raise

public __delegate void funct(int);

 

public __gc struct Evt

{

      funct * _Evt;

      public:

            Evt() { _Evt = 0; }

            __event void add_Evt1(funct * d) { _Evt += d; }

 

            static void Go()

            {

                  Evt * pEvt = new Evt;

                  pEvt->Evt1 += new funct(pEvt, &Evt::handler);

                  pEvt->Evt1(17);

                  pEvt->Evt1 -= new funct(pEvt, &Evt::handler);

                  pEvt->Evt1(17);

            }

 

      private:

            __event void raise_Evt1(int i)

            {

                  if (_Evt)

                        _Evt(i);

            }

 

      protected:

            __event void remove_Evt1(funct * d)

            {  _Evt -= d;  }

};

The problems with this design are largely cognitive rather than functional. Although the design supports adding these methods, it is not immediately clear from looking at the above sample exactly what is going on. As with the Managed Extensions property and indexed property, the methods are shot-gunned across the class declaration. Slightly more unnerving is the absence of the actual Evt1 event declaration. Once again, the underlying details of the implementation penetrate up through the user-level syntax of the feature, adding to the apparent lexical complexity. It simply labors too hard for something that is really not all that complex. The new syntax hugely simplifies the declaration, as the following translation demonstrates. An event specifies the two or three methods within a pair of curly braces following the declaration of the event and its associated delegate type, as follows:

public delegate void funct(int);

 

public ref struct Evt

{

      private:

            funct^ _Evt; // yes, delegates are also reference types

 

      public:

            Evt() {  // note the replacement of 0 with nullptr!

                  _Evt = nullptr;

            }

 

            // the new aggregate syntax of an explicit event declaration

            event funct^ Evt1

            {

                  public:

                        void add(funct^ d)

                        {

                              _Evt += d;

                        }

 

                  protected:

                        void remove(funct^ d)

                        {

                              _Evt -= d;

                        }

 

                  private:

                        void raise(int i)

                        {

                              if (_Evt)

                                    _Evt(i);

                        }

            }

 

            static void Go()

            {

                  Evt^ pEvt = gcnew Evt;

                  pEvt->Evt1 += gcnew funct(pEvt, &Evt::handler);

                  pEvt->Evt1(17);

                  pEvt->Evt1 -= gcnew funct(pEvt, &Evt::handler);

                  pEvt->Evt1(17);

   }

};

In the new syntax, we’ve tried to make the syntax as transparent as a highly polished, newly installed windshield.

 

Managed Extensions for C++: the __delegate keyword

 

This topic applies only to version 1 of Managed Extensions for C++. This syntax should only be used to maintain version 1 code. See delegate keyword for information on using the equivalent functionality in the new syntax. __delegate defines a reference type that can be used to encapsulate a method with a specific signature. The following is the syntax.

__delegate function-declarator

A delegate is roughly equivalent to a C++ function pointer except for the following difference:

When the compiler encounters the __delegate keyword, a definition of a __gc class is generated. This __gc class has the following characteristics:

In the following example, a __gc class (MyCalendar) and a delegate (GetDayOfWeek) are declared. The delegate is then bound to the different methods of MyCalendar, invoking each in turn:

// __delegate keyword

// compile with: /clr:OldSyntax

 

#include "stdafx.h"

 

#using <mscorlib.dll>

using namespace System;

 

__delegate int GetDayOfWeek();

 

__gc class MyCalendar

{

      public:

            MyCalendar() : m_nDayOfWeek(4) {}

            int MyGetDayOfWeek()

            {

                  Console::WriteLine("The handler..."); return m_nDayOfWeek;

            }

 

            static int MyStaticGetDayOfWeek()

            {

                  Console::WriteLine("The static handler...");

                  return 6;

            }

 

      private:

            int m_nDayOfWeek;

};

 

int main()

{

      GetDayOfWeek * pGetDayOfWeek;  // declare delegate type

      int nDayOfWeek;

     

      // bind delegate to static method

      pGetDayOfWeek = new GetDayOfWeek(0, &MyCalendar::MyStaticGetDayOfWeek);

      nDayOfWeek = pGetDayOfWeek->Invoke();

      Console::Write("Return value: ");

      Console::WriteLine(nDayOfWeek);

      // bind delegate to instance method

      MyCalendar * pcal = new MyCalendar();

      pGetDayOfWeek = static_cast<GetDayOfWeek*>(Delegate::Combine(pGetDayOfWeek,

      new GetDayOfWeek(pcal, &MyCalendar::MyGetDayOfWeek)));

      nDayOfWeek = pGetDayOfWeek->Invoke();

      Console::Write("Return value: ");

      Console::WriteLine(nDayOfWeek);

      // delegate now bound to two methods; remove instance method

      pGetDayOfWeek = static_cast<GetDayOfWeek*>(Delegate::Remove(pGetDayOfWeek,

            new GetDayOfWeek(pcal, &MyCalendar::MyGetDayOfWeek)));

      return 0;

}

 

Sample Output:

 

C++ .NET program output sample using old codes

 

Managed C++ .NET: the delegate keyword

 

This keyword defines a reference type that can encapsulate one or more methods with a specific function prototype. Delegates provide the underlying mechanism (acting as a kind of pointer to member function) for events in the common language runtime component model. The following is the syntax.

access delegate function_declaration

 

Parameters

Description

access (optional)

The accessibility of the delegate outside of the assembly can be public or private. The default is private. Inside a class, a delegate can have any accessibility.

function_declaration

The signature of the function that can be bound to the delegate. The return type of a delegate can be any managed type. For interoperability reasons, it is recommended that the return type of a delegate be a CLS type.

To define an unbound delegate, the first parameter in function_declaration should be the type of the this pointer for the.

 

Table 2

 

Delegates are multicast that is the "function pointer" can be bound to one or more methods within a managed class. The delegate keyword defines a multicast delegate type with a specific method signature. A delegate can also be bound to a method of a value class, such as a static method. A delegate has the following characteristics:

  1. It inherits from System::MulticastDelegate.

  2. It has a constructor that takes two arguments, a pointer to a managed class or NULL (in the case of binding to a static method) and a fully qualified method of the specified type.

It has a method called Invoke, whose signature matches the declared signature of the delegate. When a delegate is invoked, its function(s) are called in the order they were attached. The return value of a delegate is the return value from its last attached member function. Delegates cannot be overloaded. delegate keyword is a context-sensitive keyword and delegates can be bound or unbound. When you instantiate a bound delegate, the first argument shall be an object reference. The second argument of a delegate instantiation shall either be the address of a method of a managed class object, or a pointer to a method of a value type. The second argument of a delegate instantiation must name the method with the full class scope syntax and apply the address-of operator. When you instantiate an unbound delegate, the first argument shall either be the address of a method of a managed class object, or a pointer to a method of a value type. The argument must name the method with the full class scope syntax and apply the address-of operator. When creating a delegate to a static or global function, only one parameter is required: the function (optionally, the address of the function). You can detect at compile time if a type is a delegate with __is_delegate(type). The following example shows how to declare, initialize, and invoke delegates.

 

// delegate keyword

// compile with: /clr

 

#include "stdafx.h"

 

using namespace System;

 

// declare a delegate

public delegate void MyDel(int i);

 

ref class deleA

{

      public:

            void func1(int i)

            { Console::WriteLine("in func1, i = {0}", i); }

 

            void func2(int i)

            { Console::WriteLine("in func2, i = {0}", i); }

 

            static void func3(int i)

            { Console::WriteLine("in static func3, i = {0}", i); }

};

 

int main(array<System::String ^> ^args)

{

      deleA ^ newobj = gcnew deleA;

 

      // declare a delegate instance

      MyDel^ DelInst;

      // test if delegate is initialized

      if (DelInst)

         DelInst(7);

      // assigning to delegate

      DelInst = gcnew MyDel(newobj, &deleA::func1);

      // invoke delegate

      if (DelInst)

         DelInst(8);

      // add a function

      DelInst += gcnew MyDel(newobj, &deleA::func2);

      DelInst(9);

      // remove a function

      DelInst -= gcnew MyDel(newobj, &deleA::func1);

      // invoke delegate with Invoke

      DelInst->Invoke(10);

      // make delegate to static function

      MyDel ^ StaticDelInst = gcnew MyDel(&deleA::func3);

      StaticDelInst(11);

 

      return 0;

}

 

Output:

 

C++ .NET program output example

 

 

 

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | Part 7

 


 

< C++ .NET Delegates & Events 3 | Main | C++ .NET Delegates & Events 5 >