October 2009

Volume 24 Number 10

Basic Instincts - Collection and Array Initializers in Visual Basic 2010

By Adrian Spotty | October 2009

Some of the language changes that have taken place during the Microsoft Visual Studio (VS) 2010 product cycle have been aimed at simplifying tasks that were previously possible only through the use of a lot of boilerplate code. This approach in Visual Studio 2010 improves the coding experience, making you more productive. One of the features new in Visual Studio for the 2010 release is Collection Initializers. This feature was available in C# in 2008 and as part of the general parity philosophy Microsoft has been adopting. Some of the changes made to Visual Basic 2010 may be less immediately obvious, and this article will identify some of the less obvious implementation details.

Collections and arrays are very common constructs in any modern application, and these collections often have to be initialized. Prior to the 2010 releases, Visual Basic was able to handle basic array initializers with some limitations, but collections were not able to be initialized in a similar one-line statement, which often resulted in boilerplate code like the following:

Dim x As New List(Of String)
        x.Add("Item1")
        x.Add("Item2")

Or it required creating a constructor for the class, using something like the following:

Dim x as new list(of string)({"Item1","Item2"})

Neither of these approaches were particularly elegant solutions.

While both worked, they involved writing additional code to initialize the collection. Although this code is often straightforward boilerplate code, it can bloat the source code, which can lead to associated maintenance costs.

If you look through almost any code that uses collections, you will see similar boilerplate code. In the preceding example, we are adding only two string elements and are using repetitive calls to the add method for each of the strings. If we used a custom "user-defined" collection or a collection of "user-defined" types, the standard code would increase to do the same task of initializing the collection.

New Syntax

With Visual Studio 2010, we are now able to handle this collection initialization task in a much more concise form, reducing down the multiple lines of code and repeated Add method calls into a single statement:

Dim x As New List(Of String) From {"Item1", "Item2"}

This is a simple example using the framework's generic list collection types to provide a type-safe collection. These collection types are now more commonly used than handcrafted collection types, which were used in the past for implementing type-safe collections.

The syntax allows for the collection members to be specified as a list of items contained within the braces { }, and each item is separated by commas using the "FROM" keyword to determine the initial member list.The preceding example works fine for a list type, but for more complex collection types such as dictionaries, where there may be a key-value pair provided for each member, we require an additional set of braces around each key-value pair, as follows:

Dim x As New Dictionary(Of Integer, String)
          From {{1, "Item1"}, {2, "Item2"}}

The syntax is clean and consistent. Each key-value pair is determined by the nested {}, and these arguments are used when a call to the collection type (Dictionary) Add method is made. So the preceding lines would equate to:

Dim x As New Dictionary(Of Integer, String)
        x.Add(1, "Item1")
        x.Add(2, "Item2")

Implementation and Usage in Other Collection Types

The preceding examples show how collections initialize usage for typical framework types. However, you may have implemented your own collection types or may want to use some types where you are not immediately able to use the initializer because they lack an Add method (examples include Stack, Queue).

To allow us to use the Collection Initializer feature in both these scenarios, it is important to understand a little about what is going on under the covers and how this feature is implemented. This understanding will then allow us to expand its use beyond the simple list and dictionary examples.

To use Collection Initializer syntax, we require two items to be true. The type must:

  1. Implement the IEnumerable pattern, which can be either the IEnumerable interface or simply have a GetEnumerator method. This may be referred to as "duck" typing: if it looks like a duck and quacks like a duck, then it probably is a duck.
    In our case, if it contains a GetEnumerator method with an appropriate signature, then it is probably implementing similar behavior for the IEnumerable interface. This duck-typing behavior is already used to allow types to be used with the For Each construct.
  2. Contain at least one accessible Add method with one parameter. This can be either an instance or an extension method.

If your collection class meets both these conditions, you can use the Collection Initializer syntax. 

For Collection Initializers, we do not actually use the IEnumerable method for initializing the collection, but we do use it as a hint to determine that this type is, in fact, a collection type. If it implements IEnumerable or the IEnumerable pattern, then there is a high probability that you have a collection, although it's not guaranteed.

