What's new for Visual Basic

This topic lists key feature names for each version of Visual Basic, with detailed descriptions of the new and enhanced features in the lastest version of the language.

Current Version

Visual Basic / Visual Studio .NET 2017
For new features, see Visual Basic 2017

Previous versions

Visual Basic / Visual Studio .NET 2015
For new features, see Visual Basic 14

Visual Basic / Visual Studio .NET 2013
Technology previews of the .NET Compiler Platform (“Roslyn”)

Visual Basic / Visual Studio .NET 2012
Async and await keywords, iterators, caller info attributes

Visual Basic, Visual Studio .NET 2010
Auto-implemented properties, collection initializers, implicit line continuation, dynamic, generic co/contra variance, global namespace access

Visual Basic / Visual Studio .NET 2008
Language Integrated Query (LINQ), XML literals, local type inference, object initializers, anonymous types, extension methods, local var type inference, lambda expressions, if operator, partial methods, nullable value types

Visual Basic / Visual Studio .NET 2005
The My type and helper types (access to app, computer, files system, network)

Visual Basic / Visual Studio .NET 2003
Bit-shift operators, loop variable declaration

Visual Basic / Visual Studio .NET 2002
The first release of Visual Basic .NET

Visual Basic 2017

Tuples

Tuples are a lightweight data structure that most commonly is used to return multiple values from a single method call. Ordinarily, to return multiple values from a method, you have to do one of the following:

  • Define a custom type (a Class or a Structure). This is a heavyweight solution.

  • Define one or more ByRef parameters, in addition to returning a value from the method.

Visual Basic's support for tuples lets you quickly define a tuple, optionally assign semantic names to its values, and quickly retrieve its values. The following example wraps a call to the TryParse method and returns a tuple.

Imports System.Globalization

Public Module NumericLibrary
    Public Function ParseInteger(value As String) As (Success As Boolean, Number As Int32)
        Dim number As Integer
        Return (Int32.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, number), number)
    End Function
End Module

You can then call the method and handle the returned tuple with code like the following.

Dim numericString As String = "123,456"
Dim result = ParseInteger(numericString)
Console.WriteLine($"{IIf(result.Success, $"Success: {result.Number:N0}", "Failure")}")
Console.ReadLine()
'      Output: Success: 123,456

Binary literals and digit separators

You can define a binary literal by using the prefix &B or &b. In addition, you can use the underscore character, _, as a digit separator to enhance readability. The following example uses both features to assign a Byte value and to display it as a decimal, hexadecimal, and binary number.

Dim value As Byte = &B0110_1110
Console.WriteLine($"{NameOf(value)}  = {value} (hex: 0x{value:X2}) " +
                  $"(binary: {Convert.ToString(value, 2)})")
' The example displays the following output:
'      value  = 110 (hex: 0x6E) (binary: 1101110)      

For more information, see the "Literal assignments" section of the Byte, Integer, Long, Short, SByte, UInteger, ULong, and UShort data types.

Support for C# reference return values

Starting with C# 7, C# supports reference return values. That is, when the calling method receives a value returned by reference, it can change the value of the reference. Visual Basic does not allow you to author methods with reference return values, but it does allow you to consume and modify the reference return values.

For example, the following Sentence class written in C# includes a FindNext method that finds the next word in a sentence that begins with a specified substring. The string is returned as a reference return value, and a Boolean variable passed by reference to the method indicates whether the search was successful. This means that the caller can not only read the returned value; he or she can also modify it, and that modification is reflected in the Sentence class.

 using System;
 
public class Sentence
{
   private string[] words;
   private int currentSearchPointer;

  public Sentence(string sentence)
  {
      words = sentence.Split(' ');
      currentSearchPointer = -1;
  }

  public ref string FindNext(string startWithString, ref bool found)
  {
      for (int count = currentSearchPointer + 1; count < words.Length; count++)
      {
         if (words[count].StartsWith(startWithString))
         {
            currentSearchPointer = count;
            found = true;
            return ref words[currentSearchPointer];
         }
      }
      currentSearchPointer = -1;
      found = false;
      return ref words[0];
   }
  
   public string GetSentence()
   {
      String stringToReturn = null;
      foreach (var word in words)
         stringToReturn += $"{word} ";
      
      return stringToReturn.Trim();    
   }
}

In its simplest form, you can modify the word found in the sentence by using code like the following. Note that you are not assigning a value to the method, but rather to the expression that the method returns, which is the reference return value.

