다음을 통해 공유


Declaring Delegates and Events

"urn:schemas-microsoft-com:office:office" />What’s
Different in the Revised Language Definition?

Delegates and Events

 

 

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.
As these things go, this has proved to be completely non-controversial (J).
That is, there have been no advocates for the retention of the double underscore,
which everyone now seems to agree gave
the original language a somewhat grotty feel.

// the original
language (T1)

__delegate void ClickEventHandler(int, double);

__delegate void DblClickEventHandler(String*);

__gc class EventSource
{

   __event ClickEventHandler*
OnClick;

   __event DblClickEventHandler*
OnDblClick;

            //

};

// the revised
language (T2)

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
is more apparent in T2 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 the T1 design, 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 T1 language specification:

// under the
original T1 language

// explicit
implementations of add, remove, raise …

public __delegate void f(int);

public __gc struct E
{

   f*
_E;

public:

   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);

      pE->E1
-= new f(pE, &E::handler);

      pE->E1(17);

   }

private:

   __event void raise_E1(int i)
{

      if (_E)

         _E(i);

   }

protected:

   __event void remove_E1(f*
d) {

      _E
-= 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 T1 property and indexed property, the methods are shotgunned across the class
declaration. Slightly more unnerving is the absence of the actual E1 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 T2 design 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:

 

// the revised
T2 language design

delegate void f( int );

public ref struct E

{

private:

      f^
_E; // yes, delegates are also reference types

public:

      E()

      {

            _E
= nullptr; // note the replacement of 0 with nullptr!

      }

      //
the T2 aggregate syntax of an explicit event declaration

      event
f^ E1

      {

      public:

            void add(
f^ d )

            {

                  _E
+= d;

            }

      protected:

            void remove(
f^ d )

            {

                  _E
-= d;

            }

      private:

            void raise( int i
)

            {

                  if (
_E )

                  _E(
i );

            }

      }

      static void Go()

      {

            E^
pE = gcnew E;

            pE->E1
+= gcnew f( pE, &E::handler );

            pE->E1(
17 );

            pE->E1
-= gcnew f( pE, &E::handler );

            pE->E1(
17 );

      }

};

Although people tend to discount syntax as non-glamorous
and trivial in terms of language design, it actually has a significant if largely
unconscious impact on the user’s cognitive experience of the language. A confusing
or inelegant syntax increases the hazardousness of the development process in much
the same way that a dirty or fogged windshield increases that of driving. In T2, we’ve
tried to make the syntax as transparent as a highly polished, newly installed windshield.

 

disclaimer: This posting is
provided "AS IS" with no warranties, and confers no rights.

Comments

  • Anonymous
    December 16, 2003
    Will there be a similar short-hand for properties as there is for properties?delegate void f( int ); public ref struct E { public: E() { E1 = nullptr; // note the replacement of 0 with nullptr! } public event f^ E1; static void Go() { E^ pE = gcnew E; pE->E1 += gcnew f( pE, &E::handler ); pE->E1( 17 ); pE->E1 -= gcnew f( pE, &E::handler ); pE->E1( 17 ); } };
  • Anonymous
    December 17, 2003
    The comment has been removed
  • Anonymous
    March 18, 2004
    Hi Stan,

    Whay it is necesary to use "-=" and "+="
    operators, if they are forwarded to memeber
    function "add" and "remove"?

    If prefer one of them goes away and another
    stays. Honestly, I would prefer "add"
    & "remove" stay.

    Thanks.