The Add method is called for each item in the Initializer List to add items to the collection. The fact that an Add method is present does not necessarily mean it has to implement functionality to Add items to the collection. But calls to an Add method with the arguments transposed from the "Initialize list" will be made.

To illustrate this point, the following code sample in Figure 1 is valid and can use the Collection Initializer syntax, but in fact doesn't add any items to a collection.

Other Framework Classes—Use with Extension Methods

Some of the framework collection types don't meet both these requirements. Stacks and queues implement methods such as Pop and Push rather than Add. To allow you to use the concise Collection Initializer feature for these types, you can leverage the power of extension methods. It is simple to create an Add Extension method for these types, which then will permit you to initialize the methods with simple syntax.

Imports System.Runtime.CompilerServices

Module Module1
    Sub Main()
        Dim st1 As New Stack(Of Integer) From {1, 2, 3}
    End Sub
End Module

Module Extensions
    <Extension()> Sub Add(Of t)(ByVal x As Stack(Of t), ByVal y As t)
        x.Push(y)
    End Sub
End Module

When using extension methods, it is generally good practice to extend only the types that you have control over. Liberal use of extension methods can lead to conflicts or changes in behavior if the types are upgraded, so use them with caution.

Use with Implied Line Continuation

Visual Studio 2010 also contains another much-awaited feature: line continuation. This feature allows you to eliminate the pesky _ characters. Implicit line continuation is permitted for items in the "Initialize List", so it is possible to put each of these items on their own lines for clarity.

Dim st1 As New Stack(Of Integer) From {1,
                                       2,
                                       3}

Figure 1 Collection Initializer Syntax

Module Module1
    Sub Main()
        Dim NC As New TestNonCollectionClass From {1, 2, 3}
    End Sub
End Module

Class TestNonCollectionClass
    Public Function GetEnumerator() As System.Collections.IEnumerator
        'Does Nothing
    End Function

    Public Sub Add(ByVal x As Integer)
        If x = 1 Then
            Console.WriteLine("Add Item" & x.ToString)
        End If
    End Sub
End Class

This allows you to produce clean code that is simple to read, doesn't contain extra _ characters and avoids repetition. You can use it for your own collections and those already in the framework. The editor's IntelliSense support allows "FROM" support for all types that follow the preceding two rules, which means it will work for your collection types. If you have existing code that utilizes the _ character and prefer to remain consistent with old syntax, this choice is still supported.

Exception-Handling Behavior

There are a few interesting things to point out when calling the Add method for each of the "Initializer List" members.

For example,  when a call to the Add method with the provided argument from the Initialize List results in an exception, the list will not be initialized with any members. What this means in reality is that the collection is initialized with all members or its not initialized at all. 

This means that if your list has three items and the third one when called using the Add method results in an exception, you get an exception thrown and an uninitialized collection. The followingexample intentionally generates an exception to demonstrate this exact scenario and results in "Blank" being written to the console. However, changing the Initializer List to remove the value three will result in "Initialized:Element Count 2," as shown in Figure 2.

Figure 2 Changing the Initializer List

Imports System.Runtime.CompilerServices

Module Module1
    Sub Main()
        Dim l1 As Stack(Of Integer)
        Try
            l1 = New Stack(Of Integer) From {1, 2}
        Catch ex As Exception
        End Try
        If l1 Is Nothing Then
            Console.WriteLine("Blank")
        Else
            Console.WriteLine("Initialized - Element Count:" & l1.Count)
        End If
    End Sub
End Module

Module Extensions
    <Extension()> Sub Add(Of t)(ByVal x As Stack(Of t), ByVal y As t)
        If y.ToString = "3" Then
            Throw New Exception("Intentional Exception")
        Else
            x.Push(y)
        End If
    End Sub
End Module

If this didn't happen, then it would require crafting additional code to determine whether no initialization exceptions occurred and what state the collection was in.

Using Extension Methods to Shorten Syntax

The standard syntax for initializing a collection of complex objects results in having to repeat "New <type>" for each of the items in the Initializer List.

Module Module1
    Sub Main()
        Dim CustomerList As New List(Of Customer)
        From {New Customer With {.Name = "Spotty", .Age = 39},
              New Customer With {.Name = "Karen", .Age = 37}}
    End Sub
