Events in C# and VB.NET

The simple, VB.NET way

I don't want to start Yet Another Language Debate, but I still wonder why declaring events in C# is so cumbersome. If you want to add the MatchFound event to your class, in VB.NET you just insert the following code:

' Declare an event without arguments
Public Event MatchFound()

To actually raise the event, you just use:

' Raise the MatchFound event
RaiseEvent MatchFound()

The '.NET way'

The recommended way to declare the same event in C# is:

// Delegate for the MatchFound event
public EventHandler MatchFound;
 
// Raise the MatchFound event
protected void OnMatchFound()
{
  if (this.MatchFound != null)
    this.MatchFound(this, EventArgs.Empty);
}

What's with the weird null-check in the OnMatchFound method? (Yes, I know what it's for - but why do I need to type out this pattern in every event I create?)

The VB.NET version of this 'recommended' version of the event is almost (but not quite!) as bad:

' Delegate for the MatchFound event
Public Event MatchFound As EventHandler
 
' Raise the MatchFound event
Protected Sub OnMatchFound()
  RaiseEvent MatchFound(Me, EventArgs.Empty)
End Sub

Events with arguments

It gets worse when your event needs some arguments. In VB.NET, all you need to add is a parameter:

' Delegate for the MatchFound event
Public Event MatchFound(ByVal Position As Integer)
 
' Raise the MatchFound event
Protected Sub OnMatchFound(ByVal Position As Integer)
  RaiseEvent MatchFound(Position)
End Sub

That's pretty simple. In C#, it's a little more complicated. You need to add a declaration for a delegate that takes an integer and 'returns' void:

// Delegate for the MatchFound event
public delegate void MatchFoundDelegate(int position);
 
// The MatchFound event
public MatchFoundDelegate MatchFound;
 
// Raise the MatchFound event
protected void OnMatchFound(int position)
{
  if (this.MatchFound != null)
    this.MatchFound(position);
}

I have to admit I have trouble remembering this somewhat convoluted syntax. (That's why I created a couple of QuickCodes for it - but read on).

The .NET way with arguments

However, this type of parameter passing to event handlers is not the '.NET way'. The convention is, that an event handler takes two arguments: first, the object (the sender) itself, and then a single event argument object e, whose type is derived from EventArgs. (Note that the EventArgs class does not carry information, hence the use of EventArgs.Empty in the samples above). In VB.NET, declaring the event 'the .NET way' would look like this:

' Event argument class for the MatchFound event
Public Class MatchFoundEventArgs
  Inherits EventArgs
 
  ' Member variable to hold the position argument
  ' passed to the constructor
  Protected _Position As Integer
 
  ' The constructor, with 'Position' argument 
  Public Sub New(ByVal Position As Integer)
    Me._Position = Position
  End Sub
 
  ' Property Position (Integer). 
  Public Property Position() As Integer
    Get
      Return Me._position
    End Get
    Set(ByVal value As Integer)
      Me._position = value
    End Set
  End Property
End Class
 
' The MatchFound event
Public Event MatchFound As EventHandler(Of MatchFoundEventArgs)
 
' Raise the MatchFound event
Protected Sub OnMatchFound(ByVal Position As Integer)
  RaiseEvent MatchFound(Me, New MatchFoundEventArgs(Position))
End Sub

That looks like a lot - and it is. There needs to beĀ an EventClass derived class that takes a single integer argument in its constructor, which it exposes through a property (in this case Position). This class is used instead of the standard EventArgs class. In C#, it's a lot alike, but once again we need a delegate:

' Event argument class for the MatchFound event
Public Class MatchFoundEventArgs
  Inherits EventArgs
 
  ' Member variable to hold the position argument
  ' passed to the constructor
  Protected _Position As Integer
 
  ' The constructor, with 'Position' argument 
  Public Sub New(ByVal Position As Integer)
    Me._Position = Position
  End Sub
 
  ' Property Position (Integer). 
  Public Property Position() As Integer
    Get
      Return Me._position
    End Get
    Set(ByVal value As Integer)
      Me._position = value
    End Set
  End Property
End Class
 
' The MatchFound event
Public Event MatchFound As EventHandler(Of MatchFoundEventArgs)
 
' Raise the MatchFound event
Protected Sub OnMatchFound(ByVal Position As Integer)
  RaiseEvent MatchFound(Me, New MatchFoundEventArgs(Position))
End Sub

Declaring 'compatible' events

Conclusion: VB.NET allows you to declare and use events in a much simpler, cleaner way - the whole delegate thing is accomplished behind the scenes, while in C#, you have to type it out yourself. However, in order to adhere to .NET conventions, you can (and should, I suppose) declare your events in much the same way in VB.NET as you would in C#. This keeps them compatible, which has two advantages: in 'mixed language' scenarios both events can be called the same way, and it keeps your code patterns in sync between the two languages. So, no matter how much more convoluted C# event declarations are, you should use their style in VB.NET if possible.

Fortunately, this pattern can be easily caught in a QuickCode. A couple of QuickCodes, actually. See for yourself!