Visual Basic, just like Visual Studio, will be skipping directly from version 12 to version 14. Though many of these features are also new to C#, there are quite a few enhancements meant specifically to smooth some of VB’s rough edges. Here are some of the more interesting we were able to find.
Support for Nulls
For dealing with nulls, there is the ?. operator. Like its C# counterpart, this evaluates the right-hand expression if the left-hand value isn’t null. This is especially useful when working with sparse data from external sources. For example:
If customer.PrimaryResidence IsNot Nothing AndAlso customer.PrimaryResidence.Garage IsNot Nothing AndAlso property.PrimaryResidence.Garage.Cars = 2 Then Print("Two Car Garage")
becomes simply
If property.PrimaryResidence?.Garage?.Cars = 2 Then Print("Two Car Garage")
The If operator can be combined with this to supply a default value:
Dim numberOfCarPorts = If(property.PrimaryResidence?.Garage?.Cars, 0)
C# and VB are not the only languages that have null handling like this. Objective-C, well known for its use in Apple products, has this behavior by default. Specifically, method invocations using the dot operator works like VB’s ?. operator.
In the Objective-C community, this feature has a mixed reputation. Some developers love that they don’t have to worry about null reference exceptions when performing method invocations. Others hate that they don’t get null reference exceptions and instead the method silently fails, leaving them confused as to why there is a null where they were expecting it.
Meta-programming
In Visual Basic 12, we saw the introduction of the CallerNameAttribute. While this solved the property change notification problem, it wasn’t general enough for other cases where an identifier needed to be expressed as a string. For these other cases, the NameOf operator comes into play.
The first example offered by Lucian Wischik of the Visual Basic team involves parameter validation.
Function Lookup(name As String) As Customer If name is Nothing Then Throw New ArgumentNullException(NameOf(name))
This eliminates the possibility of renaming the parameter without remembering to change the string in the exception constructor. Since the NameOf operator creates constants; you can use it wherever you would use a hard-coded string.
String Interpolation
String.Format has had developers counting parameters since .NET was created well over a decade ago. Mistakes in the count have probably led to countless bugs over the years. String interpolation, a technique first demonstrated in C# by the Mono team, finally ends that practice.
Interpolated strings begin with $" instead of just ". Each position where you want to insert a value is escaped in braces in the same way String.Format would be. And like String.Format, you can include formatting options. Here is a basic example using the variables name and total, the latter being formatted as currency.
Dim message = $"Hello {name}, your amount due is {total:C2}"
Just as with String.Format, which is used by this syntax, one still has to watch out for contextual escaping. Consider this string:
Dim requestUrl = $"http://{server}/customer?name={customerName}"
This is actually a bug. What the developer really wanted was:
Dim requestUrl = $"http://{server}/customer?name={UrlEncode(customerName)}"
FormattedString
Looking at this syntax, it seems like it wouldn’t be possible to use in scenarios where you need to pass in the string from an external source, such a localization table or resource dictionary. However, that is something Microsoft is working on. Lucian Wischik writes,
It will also be possible to use different cultures, and also just to extract the raw format-string and arguments (e.g. if you wanted to use it for SQL queries and needed to escape the arguments to prevent string-injection attacks). But we haven't yet closed on the design for that.
Under the current draft specification, an interpolated string can be an actual string or it can be an object called FormattedString. This object is created when you try to assign an interpolated string to a variable or parameter of type IFormattable.
The IFormattable.ToString method on this object accepts an IFormatProvider, which can be used to override the formatting behavior.
string IFormattable.ToString(string ignored, IFormatProvider formatProvider) { return String.Format(formatProvider, format, args); }
In the above code sample, format and args are from the original string to be interpolated and its values.
Multi-Line Strings
Another feature now supported in VB is multi-line strings. No special syntax is needed; simply omit the closing quotation mark on lines that you want to continue. It will use vbCrLf, vbCr, or vbLf depending on what the source code file is using, which generally means vbCrLf for Visual Studio users.
Today, some developers will fake this feature using CData sections inside an XML literal. This usually works, but is rather verbose and clumsy.
Properties
Auto-properties can now be marked as read-only. The value for the property can be supplied in-line with the property or in the constructor.
The syntax is as one would expect:
Public ReadOnly FirstName As String = "Anonymous" Public ReadOnly LastName As String Public Sub New (firstName As String, lastName As String) Me.FirstName = firstName Me.LastName = lastName End Sub
This feature has some subtle corner-cases that should be considered. To understand them, you first must understand the concept of copy-in/copy-out parameter passing. In the CLR, you can only pass variables and fields ByRef (C# ref or out). However, in VB, you are also allowed to pass properties ByRef.
To reconcile this discrepancy, VB creates a local variable just before the function call. The property value is copied into the local variable. This local variable is passed to the function, which can update it. Once the function returns, the value is copied back from the local variable to the property.
When combined with a read-only auto-property, the following rules apply:
- If you are in a lambda expression in a constructor, emit a syntax error.
- If you are in a constructor or initializer, copy-in and copy-out apply. The copy-out operation actually writes to the underlying field.
- Otherwise, copy-in only. The copy-out operation doesn’t occur, nor is it a syntax error.
These rules are based on how read-only fields currently work.
Comments
Comments can now be placed at the end of each line in a multi-line statement. Previously they could only be placed at the end of the last line. Here is an example:
Dim emailList = From c in Customers Where c.IsActive 'ignore inactive customers And Not c.DoNotEmail 'we don’t need another spam violation Select c.FullName, c.EmailAddress
Structures
Structures can now have parameterless constructors. While supported by the CLR, no major language has offered this feature before because it is unclear when the constructor would be run. For example, structure constructors are not run when creating an array of that type.
Now clearly it would be run if you write myStruct = New MyStructrure() and wouldn’t if you wrote myStruct = Nothing, but what if you left a local or member variable initialized? No matter which answer you choose, someone will be upset.
Date Literals
Date literals (a much needed feature in JSON) have finally been upgraded to use an ISO standard format that begins with the year. In the past, it used the format associated with the United States, leading to much confusion for those in Europe.
- Old style: #3/4/2005# (March 4th or April 3rd?)
- New style: #2005-4-3#
C# Interoperability
The Overrides modifier now implies the Overloads modifier. Previously, VB developers would need to specify both in order to ensure C# users of a library would get the correct overload resolution.
Interface Ambiguity
Using interface inheritance, C# can create situations where determining which interface method is being called is ambiguous. VB disallowed this scenario, but since C# allowed it we found ourselves with interfaces VB simply couldn’t implement. (This was actually seen in a few places for one of the Microsoft Dynamics products.)
Rather than trying to follow C#’s hide-by-name overload resolution rule, VB 14 will relax those restrictions and shift to a more traditional (for VB) hide-by-signature rule.
Namespace Resolution
Another problem VB used to trip over was namespace resolution. Consider this code:
Threading.Thread.Sleep(1000)
Previously, VB used to look up the namespace "Threading", discover it was ambiguous between System.Threading and System.Windows.Threading, and just give an error. Now, VB14 will entertain both possible namespaces at once. If you type Threading. in the code editor then in intellisense after the dot you'll see the members of both namespaces.
There are many other similar cases - e.g. ComponentModel.INotifyPropertyChanged used to be ambiguous in Winforms apps between System.ComponentModel and System.Windows.Forms.ComponentModel but is now fine.
TypeOf IsNot
A decade ago Microsoft patented the IsNot operator. Since then, VB developers have been asking Microsoft to allow the use of IsNot in TypeOf expressions. For example,
If TypeOf sender IsNot Button Then
Pre-processor Directives
VB 14 offers two improvements for the pre-processor directives.
Regions
Regions can now appear inside functions and even cross the boundary between two functions.
Disabling Warnings
Like C#, Visual Basic will now be able to disable compiler warnings for a block of code. The example offered in the spec is:
#Disable Warning BC42356 'suppress warning about no awaits in this method
This is usually paired with a directive to re-enable warnings for the rest of the file.
#Enable Warning BC42356
The ID for the warning must be in quotes if it contains spaces or other punctuation. Microsoft’s tools don’t do that, but third-party analyzer rules written with Roslyn might.
It appears that VB’s Quick Fix feature will include the ability to suppress warnings by automatically adding these directives for you. This would be especially useful with the aforementioned third-party analyzer rules, as you might not be able to easily look up the ID.
XML Doc Verification
Currently the VB compiler pretty much ignores the contents of the XML docs. With VB 14, it will start looking for errors like incorrect paramref names. It will also, “properly handle crefs that are generics, operators”.
Partial module and interface declarations
Like classes and structures, you will now be able to declare modules and interfaces as partial. This is generally seen as a feature for code generators, but it can also be useful when sharing code across multiple platforms.
About the Author
Jonathan Allen got his start working on MIS projects for a health clinic in the late 90's, bringing them up from Access and Excel to an enterprise solution by degrees. After spending five years writing automated trading systems for the financial sector he has decided to shift into high end user interface development. In his free time he enjoys studying and writing about western martial arts from the 15th thru 17th century.