Category Archives: Visual Basic 2005

Feedback request on new warning

We’ve been internally discussing a new warning that we’ve added to VB 2005 and how we should treat it for upgrade, and I’d like to get some feedback from those people who have been using the beta or the community previews. A common error that we see is using a reference variable before it’s been assigned a value. For example:

Dim x As Collection
x.Add(10)

This code will throw an exception because I forgot to create a new collection using New. In VB 2005, this code will produce the following warning:

Variable 'x' is used before it has been assigned a value. A null reference exception could result at runtime.

We determine this warning by doing flow of control analysis on the function and determining that you’re using the variable before you gave it a value. This is all well and good, but the warning isn’t perfect. Just because a variable hasn’t been assigned a value doesn’t necessarily mean that there’s going to be a problem. For example:

Dim x As Collection

If Test() Then
x = New Collection()
End If

If x IsNot Nothing Then
x.Add(10)
End If

The test “x IsNot Nothing” will cause a warning that x might be used before it has been assigned a value. This is sort-of true if you look at the code in a simplistic way, but it’s really not a problem, of course, because we’re just testing against Nothing. An ideal solution, and one that we’re considering beyond VB 2005, is to add a more sophisticated level of analysis to the compiler that would allow more fine-grained analysis of null reference problems that would recognize the above situation as safe. But that’s beyond the scope of this release. For now, the level of analysis that we can do is identify places where you used a reference variable without assigning it a value.

Now, to the question: have people found this warning to be annoying on upgrade? New projects don’t seem to have major issues with this warning because you fix the warnings as you go along, by and large. But upgraded projects can be a different story: depending on the coding style used, a project might have a ton of these warnings show up when you first open it in VB 2005. For example, I recently upgraded the VBParser project and spent a little while fixing up 100 or so of these warnings, all of which were what you might call “false positives” – I was using variables before they had been assigned, but always in a safe and courteous manner.

What have people’s experiences been with this warning on upgrade? Was it annoying? Was it helpful? Would it be desirable to not have the warning turned on by default in upgrade? Just to be clear: we’re not planning on changing anything as it stands right now. But now that the beta’s been out in the wild for a while, we thought it might be a good time to validate our assumptions against some cold, hard customer feedback…

Refactoring and drafting, revisisted

In response to my entry on refactoring in VB 2005, some have asked why VB couldn’t take advantage of C#’s work on refactoring the same way that C# took advantage of VB’s work on Edit and Continue. I anticipated this would come up, and the answer is that some features are subject to drafting at the current time and some aren’t.

Edit and Continue is a feature that spans the compiler, the debugger and the CLR. As such, work done in the latter two areas can be applied across all the languages that want to take advantage of Edit and Continue. Refactoring, on the other hand, is a feature that is currently implemented entirely within the compiler. Thus, there’s no opportunity for taking advantage of the work that other teams have done on the feature.

Now, one could certainly imagine a world where it might be possible to share the work on refactoring across the languages, but we’re just not there yet. Refactoring requires an intimate understanding of the structure and meaning of the code that’s being modified, which is why it’s currently implemented as a part of each language’s IDE component. If there was an abstraction layer, however, that could abstract away the different syntax and semantics of the languages to such a degree that one could manipulate C# and VB code in a language-independent way that preserved semantics, then common refactorings would be possible. At the moment, though, neither VB nor C# is architected in this way.

Given some of my experiences with the CodeDOM and WinForms designer trying to do a much simpler form of this, I’m not entirely sanguine that such an abstraction layer is possible. Perhaps some of the third-party refactoring tools will prove me wrong on this. It’s something that we talk about and will think about as we move past 2005, but for now each language is on it’s own as far as refactorings…

Refactoring: Not for VB 2005 (for the most part)

The problem with making confident predictions about the future is that time has a way of making a liar out of you despite your best intentions. As Steven posted Friday, VB will not have any refactoring features beyond “rename symbol” in VB 2005. This was an extremely painful cut and one that we tried to move heaven and earth to avoid, but in the end we didn’t feel we could get the features into the product in a high-quality way without causing serious risk to our shipping goals. Balacing the cries for refactoring against the cries for us to ship as soon as possible was difficult, and we really regret the fact that it ain’t going to happen this time (for the most part… it’s not like rename symbol is chopped liver or anything!).