End Module

Class Customer
    Public Property Name As String = ""
    Public Property Age As Integer = 0
End Class

The syntax requires that each item in the Initializer List is instantiated. Through the use of extension methods, it is possible to shorten this syntax still further. The functionality relies on the fact that the collection type supports the IEnumerable interface and also has an Add method, which will result in the enclosed Initializer List items being mapped to the add method parameters, as shown in Figure 3.

Figure 3 Use of Extension Methods

Imports System.Runtime.CompilerServices

Module Module1
    Sub Main()
        'Shortend Syntax through the use of extension methods
        Dim CustomerList As New List(Of Customer) 
          From {{"Spotty", 39}, {"Karen", 37}}

    End Sub
End Module

Module Extension
    <Extension()> Public Sub add(ByVal x As List(Of Customer), _
      ByVal Name As String, ByVal Age As Integer)
        x.add(New Customer With {.Name = Name, .Age = Age})
    End Sub
End Module

Class Customer
    Public Property Name As String = ""
    Public Property Age As Integer = 0
End Class

Add Method Overloading

The Add method condition is critical to this feature and can be overloaded. Each method will call its own overload based upon the calling arguments. There is no magic here and the Add method overload resolution works just as overload resolution works for any method.

This again is best demonstrated by a simple example. In Figure 4, we have overloads for different data types—we will call the one appropriate for the calling argument.

Why Does the Syntax Use 'FROM'  and not '='?

A common question asked of the product team for this feature is the use of the "FROM" keyword, because the array initializers already are using the "=" for assignment.

In Visual Basic, it is possible to instantiate a collection class in three different ways:

Dim x1 As New List(Of Integer)
        Dim x2 = New List(Of Integer)
        Dim x3 As List(Of Integer) = New List(Of Integer)

Add to this the fact that two of the syntaxes for instantiating a collection already include an = character in the syntax. Using an additional = character would result in confusion in syntax between that of assignment of a new instance of the collection type and initialization of members to the collection.

So using a keyword rather than a = character to determine the result of a Collection Initializer action avoids this issue and allows all existing syntax conventions to declare and initialize collection types. Yet the way to determine the Initializer List remains familiar to the existing array initializer syntax.

The FROM keyword was chosen after much discussion. To those familiar with LINQ queries, it might seem a strange choice as there is already a FROM keyword used in LINQ. It may appear that this choice could lead to potential ambiguities when used within LINQ queries but as we will see, this is not the case. 

Doesn't 'FROM' Clash with 'FROM' in Queries?

Even in queries that contain Collection Initializers, there is no ambiguity of the keyword. This can be demonstrated by the following code:

Module Module1
    Sub Main()
        'The first from is the query,
        'the second is the collection initializer.
        'The parser can always successfully identify the difference
        Dim x = From i In New List(Of Integer) From {1, 2, 3, 4}
                Where i <= 3
                Select i
        For Each i In x
            Console.WriteLine(i)
        Next
        Stop
    End Sub
End Module

Limitations Using Collection Initalizer with Object Initializer

For the 2008 product, the Visual Basic team implemented object initializers that enabled the developer to initialize object fields/properties when they were instantiating an object instance:

Dim x As New List(Of Integer) With {.Capacity = 10}

However, it is not possible to initialize both the object and the collection on the same declaration, so the following will produce a syntax error:

Dim x as New List(Of Integer) from {1,2,3} With {.Capacity = 10}

There are some other less obvious changes that occurred in 2010 relating to arrays that may not be immediately noticeable but could have impact on code using arrays.

Figure 4 Overloads for Different Data Types

Imports System.Runtime.CompilerServices

Module Module1
    Sub Main()
        Dim l1 As New Stack(Of Integer) From {1, 2.2, "3"}
        Console.WriteLine("Element Count:" & l1.Count)
        Stop
    End Sub
End Module

Module Extensions
    <Extension()> Sub Add(ByVal X1 As Stack(Of Integer), _
      ByVal x2 As Integer)
        Console.WriteLine("Integer Add")
        X1.Push(x2)
    End Sub
    <Extension()> Sub Add(ByVal X1 As Stack(Of Integer), _
      ByVal x2 As Double)
        Console.WriteLine("Double Add")
        X1.Push(CInt(x2))
    End Sub
    <Extension()> Sub Add(ByVal X1 As Stack(Of Integer), _
      ByVal x2 As String)
        Console.WriteLine("String Add")
        X1.Push(CInt(x2))
    End Sub
