Sunday, September 02, 2007 12:00 AM bart

Visual Basic 9.0 Feature Focus - Relaxed delegates

Welcome back to the Visual Basic 9.0 Feature Focus blog series. In this post, we'll cover Relaxed delegates, a small but useful feature that boosts developer convenience. In VB 8.0 the use of delegates was very strict concerning the methods supported to be called through a delegate. Two samples make this clear:

Delegate Sub Process(ByRef o As Object)

Module Module1

    Sub DoIt(ByRef s As String)
        Console.WriteLine(s)
    End Sub

    Sub Main()
        Dim p As New Process(AddressOf DoIt)
        p("Hello")
    End Sub

End Module

Oops, this doesn't work. But why? The IDE and compiler complain about the following:

image

What's up? VB 8.0 has the nasty habit to require an exact signature match when pointing a delegate to a method. In the sample above, the compiler complains that the parameter to DoIt is a String while it expects an Object, even though each String is an Object because of the class hierarchy. In other words, regular rules that apply to regular method calls do not hold anymore when working with delegates: signatures have to match exactly. The same holds for the use of event handlers:

Class FrontEnd

    Private WithEvents engine As MyEngine

    Private Sub OnError(ByVal source As Object, ByVal e As ErrorEventArgs) Handles engine.OnError

    End Sub

End Class

Class MyEngine

    Public Event OnError As EventHandler(Of ErrorEventArgs)

    Public Sub Fail()
        RaiseEvent OnError(Me, New ErrorEventArgs("Bang!"))
    End Sub

End Class

Class ErrorEventArgs
    Inherits EventArgs

    Private _message As String

    Public ReadOnly Property Message() As String
        Get

            Return _message
        End Get
    End Property

    Public Sub New(ByVal message As String)
        _message = message
    End Sub

End Class

The code above will compile but if you change the OnError method signature of the FrontEnd class like this:

    Private Sub OnError(ByVal source As Object, ByVal e As EventArgs) Handles engine.OnError

it won't. See the difference? The 'e' parameter now is of type EventArgs rather than ErrorEventArgs and although the class hierarchy is there to make this a valid signature in regular method call circumstances, the compiler will nag about it:

image

The solution? VB 9.0! Both fragments above will compile now in VB 9.0 thanks to the concept of relaxed delegates. In other words, the method signature use in a delegate binding shouldn't be an exact match anymore, it should just be consistent with the rules that apply for regular method invocations. But there's more... If you don't need the arguments of the method being bound to the delegate, either using AddressOf or Handles, you can omit these:

Sub DoIt(ByRef s As String)

will be accepted for the following delegate:

Delegate Sub Process(ByRef o As Object)

and:

Private Sub OnError() Handles engine.OnError

will be accepted for this delegate:

EventHandler(Of ErrorEventArgs)
= Delegate Sub(ByVal sender As Object, ByVal e As ErrorEventArgs)

Happy coding!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# re: Visual Basic 9.0 Feature Focus - Relaxed delegates

Sunday, September 02, 2007 9:37 PM by Curious

Curious on something.  On the top example, say we put p in a list.  Then blindly looping through it with other Process delegates, we call it with a vanilla Object (or something not-string ).    Where is the error identified?

Clearly it must be at runtime, but I mean where is the casting exception thrown from - as in, where would the debugger break - is it the call site, or the method itself?  

# re: Visual Basic 9.0 Feature Focus - Relaxed delegates

Sunday, September 02, 2007 11:22 PM by Steve

This is so wrong, if I create an instance of the Process delegate by passing in the DoIt sub it will still be (ByRef o As Object) which means I can pass anything, what will happen then if I pass an integer to it?

# re: Visual Basic 9.0 Feature Focus - Relaxed delegates

Monday, September 03, 2007 4:11 AM by bart

Hi Curious, Steve,

As your questions are very similar, allow me to put the answers in one comment to this post.