On the other hand, it does mean that we get a while longer to have lots of colorful, humorous arguments about whether we should have a “Refactoring” menu in VB or not. Oh, joy… 

Form constructors and DTEE

Some people were curious what I was getting at with my quick question yesterday, so now that I’ve got a little more time to explain, let me…

The problem that we’re looking at right now has to do with some subtle interactions between several new debugging features in VB 2005. One of the features is what we call Design Time Expression Evalutation (abbreviated to “DTEE”), which is just a fancy way of saying “you can evaluate expressions and call functions from the Immediate window even when your application isn’t running.” This was a nice feature from past versions of VB that we lost in VB 2002 and we’re happy to bring back in VB 2005.

The way DTEE works is that it actually starts up a copy of your application in the background when you try to evaluate something at design time. This background copy of your application is called the vshost process, and what the debugger does is set a breakpoint at the very beginning of the program to prevent the vshost application from actually running and doing things like putting up UI, etc. The vshost process then sits there in a debug break state, waiting for the user to ask to debug the application or do DTEE.

Another feature that I think we introduced in VB 2003 but are extending in VB 2005 is something called “Just My Code.” This is a debugger feature that filters out all the non-user code from places like the callstack window and prevents the application from breaking in non-user code. This means that if you call a .NET Framework API that throws an exception, you’re going to break in your code that called the API rather than way down in the Framework (since you likely don’t have any source code for the Framework). You can always turn off JMC in the options dialog if you want to see non-user code, BTW.

Now, let’s think about a new WinForms project for a moment. A new WinForms project contains no actual user code, because all the code in the hidden partial form class is marked as non-user code. So… can you see the problem? Right. You can’t do DTEE on a new WinForms project because the vshost process tries to put the breakpoint at the beginning of the program, but since the beginning of the program is not user code, JMC prevents the breakpoint from getting hit.

This isn’t a huge, huge deal – there are a couple of ways we can attack this problem. One proposed way would be to make changes to the debugger to recognize this kind of situation and handle it in a different way. Another proposed way was to change the way that we handle Sub New in the hidden partial class, perhaps making it compiler generated (and thus allowing us to fiddle with the way the debugger sees it). This would mean automatically generating the call to InitializeComponent as well, but what happens if the user specifies their own Sub New? Should we still automatically generate the call to InitializeComponent? If so, should it be before or after the user code in Sub New?

So the purpose of my request for feedback was to get a general feel for how we might have to proceed if we went down that path. The most important thing, of course, is to First, do no harm (to mis-quote the Hippocratic Oath). In solving one problem, we don’t want to create more problems for people.

I’ll let you know what we end up doing in the comments here… Thanks for all the quick feedback!

Feedback request: Form constructors?

A quick request for feedback… How often do you:

  • Use Sub New in a form instead of handling the form’s Load event?
  • When you do use Sub New, are there things that you have to do before the call to InitializeComponent, or do things generally go after the call to InitializeComponent?

We’re thinking about how to solve some issues that we’ve uncovered in the beta and this feedback might help in deciding how to deal with them. Thanks!

Performance matters!

I was chatting with some of the people on our performance team (i.e. the people who are supposed to make sure VB is as speedy as possible), and they were talking about some plans they were considering to help gather public feedback on problem areas in the product. “What about the MSDN Product Feedback Center?” I asked. “We hadn’t thought of that,” they replied, “perhaps you should blog about it!” So consider it done. I am now officially encouraging everyone to report any major performance problems you’re having with VB using the MSDN Product Feedback Center.

To ensure that your bug gets the amount of attention due it, here are several suggestions:

  • Put the word “Performance:” in the title of the bug.
  • Include the general hardware specifications (CPU speed, memory, etc.) that you’re testing on.
  • Include any changes to the environment that you’ve made (i.e. what tool windows are showing, which profile you are using, etc.).

And, most importantly:

  • Include specific steps that will allow us to easily reproduce the problem.

