Category Archives: Visual Basic 2005

The Truth about Nullable Types and VB…

There’s been a little confusion about how VB’s going to handle nullable types in VB 2005, so let me try and amplify a little bit on the entry on the VB team blog.

What are nullable types?

One of the primary differences between value types such as Integer or structures and reference types such as Form or String is that reference types support a null value. That is to say, a reference type variable can contain the value Nothing, which means that the variable doesn’t actually refer to a value. In contrast, a value type variable always contains a value. An Integer variable always contains a number, even if that number is zero. If you assign the value Nothing to a value type variable, the value type variable just gets assigned its default value (in the case of Integer, that default value is zero). There is no way in the current CLR to look at an Integer variable and determine whether it has never been assigned a value – the fact that it contains zero doesn’t necessarily mean that it hasn’t been assigned a value.

Unfortunately, there are situations where it would be nice for value types to have an actual null state. The most common example is in a type that represents information from a database. Many databases allow columns of any type to contain a null value that means “you didn’t assign a value to this column.” For example, an Employee database might assign null to the Salary column of retired workers, meaning that they no longer have a salary (instead of assigning some magic value like zero to their salary, which might be accidentally confused to mean that they are still working for no money). The fact that the CLR doesn’t support null values for value types can be somewhat painful in this situation and is the reason that we have the System.Data.SqlTypes namespace. But those types do a lot of extra SQL-type stuff. Wouldn’t it be nice to have this for all value types?

The answer to this is yes, and the generics support added in VS 2005 allows us to do it. In VS 2005, we’re introducing a new generic type called Nullable(Of T) that adds nullability to any value type. How it does this is beyond the scope of this entry and is left as an exercise for the reader. But, suffice it to say, it works just fine. Here’s an example:

Sub PrintValue(ByVal i As Nullable(Of Integer))
    If i.HasValue Then
        Console.WriteLine(CInt(i))
    Else
        Console.WriteLine(“Null value!“)
    End If
End Sub

What is C# doing?

From this base level of functionality, C# is taking nullable type support a bit further in VC# 2005. The primary features they’re implementing are:

  • A new type modifier ? that means “nullable.“ So instead of writing “Nullable<int> i;“ in C#, you can write “int? i;“.
  • A “coalesce“ operator ?? that is a quick way of converting a nullable type to a non-nullable type with a default value. So “int x; int? y; x = y ?? 0“ will assign the value of y to x if it is not null, and will assign the value 0 to x otherwise.
  • Lifted conversions. If a type T has a conversion to type U, then the type T? will have a convertion to type U?. This means you can convert an int? to a long? without having to do explicit null checks.
  • Lifted operators. If a type T supports an operator X, then the type T? supports the same operator. This means that you can add two int? values and get an int? value in return without having to do null checks.

Beyond the question of convenience, the last point means that nullable types in C# will support null propagation semantics. If you add two int? values and one of them happens to be null, then the result of the addition is going to be null. The one place where they break this is for comparison operators – unlike a language like SQL, comparing two nullable types is always going to result in a bool value, not a bool? value. I understand why they’re doing this, but it is an unfortunate thing to have to do.

What is VB doing?

Very unfortunately, nothing will be done in VB 2005 beyond what’s already supported by the Nullable type. We would very much like to support features along the lines of what C# is doing (although I don’t think you’ll ever be writing “Dim x As Integer?”), but nullable types were introduced into the BCL too late in the product cycle for us to be able to add extra features this time around. It’s very disappointing, to say the least, but we do look forward to adding more support in the future. (One interesting point is that it is likely that VB will be able to preserve null propagation when lifting comparison operators because we have two comparison operators. Score another point for Is vs. =.)

This isn’t to say that Nullable types won’t be very useful in VB – just that some extra niceties you’ll get in C# will be, um, temporarily unavailable. May we suggest while you’re waiting that you enjoy the other fine features of our language?

Send us those Watson reports!

I would have to second Cyrus’s request for everyone to send us Watson crash dumps when you run into problems. Watson is an invaluable tool for finding squirrly bugs that may be difficult for us to reproduce or require configurations that, for some reason, we don’t test. I will also add that debugging a Watson crash dump is a big pain in the rear, so you can also take some perverse pleasure in the fact that whoever introduced the bug in the first place is going to have to suffer to fix it. (I confess that sometimes I get a little jolt of satisfation on this as a user when I’ve hit some particularly bad crash.)