End Module

Array Initializer Changes

Although prior to the 2010 version it was possible to initialize simple arrays, there were still some limitations. One of the recent changes allows array types to be initialized and inferred in more concise ways. 

Previously, to identify an item in Visual Basic as an array required you to specify a set of parentheses on the identifier:

Dim a() As Integer = {1, 2, 3}

Now these parentheses are not required and the type will be inferred as an array type with Option Infer On, resulting in a more concise syntax:

Dim a = {1, 2, 3}

Array Type Inference

In 2008, the type-inference feature was implemented, which enabled data types to be inferred from their assignment values. This worked for single objects declared within method bodies, but arrays did not infer a type and therefore were always arrays of Objects.

Sub Main()
        Dim a() = {1, 2, 3}
          'Type would previously result in Object Array prior to 2010
    End Sub

This has been improved, and now array types are inferred, as follows:

Sub Main()
        Dim a = {1, 2, 3} 'Type now infers Integer Array
    End Sub

The inferred type of the array is determined by the dominant type functionality added in 2008. This type inference still works only for code declared within method bodies, so fields will still be object arrays unless specifically typed.

It is possible to initialize multiple array dimensions, but all the nested pairs must contain the same number of members. So two-dimension examples will work.

The following two examples will both create a two-dimensional integer array:

Dim TwoDimension1(,) = {{1, 2}, {3, 4}}
        Dim TwoDimension2 = {{1, 2}, {3, 4}}

For single-dimension arrays, the syntax is very similar to previous code, and all existing code will continue to work with a few exceptions.

Dim a() = {1, 2, 3}

In the preceding statement, the array type will be inferred from the initializer list. This will result in an integer array being inferred. In the past, these would have simply been Object arrays as we would not have inferred any array type from the Initializer list. If you are using any code that checks the type, uses reflection or contains multiple overloads including object array types, this may now produce different results. Examples of this may include late-bound methods.

Multidimensional Arrays

For you to initialize a multidimensional array, the dimensions must match the item count within the Initializer List. If these are not the same and all nested items in the initialize list do not have a consistent item count, then a syntax error will occur:

Dim TwoDimension2 = {{1, 2}, {3, 4}}
  'Valid 2 dimension integer(,) array inferred
Dim TwoDimension2Invalid(,) = {{1}, {3}}
  'Invalid – dimension and element count mismatch
Dim TwoDimension2Invalid1(,) = {{1, 2}, {3}}
  'Invalid:element count not consistent in Initializer List

This means that using the similar syntax as before, it is possible to initialize standard fixed dimensional arrays.

Jagged Array Initialization

However, this means that for a jagged array (an array of arrays), this syntax won't work as is. To allow for jagged array initializers, it is required that you wrap each of the member lists in parentheses:

Sub Main()
        'Incorrect Jagged Array syntax
        Dim JaggedDimensionIncorrect()() = {{1,2},{3,4,5}}

        'Correct Jagged Array syntax
        Dim JaggedDimension1()() = {({1,2}),({3,4,5})}
    End Sub

The preceding example results in the following:

  • JaggedDimension1(0) containing Integer array with elements 1,2
  • JaggedDimension1(1) containing Integer array with elements 3,4,5

The array type-inference will work for the nested array types. Here is a slightly more complex example:

Dim JaggedDimension1() = {({1, 2}), ({3.1, 4.2, 5.3})}

This will result in JaggedDimension1 being inferred as Object() and the members being of type Integer() and Double().

Array Inference and Expected Behavior

So you look on the Initializer List as an inferred array. Don't ! The Initializer List is not a concrete type as you might think; it is a syntactical representation of a list of members, which becomes a specific type depending upon the context it is used in.

The following code sample demonstrates that we can use the {, , ,} syntax to:

1. Infer and initialize an integer array:

Dim a = {1, 2, 3}  'Infers Integer Array