The last point is important to ensure that we actually can do something in regards to your bug report. Saying “the product is slow,” isn’t going to help us. Saying “when I show the Autos window and then hit the Step Through toolbar button on this code, it takes several seconds.” is more like it. Remember, the more specific you are, the higher chance we’ll be able to fix the problem.

I’m also talking to Robert Green to see if we can’t get a specific “Performance” Problem Type added to the Feedback Center to help categorization. The wheels grind slowly, though, so it may be a while on that one.

Volatile reads and writes in VB

Warning: Seriously geeky stuff ahead. May not be suitable for children or anyone who doesn’t love the minutia of JIT optimizations and the CLR memory model.

A reader, Russ, asks:

Given the importance of volatile in light of Greg’s comments here: http://blogs.msdn.com/grantri/archive/2004/09/07/226355.aspx. why doesn’t vb.net support a similar construct? or are we not allowed to write high-performance multithreaded code using vb? :p

The construct that Russ is referring to is volatile fields. C# allows you to declare field with the “volatile” modifier, which results in all accesses to the field being prepended in the IL with the opcode “volatile.” I’d like to pretend that I could adequately explain to you what volatile reads and writes actually do, but there is a high probability that I would get it wrong because the CLR memory model is not my thing. It is, however, Vance’s thing and you can check out a message he wrote that talks in more detail about the subject.

Although VB doesn’t support the volatile modifier, you’re not up a creek without a paddle. In fact, as I said above, marking a field as volatile in C# doesn’t do anything to the field itself – it just makes sure that the compiler prefixes all reads and writes with the volatile modifier. Volatility is really a property of reading and writing from a field, not a property of the field itself. In fact, there may be situations where you only care at certain points whether you do a volatile read or write, in which case the volitile field modifier is overkill (because it makes all reads and writes volatile). A slightly more ideal situation would be able to mark just a particular read or write as being volatile.

The CLR provides a way to do this, sort of. The type System.Threading.Thread provides VolatileRead and VolatileWrite methods that allow you to do volatile reads and writes from fields without having to do any declarative work at all. So, everything is right with the world, correct? Unfortunately, no. If you look at the definition of VolatileRead and VolatileWrite, they use reference parameters (ByRef) to refer to the field that you’re reading or writing from. This is necessary because if they were regular value parameters (ByVal) the VolatileWrite wouldn’t work at all and VolatileRead wouldn’t give you the desired behavior. (That’s because value parameters are copied onto the stack. That copy wouldn’t be a volatile read. When you pass the address of the value, then the VolatileRead method can do a volatile read of the value at that address.)

The problem is that when you pass a variable to a reference parameter, the type of the variable must exactly match the type of the reference parameter. If it doesn’t, then you have to create a temporary variable of the right type, copy the value into the temporary, call the method and then copy the value out of the temporary. This isn’t an issue for primitive types because there are overloads of VolatileRead and VolatileWrite for all the primitive types. But what about some random reference type like an array (which occurs in Grant’s example)? The closest overload is a ByRef parameter of Object, but that’s still not going to be an exact match. That means you fall into the copy-in/copy-out scheme I talked about a moment ago, which screws everything up because the reads and writes to the temporary variable are not volatile! Arrrrg!

So, are VB users just screwed? Fortunately, no. There’s another method on System.Threading.Thread that can help out here: MemoryBarrier. When you call MemoryBarrier before a write or after a read, you effectively make the read or write a volitile read or write. (In fact, in Whidbey this is how VolatileRead and VolatileWrite are implemented.) You could even write a generic VolatileRead and VolatileWrite to do this for you in VB 2005:

Function VolatileRead(Of T)(ByRef Address As T) As T
VolatileRead = Address
Threading.Thread.MemoryBarrier()
End Function

Sub VolatileWrite(Of T)(ByRef Address As T, ByVal Value As T)
Threading.Thread.MemoryBarrier()
Address = Value
End Sub

So, yes, VB users are allowed to write high-performance multithreaded code… 🙂

Nice words for My…

Jesse Liberty has written a very nice article on My for O’Reilly’s OnDotnet.com. He starts with a theme we’ve seen a lot of over the years:

