Friday, August 17, 2007
Several weeks ago, I posted this bit of code that shows how we might use a C# 3.0 query expression to calculate the sum of the squares of an array of integers.
static void Main()
{
  var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

  var
sum = (from n in numbers
             where (n % 2) == 0
             select n * n).Sum();

  Console
.WriteLine("Sum: {0}", sum);
}

Translating this sample into Visual Basic 9.0 produces almost identical code.

Sub Main()
  Dim numbers() = New Integer() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

  Dim
total = (From n In numbers _
               Where (n Mod 2) = 0 _
               Select n * n).Sum()

  Console.WriteLine("Sum: {0}", total)
End Sub

However, this translation is a bit naive because Visual Basic 9.0 actually provides syntax for more of the standard query operators than C# 3.0 does. While we have to call the "Sum" query operator explicitly in C#, Visual Basic allows us to use it directly in the query.

Sub Main()
  Dim numbers() = New Integer() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

  Dim
total = Aggregate n In numbers _
              Where (n Mod 2) = 0 _
              Select n * n _
              Into Sum()

  Console.WriteLine("Sum: {0}", total)
End Sub

In fact, Visual Basic even allows us to create our own aggregate functions and use them directly in query expressions.

<Extension()> _
Function Product(ByVal source As IEnumerable(Of Integer)) As Integer
  Dim
result = 1
  For Each n In source
    result *= n
  Next
  Return
result
End Function

Sub
Main()
  Dim numbers() = New Integer() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

  Dim total = Aggregate n In numbers _
              Where (n Mod 2) = 0 _
              Select n _
              Into Product()

  Console.WriteLine("Sum: {0}", total)
End Sub

Here we get the product of the even numbers in the array. (I removed the expression to square each even number because it produced an OverflowException.)

I should point out that there is a behavioral difference that the Visual Basic "Aggregate" keyword introduces. A standard "From" query expression is delay evaluated. That is, the results aren't actually evaluated until they are accessed through, say, a "For Each" loop. However, an "Aggregate" query expression forces the results to be evaluated immediately. In contrast, C# 3.0 query expressions always produce results that are delay evaluated.1

1A bold statement that will be completely recanted if any reader can find an example that proves otherwise.2
2Please, prove me wrong. Seriously. I'm interested in this stuff.3
3This footnote motif is clearly ripped off from Raymond Chen.

posted on Friday, August 17, 2007 10:46:31 AM (Eastern Standard Time, UTC-05:00)  #    Comments [2]

kick it on DotNetKicks.com
Friday, August 17, 2007 5:12:08 PM (Eastern Standard Time, UTC-05:00)
There are a few problems with this post...

If you call Product in either C# or VB with the From keyword instead of Aggregate, the results are always immediately evaluated.

For instance, with

(from n in numbers
where (n % 2) == 0
select n * n).Product();

This is because Product() calls GetEnumerator() and uses the query before it computes its result.

There is an Aggregate extension method in the System.Linq.Enumerable class. I have a feeling that the VB team were providing explicit support for that extension method.
Monday, August 20, 2007 8:52:08 AM (Eastern Standard Time, UTC-05:00)
I'm not sure how either of your points count as problems with the post. I wrote about your first point about "Aggregate" queries not being delay evaluated in the post. From your comment, it seems that you are suggested that this information is missing. If I'm misreading your comment please feel free to thrash me thoroughly. :-)

As for the second point, I am indeed aware of the Aggregate extension method for IEnumerable and IQueryable, but your guess that Visual Basic is using that extension method is incorrect. The Aggregate extension method is a proper fold method and requires a function to be passed to it that performs the accumulation. (I show how the Aggregate method can be used in this post: http://diditwith.net/2007/06/27/AHigherCallingRevisited.aspx)

The truth is, VB treats this code:

Aggregate n in numbers Where (n Mod 2) = 0 Select n * n Into Sum()

Like this:

(From n in numbers Where (n Mod 2) = 0 Select n * n).Sum()

The only real benefit of VB's Aggregate keyword is that it clarifies the code. When a reader sees the Aggregate keyword in code, they can be sure that the query will not return a delay-evaluated result. In C#, it's not as easy to understand what's happening.
Comments are closed.