Tuesday, October 11, 2005

Event driven programming with Visual Basic .NET

If you are a C# developer with no knowledge of Visual Basic.NET, probably you would like to read this instead.

If you have programmed in C# and VB.NET you would have noticed that the languages are very similar and there exists a line-to-line correlation with the same program written in these languages. In fact many people say that C# is just VB.NET with curly brackets (or vice-versa if you prefer). Many tools exist that provide translation from VB.NET to C# and viceversa at a good accuracy. But when I wrote my eventhandling tutorial in C#, I decided that a VB.NET version is also needed because there is some difference between the two, albeit on a superficial layer.

EventHandling mechanism in VB.NET provides better flexibility than that of C#. Although the underlying paradigm remains same, the VB.NET compiler provides some shortcuts which can be a real convenience (it actually depends on the developer's mindset whether it is actually a convenience or not).
There are 2 ways by which a Publisher can publish an event and 2 ways to handle it. The first method is to declare a delegate and an event seperately.

Namespace VBEvents
Public Class Publisher

Public Delegate Sub StateChangedEventhandler(ByVal sender As Object, ByVal args As EventArgs)
Public Event StateChanged As StateChangedEventhandler

Public Sub CauseStateChange()
Console.WriteLine("Publisher going to raise event")
RaiseEvent StateChanged(Me, EventArgs.Empty)
End Sub
End Class
End Namespace

Here we can see that there is a Delegate that defines the eventhandler's signature and a Event of the same type as the delegate. To raise an event we use the RaiseEvent keyword. Everything straightforward so far. Now lets see the second method.

Namespace VBEvents
Public Class AnotherPublisher
Public Event StateChanged(ByVal sender As Object, ByVal args As EventArgs)

Public Sub CauseStateChange()
Console.WriteLine("AnotherPublisher going to raise event")
RaiseEvent StateChanged(Me, EventArgs.Empty)
End Sub
End Class
End Namespace

Here we combined the the delegate and event declaration into one as

Public Event StateChanged(ByVal sender As Object, ByVal args As EventArgs)

Note that this is a convenience provided by the VB.NET compiler and that the IL produced as similar in either cases. The compiler automatically creates a delegate for you and names it by appending "EventHandler" to the Event's name. Thus the final outcome is one and the same. This method is not available in C#.

Now lets look at the subscription process. The first method is to subscribe is shown here :

Namespace VBEvents
Public Class Subscriber

Private m_Pub As Publisher

Public Sub New(ByVal pub As Publisher)
m_Pub = pub
'subscribe
AddHandler pub.StateChanged, AddressOf pubEventHandler
End Sub

Private Sub pubEventHandler(ByVal sender As Object, ByVal args As EventArgs)
Console.WriteLine("Handled event raised by " & sender.GetType().ToString())
End Sub

Public Sub RemovePubHandling()
'unsubscribe
RemoveHandler m_Pub.StateChanged, AddressOf pubEventHandler
End Sub
End Class
End Namespace

Here we use AddHandler to subscribe to an event and RemoveHandler to unsubscribe.

The second method uses the WithEvents keyword on a reference to the publisher to tell the compiler that we are interested in handling the events that this publisher would raise. To specify which method acts as the eventhandler for which event, we use the "handles" keyword.

Namespace VBEvents
Public Class AnotherSubscriber
Private WithEvents m_anotherPub As AnotherPublisher

Public Sub New(ByVal anotherpub As AnotherPublisher)
m_anotherPub = anotherpub
End Sub

'eventhandler for StateChanged event
Private Sub pubEventHandler(ByVal sender As Object, ByVal args As EventArgs) Handles m_anotherPub.StateChanged
Console.WriteLine("Handled event raised by " & sender.GetType().ToString())
End Sub

Public Sub RemoveAnotherPubHandling()
RemoveHandler m_anotherPub.StateChanged, AddressOf pubEventHandler
End Sub
End Class
End Namespace

The difference between these two methods is that in the second method the subscription is done statically (i.e.its hardcoded that a procedure will act as the eventhandler for a particular event while in the first method we can dynamically choose the eventhandler. The unsubscription process is same for either methods. Note that you can assign different instances of the publisher class to the withevents variable thereby handling events raised by different objects. But the downside is that there is a performance problem associated with assignment to a withevents variable. For more details on this, see Jeff Richter's most excellent book - Applied Microsoft.NET Programming with VisualBasic.NET.

If you were really paying attention you should be wondering how the "Withevents" does its trick. If we make assignment to a WithEvents variable, how will the compiler subscriber to that instance and unsubscribe from the previous instance that the variable referred to? To see whats really happening load up ILDASM (which should be your best friend if you want to see whats happening under the hood) and you can see that the compiler adds two methods to access the withevents variable. If your withevents variable is named m_Pub, the methods will be named get_m_Pub and set_m_Pub. When you make an assignment to a withevents variable, the compiler will check whether the variable currently refers some instance or not. If it refers to some instance, we unsubscribe from that instance and then subscribe to the new instance (this is what creates the performance problem which I referred to in the last paragraph).

C# makes use of the overloaded += and -= operators for subscription while VB.NET uses AddHandler and RemoveHandler for the same purpose. If you use ILDASM, we can see that the compiler creates two procedures in the publisher class behind your back for the subscription and unsubscription processes. If you event is named myEvent, the procedures will be called add_myEvent and remove_myEvent.

P.S. I know the code formatting sucks, but it would be a hell of a job to manually format it, but maybe I would do it some other time

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home