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.