Collection Initializers in Visual Basic 10

Collection Initializers in Visual Basic 10

  • Comments 2

Visual Basic 10 (in Visual Studio 2010) is getting a new feature called Collection Initializers which contains the same functionality as the C# 3 feature but it takes it one step further. Let me show you what I mean.

Visual Basic 9 (in Visual Studio 2008) already has Object Initializers which come in handy when you want to quickly populate some public properties on an object. For instance, say I have a class called Student that has Name (string), Age (integer) and Score (integer) public properties. In a single line of code I can initialize a new object and specify only the properties I want to set as long as the Student class has a default constructor:

Dim s As New Student With {.Age = 29, .Name = "Beth"}

Object Initializers are also used often in LINQ statements when you want to project selected values into your own named objects:

'produces a collection of Student objects from a LINQ query. IEnumerable(Of Student):
Dim myStudents = From contact In ContactList _
                 Select New Student _
                        With {.Age = contact.Age, _
                              .Name = contact.Name}

However if you want to create a Generic.List(Of Student) in Visual Basic 9 you can’t do it in one line. A trick is to use Array Initializers to populate an array with objects using Object Initializers and then call ToList to create the generic List:

'Use Array & Object Initializers to create an array of Students
Dim s As Student() = {New Student With {.Age = 10, .Name = "Beth"}, _
                      New Student With {.Age = 11, .Name = "Alan"}, _
                      New Student With {.Age = 12, .Name = "Robert"}, _
                      New Student With {.Age = 13, .Name = "Jenn"}}

'Convert the array to a Generic.List
Dim studentList As List(Of Student) = s.ToList()

But this is really less than ideal because of the casting. What we really want to do is have the objects automatically added to the list via the List.Add method. Collection Initalizers do just that. They are basically just a shorthand syntax for calling the Add method explicitly on the collection.

Collection Initializers in C# 3  look like this:

var nums = new List<int> { 1, 2, 3, 4, 5 };
var studentList = new List<Student> {new Student {Age = 10, Name = "Beth"}, 
                                     new Student {Age = 11, Name = "Alan"}, 
                                     new Student {Age = 12, Name = "Robert"}, 
                                     new Student {Age = 13, Name = "Jenn"}};

Visual Basic 10 now also provides this feature using the From keyword (note, VB 10 does not require underscores and provides a new syntax coloring):

Dim nums As New List(Of Integer) From {1, 2, 3, 4, 5}

Dim studentList As New List(Of Student) From
    {New Student With {.Age = 10, .Name = "Beth"},
     New Student With {.Age = 11, .Name = "Alan"},
     New Student With {.Age = 12, .Name = "Robert"},
     New Student With {.Age = 13, .Name = "Jenn"}}

What’s happening in these cases is the compilers are looking for the existence of an Add method on the collection and calling that for you. However in VB 10 they took it a step further. In C# 3 the Add method must be an instance method on the collection but in VB 10 you can provide your own Extension Method and provide your own functionality. For instance, we can make the calling syntax even simpler by writing an extension method that does the object initialization as well:

<Runtime.CompilerServices.Extension()>
Sub Add(ByVal list As List(Of Student),
        ByVal age As Integer,
        ByVal name As String)

    list.Add(New Student With {.Age = age, .Name = name})
End Sub

Now we can initialize our Generic.List(Of Student) like so:

Dim studentList As New List(Of Student) From
    {{10, "Beth"}, {11, "Alan"}, {12, "Robert"}, {13, "Jenn"}}

You can also omit the From keyword and pass the collection initialization directly to the constructor of the Generic.List because the List has an overloaded constructor that accepts an IEnumerable:

Dim nums As New List(Of Integer)({1, 2, 3, 4, 5})

So the rule of thumb is, use From to call the Add method or you could pass directly to call the constructor if it accepts an IEnumerable. Passing values this way directly isn't as efficient though because an array is created first and then passed to the constructor (which then loops through and calls Add internally). Using the From keyword calls the Add method directly for you.

Pick up a copy of the Visual Studio 2010 Beta 1 and try it out for yourself.

Enjoy!

Leave a Comment
  • Please add 5 and 4 and type the answer here:
  • Post
  • Beth Massi (Microsoft) y en el equipo de Visual Basic, nos muestra una de las nuevas caracter&iacute;sticas

  • Hi Beth,

    I am trying to pass a ready only property to collection initiatizer in VB.Net.But I keep on getting this error "Property 'variables' is 'ReadOnly'".

    Following is the code snippet which throws the error:

    Dim body As New Sequence() With {.Variables = {numbers}, .Activities = {New WriteLine With {.Text = "Initial collection:"}, New PrintCollection(Of Integer)() With {.Collection = numbers}, New WriteLine With {.Text = "-----------------"}, New WriteLine With {.Text = "Clearing the collection"}, New ClearCollection(Of Integer)() With {.Collection = numbers}, New PrintCollection(Of Integer)() With {.Collection = numbers}, New WriteLine With {.Text = "-----------------"}, New AddToCollection(Of Integer)() With {.Collection = numbers, .Item = random.Next(1, 10)}, New AddToCollection(Of Integer)() With {.Collection = numbers, .Item = random.Next(1, 10)}, New AddToCollection(Of Integer)() With {.Collection = numbers, .Item = random.Next(1, 10)}, New WriteLine With {.Text = "Collection with three random numbers:"}, New PrintCollection(Of Integer)() With {.Collection = numbers}, New WriteLine With {.Text = "-----------------"}, New [If]() With {.Condition = New ExistsInCollection(Of Integer)() With {.Collection = numbers, .Item = 5}, .Then = New RemoveFromCollection(Of Integer)() With {.Collection = numbers, .Item = 5}}, New WriteLine With {.Text = "Collection without a 5:"}, New PrintCollection(Of Integer)() With {.Collection = numbers}}}

    In the above code snippets, Variables and Activities are read-only properties of Sequence Class.

    Thanks!

Page 1 of 1 (2 items)