For a couple of years now, I’ve been touting the Microsoft-endorsed sentiment that it really doesn’t matter if you program in C# or in VB.NET, since both are just syntactic sugar layered on top of MSIL (Microsoft Intermediate Language, the true language of .NET).

But then adds:

That appears to be changing a bit with Whidbey.

He then talks about some of the features you’ll get with My. He concludes with:

The My object has made creating this application almost absurdly easy. […] VB 2 has taken a dramatic lead in Rapid Application Development with the My object.

Go check it out!

Custom events

In my previous entry on events (written well before even VS 2002 had shipped), I made the comment:

VB does not have a syntax for defining events that allows you to specify the field, the add method or the remove method. Those are always implicitly generated.

Now, most of the time this doesn’t really matter. Most of the time, the code you write in the add and remove method is going to be the same boilerplate code over and over and over again, so you’re going to want to just let the compiler do its thing and not worry about it too much. However, there are some situations in which you might want to take over managing an event’s delegate. The most common case that I know of is the situation in which you have an object that raises a lot of events. For example, a Form can raise something like 85 different events. If you accept the default compiler behavior, this means that the compiler will generate a field for each and every event to store the event handlers for that event. Which means in the case of Form, that it would generate something like 85 fields, even though in most cases programmers only ever handle about 4-5 events on a Form!

One alternative to wasting all that space is to use a hashtable to store delegates for just the events that someone is handling. To do this, though, you need to be able to control what happens when someone hooks up to or unhooks from an event. So, in VB 2005, we’re introducing something we call custom events that look something like this:

Class C1
    Public Custom Event MyEvent As EventHandler
        AddHandler(ByVal d As EventHandler)
            ...
        End AddHandler

        RemoveHandler(ByVal d As EventHandler)
           ...
        End RemoveHandler

        RaiseEvent(ByVal o As Sender, ByVal e As EventArgs)
            ...
        End RaiseEvent
    End Event
End Class

Custom events are declared with the Custom modified on the event declaration and have to explicitly state their delegate type. Custom events have three parts: an AddHandler method that is called when someone is hooking up to the event, a RemoveHandler method that is called when someone unhooks from the event and a RaiseEvent method that is called when the class does a RaiseEvent on the event. The AddHandler and RemoveHandler methods take a delegate of the type of the event. The RaiseEvent method takes the same parameters as the event delegate does. So, to store all event delegates in one hashtable, you could do the following:

Class C1
    Private EventDelegates As New Dictionary(Of String, EventHandler)

    Private Sub AddNewHandler(ByVal eventName As String, ByVal handler As EventHandler)
        If EventDelegates.ContainsKey(eventName) Then
            EventDelegates(eventName) = CType([Delegate].Combine(EventDelegates(eventName), handler), EventHandler)
        Else
            EventDelegates(eventName) = handler
        End If
    End Sub

    Private Sub RemoveExistingHandler(ByVal eventName As String, ByVal handler As EventHandler)
        If EventDelegates.ContainsKey(eventName) Then
            EventDelegates(eventName) = CType([Delegate].Remove(EventDelegates(eventName), handler), EventHandler)
        End If
    End Sub

    Private Sub RaiseOneEvent(ByVal eventName As String, ByVal sender As Object, ByVal e As EventArgs)
        If EventDelegates.ContainsKey(eventName) Then
            Dim p As EventHandler = EventDelegates(eventName)

            If p IsNot Nothing Then
                p.Invoke(sender, e)
            End If
        End If
    End Sub

    Public Custom Event MyEvent As EventHandler
        AddHandler(ByVal d As EventHandler)
            AddNewHandler("MyEvent", d)
        End AddHandler

        RemoveHandler(ByVal d As EventHandler)
            RemoveExistingHandler("MyEvent", d)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
            RaiseOneEvent("MyEvent", sender, e)
        End RaiseEvent
    End Event
End Class

One thing to notice that’s different from C# is that we make you specify a RaiseEvent method. This is to enable the RaiseEvent statement, which C# doesn’t have, to work properly. Otherwise, it works pretty much the same way C#’s event declaration does.