Dim sentence As New Sentence("A time to see the world is now.")
Dim found = False
sentence.FindNext("A", found) = "A good" 
Console.WriteLine(sentence.GetSentence()) 
' The example displays the following output:
'      A good time to see the world is now.

A problem with this code, though, is that if a match is not found, the method returns the first word. Since the example does not examine the value of the Boolean argument to determine whether a match is found, it modifies the first word if there is no match. The following example corrects this by replacing the first word with itself if there is no match.

Dim sentence As New Sentence("A time to see the world is now.")
Dim found = False
sentence.FindNext("A", found) = IIf(found, "A good", sentence.FindNext("B", found)) 
Console.WriteLine(sentence.GetSentence()) 
' The example displays the following output:
'      A good time to see the world is now.

A better solution is to use a helper method to which the reference return value is passed by reference. The helper method can then modify the argument passed to it by reference. The following example does that.

Module Example
   Public Sub Main()
      Dim sentence As New Sentence("A time to see the world is now.")
      Dim found = False
      Dim returns = RefHelper(sentence.FindNext("A", found), "A good", found) 
      Console.WriteLine(sentence.GetSentence()) 
   End Sub
   
   Private Function RefHelper(ByRef stringFound As String, replacement As String, success As Boolean) _ 
                    As (originalString As String, found As Boolean) 
      Dim originalString = stringFound
      If found Then stringFound = replacement
      Return (originalString, found)   
   End Function
End Module
' The example displays the following output:
'      A good time to see the world is now.

For more information, see Reference Return Values.

Visual Basic 14

Nameof
You can get the unqualified string name of a type or member for use in an error message without hard coding a string. This allows your code to remain correct when refactoring. This feature is also useful for hooking up model-view-controller MVC links and firing property changed events.

String Interpolation
You can use string interpolation expressions to construct strings. An interpolated string expression looks like a template string that contains expressions. An interpolated string is easier to understand with respect to arguments than Composite Formatting.

Null-conditional Member Access and Indexing
You can test for null in a very light syntactic way before performing a member access (?.) or index (?[]) operation. These operators help you write less code to handle null checks, especially for descending into data structures. If the left operand or object reference is null, the operations returns null.

Multi-line String Literals
String literals can contain newline sequences. You no longer need the old work around of using <xml><![CDATA[...text with newlines...]]></xml>.Value

Comments
You can put comments after implicit line continuations, inside initializer expressions, and amongst LINQ expression terms.

Smarter Fully-qualified Name Resolution
Given code such as Threading.Thread.Sleep(1000), Visual Basic used to look up the namespace "Threading", discover it was ambiguous between System.Threading and System.Windows.Threading, and then report an error. Visual Basic now considers both possible namespaces together. If you show the completion list, the Visual Studio editor lists members from both types in the completion list.

Year-first Date Literals
You can have date literals in yyyy-mm-dd format, #2015-03-17 16:10 PM#.

Readonly Interface Properties
You can implement readonly interface properties using a readwrite property. The interface guarantees minimum functionality, and it does not stop an implementing class from allowing the property to be set.

TypeOf <expr> IsNot <type>
For more readability of your code, you can now use TypeOf with IsNot.

#Disable Warning <ID> and #Enable Warning <ID>
You can disable and enable specific warnings for regions within a source file.

XML Doc-comment Improvements
When writing doc comments, you get smart editor and build support for validating parameter names, proper handling of crefs (generics, operators, etc.), colorizing, and refactoring.

Partial Module and Interface Definitions
In addition to classes and structs, you can declare partial modules and interfaces.

#Region Directives inside Method Bodies
You can put #Region…#End Region delimiters anywhere in a file, inside functions, and even spanning across function bodies.

Overrides Definitions are Implicitly Overloads
If you add the Overrides modifier to a definition, the compiler implicitly adds Overloads so that you can type less code in common cases.

CObj Allowed in Attributes Arguments
The compiler used to give an error that CObj(…) was not a constant when used in attribute constructions.

Declaring and Consuming Ambiguous Methods from Different Interfaces
Previously the following code yielded errors that prevented you from declaring IMock or from calling GetDetails (if these had been declared in C#):

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  

Now the compiler will use normal overload resolution rules to choose the most appropriate GetDetails to call, and you can declare interface relationships in Visual Basic like those shown in the sample.

See also

What's New in Visual Studio 2017