Sunday, June 24, 2007

Visual Basic Team Starts LINQ Cookbook Series

Serial blogs about LINQ topics appear to be gaining popularity. The VB Team has embarked on writing the LINQ Cookbook and posted the first two recipes on 6/22/2007.

Update 7/12/2007: Added recipes 5, 6, and 7.

Note: O'Reilly Media appears to claim the word Cookbook™ as a trademark, which seems outrageous to me.

LINQ Cookbook, Recipe 1: Change the font for all labels on a windows form iterates a sample WinForm's controls collection and partially refactors your project by changing the fonts of Label controls to the Comic Sans MS family.

LINQ Cookbook, Recipe 2: Find all capitalized words in a phrase and sort by length (then alphabetically) uses the String.Split() method to divide a block of text into an array of words, then uses LINQ to iterate the array, discarding words that aren't initial-letter-capped, and populating a list box with a sequence sorted first by word length and then in alphabetical order.

LINQ Cookbook, Recipe 3: Find all the prime numbers in a given range accepts two integer arguments and returns all prime numbers between and including the argument values. This project requires Beta 2 because it uses Group By in the query expression; it won't compile in Beta 1 (and might not in Beta 2 because of what appears to be a conflict between Count(), an array, and Count, an integer).

LINQ Cookbook, Recipe 4: Find all complex types in a given assembly fills a list box with the names of "complex types" from Mscorlib.dll (the System.Core namespace). The project defines a complex type as "having more than 10 public methods, of which at least one has more than 3 arguments." The original code contained a typo: t.GetMethods() should be type.GetMethods(). After fixing the typo, the code compiles in Beta 1. Update: The typo is fixed.

LINQ Cookbook, Recipe 5: Concatenating the selected strings from a CheckedListBox is a contrived app that does what it says (comma-separated) with the LINQ Aggregate ... In construct. Several comments recommend code improvements.

Linq Cookbook, Recipe 6: Your first Linq Application using Northwind shows users new to LINQ to SQL how to install and connect to the Northwind database on an SQL Express database instance. As usual, the author recommends a connection to the NORTHWND.MDF file rather than using SQL Server Management Studio Express (SSMSX) to attach the .mdf file to the instance.

LINQ Cookbook, Recipe 7: Selecting Pages of Data from Northwind demonstrates use of the Skip()Skip and Take()methods for paging data items from LINQ to SQL queries.

Update 7/14/2007: Bill McCarthy's July 14, 2007 Inside Recipe 7 post observes that running Recipe 7's LINQ query within a function that has no return type requires setting Option Strict Off, which isn't a recommended practice. Bill also provides and example of the convoluted T-SQL syntax required to support the Skip and Take operators.

Recipe 42 is the most more interesting project so far, but it would have been more interesting if it returned LINQ-related types with a query such as this:

Dim q = From type In System.Reflection.Assembly.GetAssembly( _
        GetType(Funclet)).GetTypes(), _
        m In type.GetMethods() _
        Where (type.IsPublic) _
        AndAlso (type.ToString.Contains("Linq")) _
        Select type Distinct

After giving the projects a quick test drive, I have some recommendations for these and future recipes:

  • Use a conventional <pre> block for code to be copied rather than the inline style (MsoNormal with Courier), which double-spaces the code in VS 2008 and gives line continuations the fits. See Will Visual Basic 9.0 Have Collection Initializers? as an example of a post and VB 9.0 Code for the LINQ to SQL Performance Test Harness as a page with <pre> blocks.
  • Encourage folks to set Option Strict On by strongly typing variables and literals. For example, in Recipe 2 Dim Text As String and New Char() {CChar(","), CChar("."), CChar(";"), CChar(":"), CChar(""""), CChar("!"), CChar("?"), CChar(" ")} instead of New Char() {",", ".", "!", " "} (I added a few extra splitter chars to cover more bases.)  Some examples won't run with Option Strict On.
  • Break the habit of using & as the string concatenation character; + has been preferred since VB 7.0 (see the text variable in Recipe 2).
  • Don't establish recipe prerequisites that don't apply and aren't available. Most of us don't have VS 2008 Beta 2 and it isn't needed for the first two projects.

Making VS 2008 a prerequisite for these projects indicates to me that it's coming soon. Let's hope.

Update 6/25/2007: Bill McCarthy set me straight on use of the + and & operators for string concatenation. I got into the habit of using + in VB 7.0 as the result of using it in C# and, as I recall, some problem I encountered with & early on. As the "Concatenation Operators in Visual Basic" online help topic says:

The & operator is recommended for string concatenation because it is defined exclusively for strings and reduces your chances of generating an unintended conversion.

However, it's not going to be easy to break a seven-year (bad) habit. Also, &= looks a lot stranger to me than +=.

Surprise!: In doing a search to try to remember what problem I had encountered with the & operator, I found an example of Billy Hollis using + to concatenate strings (Gasp!) on page 1 of his "Straighten Out Your Strings" article for Visual Studio Magazine:

Dim sTest As String
For i As Integer = 0 To 15000
    sTest = sTest + "Strings in VB"

To his credit, he uses & elsewhere.

Nick Randolph noted in a comment that there's a simpler VB syntax for specifying literal strings as Char: New Char() {","c, "."c, ";"c, ":"c, """"c, "!"c, "?"c, " "c}. (An even shorter syntax that I should have thought of is ",.;:""!? ".ToCharArray, which Billy Hollis discusses at length in his article.) I just used the syntax suggested by the Error Correction Options popup: "Replace "," with CChar(",")." Another case of typing when I should have been thinking. On the other hand, maybe the popup should have suggested ToCharArray.

Mea culpa. With this much egg on my face, I think I'll stop making VB syntax recommendations.

Update 7/4/2007: Added Kit George's Recipe 5.


Nick said...

You know that there is a much more abbreviated way of doing string to char conversions in VB.NET, right:
New Char() {","c, "."c, ";"c, ":"c,""""c,"!"c,"?"c, " "c}

Bill McCarthy said...

Like Nick said, you should use Char's and the c suffix instead of CChar.
And the concatentation operator for strings is & and it is the one that should be used. by using & you guarantee that string concatenation is performed under all circumstances (or an exception is thrown).
The + operator will attempt to do addition if any operand is a numeric. If you consider late bound code this is very important distinction.
AS to the "+ has been preferred since VB 7.0" Well as far as I cna recall you could always use + in VB6 as well as &. But the preferred way has been to use & becuase you avoid issues with addition being performed instead of concatenation.
Now with VB moving more back in the direction of dynamic, VB 10, it's more important than ever to use the correct operator for concatenation whihc is and always has been &.

Jonathan said...

You might want to get an actual VB programmer to vet your suggestions. The very idea of using + for concatination makes me shudder.

Anonymous said...

The word put out from Getz et al way back for the reason "&" was introduced (e.g., in Access VB) was to support concatenation of a Null - the "+" choked on that.

Below, the first MsgBox works. The second aborts with "Invalid use of null":

Public Sub subTestConcat()

Dim varNull As Variant
Dim strString As String

strString = "This "
varNull = Null

MsgBox strString & varNull
MsgBox strString + varNull

End Sub