This works as no target type is specified and it infers its type based upon the dominant type of the Initializer List members.

2. Initialize an array of different type:

Dim b As Single() = {1, 2, 3}
  'Will convert each integer value into single

This will work as we will initialize a single array with values one, two and three. The Initializer List has no intrinsic type by itself.

But the following is expected to fail:

Dim a = {1, 2, 3}  'Infers Integer()
        Dim c As Single() =
          a 'No conversion between Single() and Integer()

This may seem a little strange but variable a is inferred as an integer array and initialized with values one, two and three. Variable c is declared as a Single array. However, there is no conversion between a Single array and an Integer array, which will cause the syntax error.

Standalone Array Initializer Usage

The array Initializer List also can be used in a standalone context, in which case, it will have not have a specified target type and will work as an array of the dominant type of the members of the initialize list.

This allows some interesting scenarios not previously available. An example could be a method that takes in an array of items that you want to use to initialize a data structure. Where the method is called determines different default values, which are used as call arguments, as shown in Figure 5.

In this case, we create an array local for each call, simply to pass an array type to the method. With the new standalone usage feature, it is possible to avoid having to create unnecessary locals for this type of scenario and you can simply use the standalone Initializer List to pass array types.

The Initializer List in the standalone usage scenario snaps to an inferred array type. So in a standalone usage scenario, the initialize list can be thought of as an array literal. This saves having to declare items that are only going to be used in limited instances, such as individual method calls.

Figure 5 Different Default Values Used as Call Arguments

Module Module1

    Sub InitializeMethod(ByVal x As String())
        '....
    End Sub

    Sub Main()
        Dim i As Integer = 1

        Select Case I
            Case 1
                Dim Array1 As String() = {"Item1", "Item2"}
                InitializeMethod(Array1)
            Case 2
                Dim Array1 As String() = {"Item11", "Item12"}
                InitializeMethod(Array1)
        End Select
    End Sub
End Module

Use with Other 2010 Features

One of the features of the 2010 version of the product is multi-targeting. Although this feature existed in the 2008 version, some improvements have been made that allow many of the language features in the 2010 version of the product to be used on down-targeted versions (2.0, 3.0 and 3.5 targets). Therefore, you can use this improved functionality for collection and array initializers for your existing applications if you are using the 2010 version of the product. This enables you to simplify your existing source code and take advantage of the new language functionality.

So when you use this new functionality, is there potential for breaking existing code? The answer to this is yes, there is some potential for backward-compatibility breaks, but this is limited to a few scenarios where the benefits outweigh the original breaking change and the existing functionality can be retained easily:

Dim x1() = {1, 2, 3}

In previous versions, the preceding line of code would have resulted in an Object array, but with Visual Studio 2010, this will be type-inferred to the dominant type of the elements—in this case Integer(). This will be the case regardless of whether you are targeting 4.0 or down-targeting to an earlier version. If you have code that is expecting a specific type of Object array, then this code may fail. It can easily be corrected by explicitly specifying the target type:

Dim x1() As Object = {1, 2, 3}

The exception to this behavior is if there is no dominant type for the elements:

Dim x1() = {1, 2.2, "test"}

This will retain the earlier behavior, resulting in x1 being an object array.

Easy Application

Collection Initializers are a great addition to the language. They allow for a much more concise syntax in order to initialize both framework and user-defined collection types. With minimal changes, this syntax can be applied to existing code easily.

The functionality is ideally suited to be able to use other language features such as extension methods, enabling the reduction of the use of syntax for any collection types. This reduction in boilerplate code previously used makes the code smaller and easier to maintain.

The array initializer behavior has changed slightly, resulting in implementing array type inference and improved functionality, allowing standalone usage of arrays and improved ability to initialize multi-dimensional and jagged arrays over previous versions.

Virtually every application uses arrays or collections and this feature can be used in almost any project.


Adrian Spotty Bowles has developed using every version of Visual Basic and has managed to find his way to Redmond, Wash., where he works on the Visual Basic product team as a software design engineer tester focused on the Visual Basic compiler. During the Visual Basic 2008 release, he worked on many of the language features, including Extension Methods.You can reach Bowles at Abowles@microsoft.com.