Interestingly, in VS 2002 and VS 2003, the VB compiler itself won’t report most crash dumps. That’s because Watson normally works by catching unhandled exceptions that escape out into the system. The problem with this is that after Watson finishes collecting its crash information, it really can’t do anything but kill the offending process, so you lose everything you’ve got in the IDE at that point. So instead of having compiler crashes result in losing everything, the VB compiler catches all of its own exceptions and gives you a dialog that says, in effect, “Something bad really happened. Save all your work and restart the IDE.” Since we never let any exceptions escape out into the system, Watson never gets involved. (I believe the C# compiler does the same thing in VS 2002 and VS 2003.)

For VS 2005, Watson has been enhanced to allow us to invoke it without actually having to kill the application. So going forward, VB will give you a Watson dialog to let you report the crash and then let you save your data. So we expect to get a lot more Watson reports this time around, which is a good thing. (As a side note, in VS 2002 and VS 2003, we did allow exceptions to escape to Watson during the betas so we could get better beta information. It was just suppressed in the released retail version.)

Interestingly, one of the handful of features that I actually implemented in VB 2005 was enabling this Watson reporting…

Attributes, CLS compliance and VB 2005

Since I’m answering questions today, I might as well address Robert‘s question as to why VB doesn’t support attributes that take 1-dimensional arrays. Actually, not only does VB not support attributes that take 1-dimensional arrays, we also don’t support attributes that take parameters typed as Object. So why not? Time, mostly, but the CLS comes in to play here as well.

Attributes that take 1-dimensional arrays and Object are not CLS compliant, as per CLS rule #34 (see pg. 118 of Jim Miller’s excellent Common Language Infrastructure Annotated Standard book). Interestingly, though, the C# compiler does not warn you when you declare attribute constructors that use the non-CLS compliant types but you declare your assembly to be CLS compliant. My take on it is that they have a bug in their compiler and that they should warn you. However, I suspect they might also make a claim that the vague wording of the rule makes their compiler behavior correct – the rule states that other types cannot be “encoded,” so I guess they could argue that it’s not the attribute definition that’s non-CLS compliant, but some theoretical use of it. However, the CLS rules generally talk about prohibit you from declaring something non-CLS compliant not using something that’s non-CLS compliant, so I’m not sure I would lend this argument (if, indeed, they actually wanted to make it) much credence.

Anyway, language lawyering aside, the issue was that attributes came online very late in VB 2002, and we ended up not support 1-dimensional arrays and object values in attributes for time reasons. We figured it wouldn’t be that big of a deal because, hey, they’re not CLS compliant! Of course, since there’s no warning from the C# compiler about using these types in attributes, people expect them to work in VB and, when push comes to shove, the CLS argument just isn’t very satisfying. Which is all a long way of saying that we’re supporting them in VB 2005.

And I’m going to have to go now and test to see whether we give you a CLS warning…

vbc.rsp, coming in VB 2005!

Jay’s blog entry on references and such reminded me of another small feature that we’re doing for VB 2005, the default response file for vbc.exe. Basically, this feature makes the VB command-line compiler work in pretty much the same way that the C# command-line compiler works today: there is going to be a file vbc.rsp that sits with the command-line compiler and includes a lot of default settings for compilation, mainly references and imports. If you don’t want the default response file, you can specify the /noconfig switch and we won’t load the default settings.

Jay’s main point, though, is about the difficulty of getting to classes that you know must exist but which you don’t have a reference or import for yet. He suggests that projects by default should reference the entire framework but this won’t work, at least for VB. I’ve been doing quite a bit of work looking at how long it takes to create a new WinForms project in Whidbey and it turns out that the Frameworks DLLs are big and getting bigger. Just loading all the symbols out of them so that we can do Intellisense and code model stuff consumes quite a bit of memory. So the thought of referencing even more DLLs makes me shudder – C#’s Intellisense/code model engine might be different, but for VB the “small performance impact” would be very big. Unless, of course, you’ve got gobs of available memory, which we all have, right?

That said, it seems like there are other ways we could attack the problems of adding references and of adding imports. This is of particular importance to VB because of the fact that the My namespace is intended to aggregate functionality from across the Framework. How do we deal with My.Foobar, if you don’t have a reference to System.Foobar.DLL yet? So I think we’ve still got some work to do on that point.

The My namespace and C#

Don and Robert ask in comments to my previous entry, “Will C# be able to use the My namespace?” The answer is: yes and no.

Most of the functionality contained in My is implemented by types that will be defined in Microsoft.VisualBasic.DLL. So C# users are welcome to reference the DLL and use types such as System.IO.FileSystem or Microsoft.VisualBasic.MyServices.MyComputer. Besides the cognitive dissonance of having to actually use the name “VisualBasic,” there’s really no difficulty there.

However, the top level “glue” that brings all the My pieces together is something that’s compiler-specific and so C# users won’t see it. The top-level My stuff is compiler-specific for two reasons:

  • The My types exposed in a particular project are dependent on the type of the project. A web service project will have a different My namespace than a WinForms project will. Thus, the top-level definition of My has to be integrated into the actual building of the project.
  • Pieces of the My namespace (such as My.Forms) are dependent on objects that are actually a part of the project itself. Thus, the compiler has to specially generate those pieces of the My namespace at compile time.

Roy raises the question as to whether divergence between the languages like this is a good thing or a bad thing. While I’m sympathetic to his point about increased difficulties moving between two languages, it does raise the question: what’s the point of having two separate languages if they have exactly the same set of features? Case insensitivity vs. case sensitvity, for example, is not reason enough alone for Microsoft to invest all the money that it does building both products.

Ultimately, feature set comparability has to be a balancing act between innovation and knowledge portability. If C# and VB cannot implement a new feature without the other language implementing it, the user ultimately suffers by way of reduced language innovation. It would be hard to argue that VB users should not get Edit and Continue back because C# doesn’t plan to support it in Whidbey. Similarly, should C# users not get anonymous methods because VB doesn’t plan to support them in Whidbey? Inevitably, the best ideas of one language will be cribbed by the other language. If My turns out to be a smashing success, I’d expect C# to come sniffing ’round it the next time around. Certainly we’re watching how iterators and anonymous methods play out as we think about features for the future.

Innovation, however, does come at a cost and that can’t be ignored. But I think, in the end, the cost is worth it to both languages’ users.

If everybody downloaded Whidbey, would you do the same thing?

I hate to post on something after everybody and his sister has posted about it (mmmmm… groupthink…), but I just wanted to make sure that my corner of the world was, indeed, aware that we’ve released the first of several technical previews of Visual Studio 2005. As someone who works off of daily builds and frequently finds that things suddenly stop working for no reason, let me just say that they are strictly “let’s see what’s new” stuff and not “let’s deploy something real” stuff. Caveat emptor. Mileage may vary. See store for more details…

Events and race conditions

JayBaz points out something that is worth keeping in mind about events: they can be subject to race conditions in multi-threaded scenarios. In VS 2002 and VS 2003, you’ll need to do what the C# folks do and handle the race condition yourself:

Event Moved(ByVal x As Integer, ByVal y As Integer)

Sub Move(ByVal x As Integer, ByVal y As Integer)
Dim temp As MovedEventHandler = Me.MovedEvent

If Not temp Is Nothing Then
temp(x, y)
End If
End Sub

(The names “xEventHandler” and “xEvent” are automagically generated names – the first is the delegate type of the event and the second is the hidden field generated to hold the event delegate.)

In Whidbey, however, we’ve changed the code generation for RaiseEvent so that this will be done automatically for you. We work hard so you don’t have to!

TryCast (aka. the ‘as’ operator)

About 8 months ago, I promised to explain why the C# ‘as’ operator was worth adding to VB and then never got back to it. While trying to figure out what comments I missed, I came across the promise again and figured I’d take the opportunity to rectify the situation.

In Whidbey, we’re going to introduce YACO (Yet Another Conversion Operator) called TryCast that’s equivalent to the C# ‘as’ operator. Why? It turns out that it’s useful in some specific, but not uncommon, situations. Let’s say that you want to write a method that accepts an object and, if it implements a particular interface, do something with it. For example:

Sub Print(ByVal o As Object)
Dim PrintableObject As IPrintable

If TypeOf o Is IPrintable Then
PrintableObject = DirectCast(o, IPrintable)
PrintableObject.Print()
End If
...
End Sub

Ok, great, this works fine. The problem is, though, that we’re doing redundant work here. You see, when the CLR executes the If statement, it does a type check for the TypeOf expression. If o does implement IPrintable, it then goes ahead and casts o to IPrintable, which does another type check to ensure that the value really does implement IPrintable. So you’re doing two type checks, and type checks can be expensive. (When I say “expensive” here, I don’t mean expensive like “$760,000 for a Ferrari” expensive, I mean more like “$3.50 for a latte twice a day adds up over 365 days” expensive.)

What TryCast does is it allows you to combine the two type checks into one. TryCast, as its name suggests, will try the cast and, if it succeeds, return the value cast to that type. Otherwise, it returns the value Nothing. So you can rewrite the code above as:

Sub Print(ByVal o As Object)
Dim PrintableObject As IPrintable = TryCast(o, IPrintable)

If PrintableObject IsNot Nothing Then
PrintableObject.Print()
End If
...
End Sub

Voila, two type checks becomes one! Even if we hadn’t gotten any feature requests for this, we were planning to do it anyway because we found that several of our language helpers that deal with object values could be sped up ~5% just by eliminating the redundant type checks. So it’s not nothing. (So to speak.)

One question that might come to mind is: why not just optimize the first code into the second code under the covers instead of introducing a new operator? We did consider this at the compiler level, and the CLR could also choose to do it at the JIT level. However, while we could optimize this particular case, it’s easy to construct cases that would be difficult for the compiler or JIT to optimize. Which would mean that you could make a minor tweak to the structure of your code and suddenly lose the optimization without knowing it. In this case we figured explicitness was better…

Update 4/3/04: Made some corrections based on comments below.