Running with Code

Like scissors, only more dangerous

  Home  |   Contact  |   Syndication    |   Login
  67 Posts | 0 Stories | 84 Comments | 22 Trackbacks

News



Archives

Post Categories

All Terralever

ASP.NET

Misc

One of the more obscure features of C# is the ability to specify custom overloads for adding and removing event registration similarly to properties, via the add and remove keywords.  Known as "event accessors," they implement the parts of event registration that the C# compiler normally handles.  You didn't think that that += operator was implemented on the type, did you?

   1:  class Test
   2:  {
   3:      public event EventHandler Event1;
   4:   
   5:      private EventHandler ev2;
   6:      public event EventHandler Event2
   7:      {
   8:          add
   9:          {
  10:              if (ev2 != null)
  11:                  ev2 = (EventHandler)Delegate.Combine(ev2, value);
  12:              else
  13:                  ev2 = value;
  14:          }
  15:          remove
  16:          {
  17:              if (ev2 != null)
  18:                  ev2 = (EventHandler)Delegate.Remove(ev2, value);
  19:          }
  20:      }
  21:      protected virtual void OnEvent2(EventArgs e)
  22:      {
  23:          if (ev2 != null)
  24:              ev2(this, e);
  25:      }
  26:  }
  27:   

This pattern is actually used extensively throughout the Windows Forms library, where controls add event handlers to base event handler collections implemented within a hashtable.  I can only surmise that this is done to prevent having dozens of event fields cluttering up the classes.

Now, if we were to compile this app and disassemble it in Reflector, we'd get a very similar picture to what we've got.  Reflector would show the compiler-generated add/remove blocks for Event1, though not when the event declaration is selected, and it also indicates that there are compiler directives that show the event accessors are synchronized.

Visual Basic .NET also supports this pattern, but adds an additional keyword: the RaiseEvent keyword:

   1:  Public Class Test
   2:      Public Event Event1 As EventHandler
   3:   
   4:      Private ev2 As EventHandler
   5:      Public Custom Event Event2 As EventHandler
   6:          AddHandler(ByVal value As EventHandler)
   7:              If Not ev2 Is Nothing Then
   8:                  ev2 = CType(System.Delegate.Combine(ev2, value), EventHandler)
   9:              Else
  10:                  ev2 = value
  11:              End If
  12:          End AddHandler
  13:   
  14:          RemoveHandler(ByVal value As EventHandler)
  15:              If Not ev2 Is Nothing Then
  16:                  ev2 = CType(System.Delegate.Remove(ev2, value), EventHandler)
  17:              End If
  18:          End RemoveHandler
  19:   
  20:          RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
  21:              ev2(sender, e)
  22:          End RaiseEvent
  23:      End Event
  24:   
  25:      Protected Overridable Sub OnEvent2(ByVal e As EventArgs)
  26:          If Not ev2 Is Nothing Then
  27:              RaiseEvent Event2(Me, e)
  28:          End If
  29:      End Sub
  30:  End Class

In this example, Visual Basic allows you to implement exactly how Event2 is raised.  When I look at this in Reflector to see how C# uses this, here's what I see:

Reflector view of custom VB event

Reflector gives C# the raise keyword.  Why haven't the C# language experts done so?

How would this be worthwhile?  Well, suppose that we're building an application that can have plugins.  We don't know that plugins are always going to work correctly, so when they handle an event, they may raise an exception.  The problem is, if an event is invoked and the first event handler causes an exception, none of the successive handlers will be invoked.

Arguably, the "state of the application is undefined after an exception is raised, so we should gracefully exit."  But that's not always the case!  What if the way to gracefully do this is to analyze the stack trace within the application, determine which plugin caused the exception, and unload the plugin?  We can't do any of this from C#.

Give us the raise keyword!

This is the end of my "C# 4.0 Wishlist" series.  For reference, here are the other articles:

posted on Sunday, January 27, 2008 11:25 PM

Feedback

# re: My C# 4.0 Wishlist, Part 5 : The raise Keyword 1/28/2008 5:57 AM Gabriel Lozano-Moran
On a sidenote the way you raise the events there is a possible "raise" ;-) condition.

# Can be done in C# 3/4/2008 5:56 AM Shaun Austin
Hi,

What you suggest can be done already in C# although certainly not in as elegant a way as a "raise" keyword:

foreach(Delegate del in MyEvent.GetInvocationList())
{
try
{
del(this, args);
}
catch(... ex)
{
// handle and unregister plugin here.
}
}

That's just off the top of my head but I have used similar before.

# re: My C# 4.0 Wishlist, Part 5 : The raise Keyword 11/4/2008 5:50 AM Ron Inbar
No, Shaun, I think you're missing the point: your code is great, but where do you intend to plug it? The RaiseEvent accessor in VB provides a hook for customizing the way the event is raised. In C#, you raise an event directly by referencing the underlying delegate, which is a little problematic because if the event is declared 'public,' so is the delegate. In VB, on the other hand, there is special syntax to raise an event, using the RaiseEvent keyword in a statement, which, in the case of a custom event, calls the RaiseEvent accessor. The best you can do in C# is provide an On<event name> method and hope that no one attempts to raise the event directly.

Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 1 and 5 and type the answer here: