This project is read-only.

New language features in VB14

Topics: VB Language Design
Nov 12, 2014 at 3:30 PM
Edited Nov 22, 2014 at 8:37 PM

New Language Features in Visual Basic 14

Lucian Wischik and Anthony D. Green, PMs on the VB/C# language team at Microsoft

This is a summary post that draws together all the new features in VB14.

The ?. operator

This new operator is a convenient shorthand for the many occasions when you have to check for null:
Dim x = customer.Address?.Country

' is shorthand for
Dim _temp = customer.Address
Dim x = If(_temp IsNot Nothing, _temp.Address.Country, Nothing)
You can also use it in a sequence and you can mix with the regular . operator, e.g. a?.b.c?.d. It reads left-to-right. Any null value in "?." will just stop the sequence short, and any null value in "." will raise a NullReferenceException as usual.

For a string value like customer.Address?.Name, if it stops short then the result is Nothing As String; likewise for other reference types. For an integer value like customer?.Age, if it stops short, then the result is the nullable Nothing As Integer?; likewise for other value types.

You can use it in other handy ways, e.g.
If customer?.Age > 50 Then ...
' branch taken ONLY IF customer is non-null and is older than 50

Dim name = If(customer?.Name, "blank")  ' a default name if customer is null
In addition to "?." there are several other null-checking operators in VB14:
Dim x As Action(Of Integer) = GetCallbackAction()
x?(5)  ' only invoke if x is non-null

Dim x As List(Of Integer) = GetList()
Dim elem = x?(1)  ' only fetch the first element if x is non-null

Dim x As Dictionary(Of String, Object) = GetDictionary()
Dim val = x?!name  ' only look up the key "name" if x is non-null
Detailed language design notes for the ?. operator are here: https://roslyn.codeplex.com/discussions/540883

ReadOnly auto-implemented properties

VB has long had auto-implemented properties. Now they can be ReadOnly as well.
Class Customer
   Public ReadOnly Property Tags As New List(Of String)
   Public ReadOnly Property Name As String = ""
   Public ReadOnly Property File As String

   Sub New(file As String)
      Me.File = file
   End Sub
End Class
Just as with normal auto-implemented properties, you can assign initial values to ReadOnly autoprops. Also you can assign to them in the constructor: this writes direct to the backing field.

As with normal auto-implemented properties, the private backing fields of the above properties are "_Tags, _Name, _File" and these backing fields can be assigned to at any time, not just in the constructor.

There are further detailed language design notes here: https://roslyn.codeplex.com/discussions/568824

Multiline string literals

We now allow string literals that split over multiple lines.
Dim x = "hello
world"
This is cleaner and easier to use than the workaround you'd use before:
Dim x = <xml><![CDATA[Hello
World]]></xml>.Value
The string will include whatever kind of separator (vbCr or vbCrLf or whatever) that was found in your source file.

(Please let us know if parsing is working okay, or if you too often end up accidentally putting the entirety of your file into a string.)

String interpolation

Note: This feature isn't yet in VS2015 Preview
String interpolation is an easier way of writing strings with expressions in them:
Dim s = $"hello {p.Name} you are {p.Height:0.00}m tall"

' is shorthand for
Dim s = String.Format("hello {0} you are {1:0.00}m tall", p.Name, p.Height)
String interpolation is often easier than String.Format because it saves you having to juggle the positional placeholders {0} and {1}.

Although we can't show it in markdown here in this document, there's full colorization and intellisense for the expressions inside the holes.

Note that, since it's shorthand for the specified call to String.Format, (1) string interpolation uses the current culture, and (2) it isn't a constant. However the compiler is at liberty to optimize string interpolation if it knows how String.Format will behave and if it can figure a faster way to do that (e.g. by avoiding boxing).

If you want to embed an actual curly brace character you write it double, e.g. Dim s = $"{{ {guid} }}".

We are considering a design where, rather than always just giving you a string, the string interpolation would also be able to give you a "FormattedString" object. This would allow you to use invariant culture. More detailed language design notes are here: https://roslyn.codeplex.com/discussions/570614

NameOf operator

Note: This feature isn't yet in VS2015 Preview
The NameOf operator is a better way of embedding a string literal into your code, when that string literal refers to a programmatic element.
Sub f(s As String)
   If s Is Nothing Then Throw New ArgumentNullException(NameOf(s))
End Sub

Private _age As Integer
Property Age As Integer
   Get
      Return _age
   End Get
   Set
      _age = value
      RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Age)))
   End Set
End Property

Public AgeProperty As DependencyProperty = DependencyProperty.Register(NameOf(Age), GetType(Of Integer), GetType(C))
Why use NameOf rather than just a string literal? -- because NameOf is a part of the language: you get proper intellisense while you're typing its argument, and if you make a typo then you'll get a compile-time error, and it gets renamed property when you do a refactor-rename. String literals have none of these benefits.

The result of the NameOf operator is simply the unqualified identifier name that you passed into the function.

NameOf works in a few unusual ways, for convenience. You can write NameOf(x.Log) to get the name of every method overload of "Log" in x, also including extension members. You can write NameOf(Customer.Age) to get the name of an instance property off a type-name.

The NameOf operator is a new reserved keyword. (Thus, if you had declared any identifiers called NameOf in your code before, they will now produce compile-time errors.)

There are detailed language design notes on the NameOf operator here: https://roslyn.codeplex.com/discussions/570551

Comments within LINQ expressions and after implicit line continutions

Comments are allowed in more places. Here are examples which failed to compile before VS2015 but now work fine.
Dim invitees = {"Jim",    ' got to invite him!
                "Marcy",  ' Jim's wife
                "Jones"}

Dim addresses = From i In invitees      ' go through list
                Let address = Lookup(i) ' look it up
                Select i, address
In general, comments are allowed before implicit line continuations.
Nov 12, 2014 at 3:30 PM

Smart name resolution

VB14 improves its rules for qualified names:
Threading.Thread.Sleep(1000)
' In Console and Winforms this is okay, but in WPF aps it is ambiguous
' between System.Threading and System.Windows.Threading
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.

And Diagnostics.Debug.WriteLine used to be ambiguous in WinRT apps between System.Diagnostics and Windows.Foundation.Diagnostics but now is fine.

Structures allow Sub New()

Now, VB allows you to declare a parameterless constructor for a struct. Previously it was an error.
Structure MyStruct1
   Public f As Integer
   Sub New()
      f = 15
   End Sub
End Structure
If you write Dim x As New MyStruct1 then it will call the parameterless constructor. If you write Dim x As MyStruct1 = Nothing then it won't, and will just zero-initialize the structure.

Year-first date literals

I have never understood whether #11/12/2014# is supposed to be 11th December or 12th November. There's a good unambiguous ISO standard for writing dates, and VB supports a similar format:
Dim d = #2014-11-12#  ' yyyy-MM-dd, November 12th 2014
You can also use slashes #2014/11/12#, and as always you can specify the time as well #2014-11-12 19:30# or #2014-11-12 7:30 PM#

(Who embeds date literals in their code? -- anyone who's writing unit tests!)

ReadOnly interface properties can be implemented by ReadWrite props

This cleans up a quirky corner of the language. Look at this example:
Interface I
    ReadOnly Property P As Integer
End Interface

Class C : Implements I
    Public Property P As Integer Implements I.P
End Class
Previously, if you were implementing the ReadOnly property I.P, then you had to implement it with a ReadOnly property as well. Now that restriction has been relaxed: you can implement it with a read/write property if you want. This example happens to implement it with a read/write autoprop, but you can also use a property with getter and setter.

TypeOf <expr> IsNot <type>

VB has always championed syntax that reads more like a natural language. In 2005 we even added the IsNot operator to make negative reference checks read more naturally. But you still had to write negative type checks with this awkward syntax:
If Not TypeOf sender Is Button Then
Now in VB14 you can write it more readably:
If TypeOf sender IsNot Button Then

'#Disable Warning' and '#Enable Warning'

VB14 now lets you disable / enable warnings for regions within a file:
#Disable Warning BC42356 ' suppress warning about no awaits in this method
    Async Function TestAsync() As Task
        Console.WriteLine("testing")
    End Function
#Enable Warning BC42356
You can disable and enable a comma-separated list of warnings too.

Further language design notes for this feature are here: https://roslyn.codeplex.com/discussions/543476

XML doc-comment improvements

Previously when you wrote references in your comments then you were basically on your own. Now the language has full support:
  • it validates paramref names
  • it properly handles crefs that are generics, operators, ...
  • it does colorizing, refactoring etc. in xml doc-comments
''' <summary>
''' Syncs <paramref name="md"/>, which is
''' a <see cref="DataObject(Of String).CloudMetadata"/>, to the cloud
''' </summary>
''' <param name="md">The local copy to sync</param>
Sub SyncFromClientToAzure(md As DataObject(Of String).CloudMetadata)
   ...
End Sub

Partial module and interface declarations

VB used to only allow Partial on classes and structures. Now it is allowed on modules and interfaces as well:
Partial Module DatabaseExtensions
   ' Extension methods from code generator
End Module

Partial Interface IBindable
   ' WPF-specific binding contract additions
End Interface
Partial types are great for anyone doing any kind of code generation because you can separate out the parts of the class that are generated by tooling, and the parts that are written by hand. Beyond that, partial interfaces are also handy when you want to reuse code files but separate out some parts of an interface that only makes sense on a particular platform.

'#Region' directives inside method bodies

'#Region' is now allowed within method bodies and can cross method bodies. For instance:
Function Range(min As Integer, max As Integer) As IEnumerable(Of Integer)
   If min > max Then Throw New ArgumentException
#Region "validation"
   Return Helper(min, max)
End Function

Private Iterator Function Helper(min As Integer, max As Integer) As IEnumerable(Of Integer)
#End Region
   For i = min To max
      Yield i
   Next
End Function

'Overloads' modifier inferred from 'Overrides' modifier

Previously, VB libraries had to write both modifiers Overrides Overloads to play nice with C# users. Now, 'Override' members are also implicitly 'Overloads'.

CObj allowed in attribute arguments

The following code used to give an error that CObj(...) isn't a constant. But it is, so we no longer give the error.
<DefaultValue(CObj(1))>
Previously, you had no way to specify the "object" overload of an attribute (like this one) that also took an Enum.

Declaration and consumption of ambiguous methods from unrelated interfaces

This is a subtle corner of the VB language. Consider the following code:
Interface ICustomer
  Sub GetDetails(x As Integer)
End Interface

Interface ITime
  Sub GetDetails(x As String)
End Interface

Interface IMock : Inherits ICustomer, ITime
  Overloads Sub GetDetails(x As Char)
End Interface

Interface IMock2 : Inherits ICustomer, ITime
End Interface
Previously it used to be illegal to call the method GetDetails in either "IMock" or "IMock2", on the grounds that you the caller might not know which "GetDetails" you meant to invoke. And VB also used to disallow you to declare an interface like "IMock" on the grounds that you couldn't call it anyway.

But C# did let you declare interfaces like these, leaving VB users unable to call them.

So now VB14 relaxes the rules. VB will let you declare these interfaces, and will let you invoke methods on them. It will use the normal rules of overload resolution to figure out which "GetDetails" is the most appropriate for a given callsite.

Full language design notes are here: https://roslyn.codeplex.com/discussions/570975
Nov 12, 2014 at 3:47 PM
Congratulations and thank you!
Nov 12, 2014 at 7:12 PM
lwischik wrote:

Multiline string literals

We now allow string literals that split over multiple lines.
Dim x = "hello
world"
This is cleaner and easier to use than the workaround you'd use before:
Dim x = <xml><![CDATA[Hello
World]]></xml>.Value
Having a means of declaring multi-line string literals would be nice, but allowing newlines within "normal" string literals would seem to imply that a misplaced quote mark could cause a huge amount of code to be misinterpreted as a string literal and worse, cause string literals to get misinterpreted as code. Such a thing could be disastrous with auto-formatting. For example:
Dim St As String = "What happens if the closing quote is omitted"
Dim St2 As String = ":   call George's cell phone"
By my understanding, if the first line includes its closing quote, then St2 would be set to a string literal ": call george's cell phone"; omitting the closing quote from the first line would turn the code into the equivalent of:
Dim St As String = "What happens if the closing quote is omitted" + VbCrLf + "    Dim St2 As String = ": call George's cell phone"
which would be perfectly legitmate code, but would get munged by auto-formatting so that fixing the quote would end up with the code being
Dim St As String = "What happens if the closing quote is omitted"
Dim St2 As String = ": Call George() 's cell phone"
Such problems could be avoided if multi-line strings needed to have special opening and closing markers. Situations where a marker might appear within a string could be handled by allowing the string-start sequence to specify an end sequence. Additionally, using a special string-start marker would make it possible to specify the type of newline character that should be included within the string, independent of what appears in the source code. IMHO, if a language regards CR, LF, and CRLF as synonyms, then sending a source file through a transport layer that changes line endings should not alter its semantics. Making line-ending distinctions in a source file be semantically relevant seems a recipe for disaster.
Nov 12, 2014 at 7:18 PM
lwischik wrote:

'#Region' directives inside method bodies

'#Region' is now allowed within method bodies and can cross method bodies. For instance:
Was any consideration given to the possibility of allowing regions to appear within a block of code, but requiring that each region must either be entirely inside or entirely outside each individual method, block of code, or other such structure?
So now VB14 relaxes the rules. VB will let you declare these interfaces, and will let you invoke methods on them. It will use the normal rules of overload resolution to figure out which "GetDetails" is the most appropriate for a given callsite.
Will it allow the fact that code is either reading or writing a property to be used as a means of disambiguating read-only and write-only properties?
Nov 12, 2014 at 7:48 PM
Edited Nov 12, 2014 at 7:52 PM
supercat wrote:
[Multiline string literals] allowing newlines within "normal" string literals would seem to imply that a misplaced quote mark could cause a huge amount of code to be misinterpreted as a string literal and worse, cause string literals to get misinterpreted as code. Such a thing could be disastrous with auto-formatting.
We shared your concerns when we first implemented the feature. But trying it out in practice, none of us have practically suffered. Supercat please install the VS2015 preview and see what you think, if your concerns are borne out in practice. Why hasn't it proved to be a problem for us? I think because
  1. when you start typing a string it auto-inserts the closing quotation mark so you typically don't end up toggling the rest of your file into a string
  2. even if the next chunk of your code does mistakenly get turned into a string, that doesn't trigger the auto-formatter.
  3. the syntax highlighting shows you what's happening so you fix the problem rather than triggering the auto-formatter yourself.
supercat wrote:
[#region flexibility] Was any consideration given to the possibility of allowing regions to appear within a block of code, but requiring that each region must either be entirely inside or entirely outside each individual method, block of code, or other such structure?
We acknowledge that #regions are a controversial topic. Nevertheless there are good use-cases which people like for having #region span the boundary between two methods, exactly as shown in the OP. I think the right approach here is: don't use regions that way if you don't like it (or write an analyzer to restrict it!)


supercat wrote:
[unrelated ambiguous methods in interfaces] Will it allow the fact that code is either reading or writing a property to be used as a means of disambiguating read-only and write-only properties?
No. That could get really hairy!
Nov 14, 2014 at 11:04 PM
lwischik wrote:
We shared your concerns when we first implemented the feature. But trying it out in practice, none of us have practically suffered. Supercat please install the VS2015 preview and see what you think, if your concerns are borne out in practice.
I'm using Windows 7, and the installer doesn't seem to like that. What I've seen of Windows 8 I've really disliked, so I've had little interest in upgrading until such time as Microsoft comes up with something better (Windows seems to alternate between good and bad versions).
Jan 14, 2015 at 9:06 PM
"The ?. operator"

YAY! This was one of the few things I really missed when moving from Obj-C. All those ternary Ifs( were driving me crazy, and I couldn't understand how this wasn't already in there. I guess the port to VB started biting the right person's behind :-)

So where does one write suggestions for the next version? I'm constantly frustrated by the lack of the true equivalent for #define, which makes it annoying to conditionally compile code based on the presence of another (optional) file. #const is limited to the source file it's in, which defeats the entire purpose.
Jan 21, 2015 at 5:44 PM
Since you're allowing year first date literals, would it be possible to allow relative dates? #now# or #today-1#? One of the things that has long been missing from .Net is a simple and easy way to get the build time. That's what the #now# would be for. #today+10# or #today-100# would be good for unit test.
Jan 21, 2015 at 7:02 PM
johnmoreno wrote:
Since you're allowing year first date literals, would it be possible to allow relative dates? #now# or #today-1#? One of the things that has long been missing from .Net is a simple and easy way to get the build time. That's what the #now# would be for. #today+10# or #today-100# would be good for unit test.
I would expect #today# or #now# to refer to the current time, not the build time, and that is easily accessible from the DateTime struct. To my knowledge the only way to extract the build date/time from a .NET assembly is to calculate it from the build/revision elements of the assembly version number, assuming that you're using wildcard notation to allow the compiler to embed those values. The build is the number of days from 2000-01-01 and the revision is half of the number of seconds from midnight.

My personal preference would be to not use either in unit testing since they introduce non-determinism.
Jan 21, 2015 at 10:22 PM
Halo_Four wrote:
I would expect #today# or #now# to refer to the current time, not the build time, and that is easily accessible from the DateTime struct.
The ## notation indicates that it is a literal (constant) value added at compile time. I don't think it would be confusing at all for that to include a calculated value, it's fundamentally no different than Const I as Integer = 1 + 2
To my knowledge the only way to extract the build date/time from a .NET assembly is to calculate it from the build/revision elements of the assembly version number, assuming that you're using wildcard notation to allow the compiler to embed those values. The build is the number of days from 2000-01-01 and the revision is half of the number of seconds from midnight.
There are various dates in the DLL's that can be used. Not 100% reliable. Using the build revision number relies upon something outside of the language AND allowing certain things to be set which you may not want to be set. Const compiledDate as Date = #today# doesn't rely upon anything except the compiler.