The compiler turns out to be pretty smart; when it encounters a scenario with relaxed delegates, it will create a helper function (which is also called a lambda expression, although it doesn't produce a result value necessarily) that does the necessary translation. For example, consider the definition below:

   Delegate Sub Process(ByVal o As Object)

   Sub DoIt(ByRef s As String)

       Console.WriteLine(s)

   End Sub

   Sub Main()

       Dim p As New Process(AddressOf DoIt)

       p(123)

   End Sub

The main function will be compiled as if it were:

   Sub Main()

       Dim p As New Process(AddressOf _Lambda$__1)

       p(123)

   End Sub

   Sub _Lambda$__1(ByRef a0 As Object)

       DoIt(Microsoft.VisualBasic.CompilerServices.Conversions.ToString(a0))

   End Sub

If your DoIt definition would be:

   Sub DoIt(ByRef i As Integer)

       Console.WriteLine(s)

   End Sub

and you were te call it as:

   Sub Main()

       Dim p As New Process(AddressOf DoIt)

       p("123")

   End Sub

the generated "lambda" would be:

   Sub _Lambda$__1(ByRef a0 As Object)

       DoIt(Microsoft.VisualBasic.CompilerServices.Conversions.ToInteger(a0))

   End Sub

Now, if you pass something that's incompatible with the target method, you'll get an InvalidCastException in the cases above and the IDE will mark the p(...) call in Main as the location of the error (because of the hidden generated "lambda" helper).

Indeed, you will only want to use relaxed delegates when it makes sense. There's happening nothing more but runtime object casting. In some repect it's similar to C#'s foreach loop where the loop variable can be declared as a subclass of the collection you're iterating over; in such a case the developer is responsible to know what the collection will contain (the input so to speak):

object[] o = new object[] { 1, 2, 3 };

foreach (int i in o)

  Console.WriteLine(i);

With calling through delegates it's pretty much the same. It just depends on the direction you're taking it. VB 8.0 wouldn't allow the signature of an event handler to be "more general" than the one defined by the object's event you're creating a handler for. Say the method uses a MyCustomEventArgs parameter, but you define a handler that does use EventArgs - nothing is wrong, but it isn't valid for VB 8.0, now it is. Another typical sample is this:

   Sub Main()

       Dim w As New ParameterizedThreadStart(AddressOf Process)

       Dim t As New Thread(w)

       t.Start()

   End Sub

   Sub Process(ByVal w As Worker)

   End Sub

You do know you'll start a thread using a ParameterizedThreadStart and you'll use a Worker object. In here the callee and the caller are tightly coupled, so it makes sense you're able to write this and let the compiler insert the appropriate casts, rather than having to use an Object parameter to the Process method which needs manual casting.

-Bart

# 7 Links Today (2007-09-04)

Tuesday, September 04, 2007 8:20 AM by 7 Links Today (2007-09-04)

Pingback from  7 Links Today (2007-09-04)

# VB 9.0 Feature Focus

Wednesday, September 05, 2007 6:50 PM by Nick's .NET Travels

In my previous post I commented that VB is coming of age in Visual Studio 2008 with better support for

# New Features of Visual Basic 9 Article Series

Thursday, September 27, 2007 5:47 AM by Walter Stiers - Academic Relations Team (BeLux)

In a series of 15 posts, Bart De Smet explores several of the new features in Visual Basic 9 . These

# Recap of European Launch Events for Visual Studio 2008 (Amanda Silver)

Monday, March 24, 2008 4:44 PM by The Visual Basic Team

Last week I had the privilege of speaking in Lisbon, Portugal & Birmingham, England at launch events

# MSDN Blog Postings » Recap of European Launch Events for Visual Studio 2008 (Amanda Silver)

Pingback from  MSDN Blog Postings  » Recap of European Launch Events for Visual Studio 2008 (Amanda Silver)

# ヨーロッパでの Visual Studio 2008 発表イベントの要約 (Amanda Silver)

Monday, June 09, 2008 11:42 PM by The Visual Basic Team

先週、 ポルトガルのリスボン と イギリスのバーミンガム での Visual Studio 2008 の発表イベントで講演する機会がありました。どちらも熱狂的な雰囲気に包まれていたのは、今回の Visual

# VB 9.0 Feature Focus – Link Collection

Saturday, August 09, 2008 7:17 AM by B# .NET Blog

Collecting a few of my posts for easy quick reference: Visual Basic 9.0 Feature Focus – Introduction