Friday, June 22, 2007

Will Visual Basic 9.0 Have Collection Initializers?

For reasons unknown, all mention of collection initializers for Visual Basic 9.0 has disappeared. The disappearance seems to have occurred without comment from the VB team or VB users.

The September 2005 "Overview of Visual Basic 9.0" .doc file covered the topic briefly, but the current HTML Overview of Visual Basic 9.0 version of February 2007 discusses only object initializers and array initializers. VS 2008 Beta 1 online help has a "connection initializers (C#)" topic but no corresponding VB topic.

The 2005 Overview's section 1.3 "Object and Collection Initializers" says this about collection initializers:

As we have seen, object initializers are also convenient for creating collections of complex objects. Any collection that supports an Add method can be initialized using a collection initializer expression. For instance, given the declaration for cities as the partial class,

Partial Class City
  Public Property Name As String
  Public Property Country As String
  Public Property Longitude As Float 
  Public Property Latitude As Float
End Class

we can create a List(Of City) of capital cities of our example countries as follows:

Dim Capitals = New List(Of City){ _
  { .Name = "Antanarivo", _
    .Country = "Madagascar", _
    .Longitude = 47.4, _
    .Lattitude = -18.6 }, _
  { .Name = "Belmopan", _
    .Country = "Belize", _
    .Longitude = -88.5, _
    .Latitude = 17.1 }, _
  { .Name = "Monaco", _
    .Country = "Monaco", _
    .Longtitude = 7.2, _
    .Latitude = 43.7 }, _
  { .Country = "Palau",
    .Name = "Koror", _
    .Longitude = 135, _
    .Latitude = 8 } _
}

This example also uses nested object initial[iz]ers, where the constructors of the nested initializers are inferred from the context. In this case, each nested initializer is precisely equivalent to the full form New City{…}.

Note: The preceding excerpt also appears in the session of the same name made to the XML 2005 Conference & Exhibition held in Atlanta November 14-18, 2005. I doubt if that code worked in any VB 9.0 preview.

Neither the class nor the sample collection initializer will compile. It's one thing to change the syntax for a new feature, but another to use a defective class declaration in sample code.

The authors of the original version, Erick Meijer, Amanda Silver, and Paul Vick, imported and modified the class code and modified the List(Of City) code to utilize the current New Type With syntax in "Object and Array Initializers" of the February 2007 version:

As we have seen, object initializers are also convenient for creating collections of complex objects. Arrays can be initialized and the element types inferred by using an array initializer expression. For instance, given the declaration for cities as the class,

Partial Class City
  Public Property Name As String
  Public Property Country As String
  Public Property Longitude As Long 
  Public Property Latitude As Long
End Class

we can create an array of capital cities for our example countries as follows:

Dim Capitals = { _
  New City With { _
    .Name = "Antanarivo", _
    .Country = "Madagascar", _
    .Longitude = 47.4, _
    .Lattitude = -18.6 }, _
  New City With { _
    .Name = "Belmopan", _
    .Country = "Belize", _
    .Longitude = -88.5, _
    .Latitude = 17.1 }, _
  New City With { _
    .Name = "Monaco", _
    .Country = "Monaco", _
    .Longtitude = 7.2, _
    .Latitude = 43.7 }, _
  New City With { _
    .Country = "Palau",
    .Name = "Koror", _
    .Longitude = 135, _
    .Latitude = 8 } _
}

After fixing the class declaration by removing Property and changing Long to Float, the array initializer expression won't compile because there's a missing New City() type declaration in the first line, which should read Dim Capitals = New City(){ _. It's clear that no one tested the code before publishing the update.

Note: It's a shame that VB didn't adopt the Dim corollary to C#'s var arrayName = New[]{ ... } syntax, as Ralf Ehlert mentions in his "Problems with New Features of VB9" post in the Visual Studio VB Express Orcas forum.

The authors made no mention of VB 9.0 collection initializers, which were also missing from Jean-Marie Pirelli's "C# 3.0 and VB 9.0" session at Microsoft TechDays 07 March 27-28, 2007 in Geneva. Uncharacteristically, Jean-Marie provided both C# and VB examples, but he dispensed with VB examples when he reached the Collection Initializers slide. (Anders Hejlsberg and Jay Schmelzer received credit for the slides.)

It's interesting that Jeff Bramwell's Collection Initializer post in the Visual Basic Orcas forum didn't receive a reply from a Microsoft representative. The suggestion he received from Klaus Even Enevoldsen wasn't apropos collection initializers.

Note: My March 26, 2007 Updated "Overview of Visual Basic 9.0" Stealth Post lists other problems with the Overview reported by Jim Wooley. These errors relate to unimplemented VB 9.0 features scheduled for Beta 2.

There also were errors in the LINQ to SQL documentation that I reported in my April 26, 2007 More Issues with VB 9.0 Samples in the Updated LINQ to SQL Documentation post.

Update 6/24/2007: Added comment about data type error in Cities class declaration and corrected minor typos.

Update 6/25/2007: Bill McCarthy takes issue with my "It's clear that no one tested the code before publishing the update" statement about the second sample that uses the New Type With syntax in his VB9 and collection initializers post.

Regardless of syntax issues with the array initializer code, the class declaration has both syntax (Property) and data type (Long) errors.

Regarding the failure of the initializer code to compile, Bill says:

[I]t is clear the document is forward looking, and talking about how things will/should be.

So, given that, if we stop and analyze the syntax for array initializers it should be basically as presented. Perhaps one could argue the syntax should be Dim Capitals() = {.    It should also support the syntax you can use today in VS 2005, e.g: Dim ints() As Int32 = {1,2,3} , or as per the code Roger suggests Dim ints() As Int32 = New Int32(){1,2,3}

Okay so given the minimum it needs to support (current syntax), in VB9 we add anonymous types and inferred typing.  Inferred typing means we remove the "As XXX" part.  So this means the syntax Dim Capitals As City() becomes Dim Capitals and the Dim Capitals() As City syntax becomes  Dim Capitals().  Anonymous types means we can't define the type name. So New City() { would become New() { _ , and given that the existing syntax doesn't even require the New Int32(), the New() becomes superfluous because the set brackets { } define the array data.

IOW: the syntax as shown is as it should be. (IMO)

Explicitly typing the array members by adding New City With { ... syntax precludes the array being of an anonymous type or use of inferred typing and the Dim Capitals = New City(){ ... statement is required. I didn't propose Dim Capitals() As City = {...} or Dim Capitals As City() = New City(){...}  as inferred by Bill's "Dim ints() As Int32 = {1,2,3} , or as per the code Roger suggests Dim ints() As Int32 = New Int32(){1,2,3}" sentence.

You can create anonymously-typed array elements by substituting New With for New City With, but Dim Capitals() = {...} returns an array of type Object with Option Strict Off and won't compile with Option Strict On. If VB 9.0 finally supports the New() construct for anonymously typed arrays, the the preceding code should compile with Dim Capitals = New(){ _, but I don't believe that New(){ _ would be superfluous. Here are two C# array initializer examples that use new[]:

/* Array initializer with object initializers (array is inferred type LineItem) */

var LineItems = new[]
{
    new LineItem {OrderID = 11000, ProductID = 11, Quantity = 10, QuantityPerUnit = "24  500-g bottles", UnitPrice = 15.55M, Discount = 0.0F},
    new LineItem {OrderID = 11000, ProductID = 21, Quantity = 20, QuantityPerUnit = "12 1-kg cartons", UnitPrice = 20.2M, Discount = 0.1F},
    new LineItem {OrderID = 11000, ProductID = 31, Quantity = 30, QuantityPerUnit = "24 1-kg bags", UnitPrice = 25.45M, Discount = 0.15F}
};

/* Array initializer with object initializers (array and elements are anonymous types) */

var LineItems2 = new[]
{
    new {OrderID = 11000, ProductID = 11, Quantity = 10, QuantityPerUnit = "24 500-g bottles", UnitPrice = 15.55M, Discount = 0.0F},
    new {OrderID = 11000, ProductID = 21, Quantity = 20, QuantityPerUnit = "12 1-kg cartons", UnitPrice = 20.2M, Discount = 0.1F},
    new {OrderID = 11000, ProductID = 31, Quantity = 30, QuantityPerUnit = "24 1-kg bags", UnitPrice = 25.45M, Discount = 0.15F}
};

However, I haven't heard anything about VB 9.0 implementing New(). On the other hand, I'm not sure why I would want to create an anonymously typed array or what its use would be.

1 comments:

Bill McCarthy said...

Hey Roger,

RE : "Explicitly typing the array members by adding New City With { ... syntax precludes the array being of an anonymous type or use of inferred typing "

No. You can have type inferrance anywhere you don't explicitly state the type on the left hand side of the assignment operator.
So if you ommit the AS XXX on the left you are using inferrance. e.g: Dim x = 42

RE the use of New(). In c# new[] has a clear meaning. In VB New() doesn't necessarily indicate an array. What does indicate the arry on the assingment side is the {}'s . Hence the New really is superfulous on the RHS of the assignment operator.