Introduction
Complicated things seem intuitively simple when complexity is cleverly hidden in the open. LINQ is a query language that at its basic is pretty simple to learn to use, but there is a lot of meaning in all of the aspects of a query. Understanding these cleverly hidden meanings will help you get beyond the basics and really unleash the power of LINQ.
Working with range and let introduces the notion of range and temporary range variables. Understanding how range variables are defined in from and let statements in LINQ will help you write more powerful queries.
Using Range Variables
The From clause is the first clause in a LINQ query, everywhere it’s required. From is always required except in Aggregate queries. As a refresher, a basic LINQ query is
From something In someCollection Select something
The From clause is First because it defines the context for Intellisense, which is used to aid the Select clause, especially if you define a projection. (A projection is a new type that can be defined using “with new”. If you follow the grammar of the syntax advantage, you can write a LINQ query. The sample in Listing 1 uses an initialized array of integers, and the query returns only the even numbers.
Listing 1: A basic query that returns even numbers.
Sub Range() Dim numbers = New Integer() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} Dim evens = From num In numbers _ Where num Mod 2 = 0 _ Select num For Each value In evens Console.WriteLine(value) Next Console.ReadLine() End Sub
The LINQ query is the statement that begins with Dim evens = From num In numbers. The first word after the From keyword is referred to as the range variable. The Range variable plays the same role as the iteration variable in a for loop. For example,
For Each num In numbers
illustrates the relationship between a range variable in a LINQ query and the iteration variable in a for loop.
In the Select clause, you can select just the range variable or if the range variable is a compound type—read an instance of an object—you can define a projection using Select New With. The next section is an unrelated use of the word Range but it does demonstrate a projection in the Select clause.
Unrelated: Using Range Generation
In object-oriented land, there are a lot of synonymous meanings. Generalization in UML means inheritance in code. There are also words that are used as methods. For instance, I talked about the first word after From being called a range. There is a Range method, too. In this case, the words are similar but not related in this context. The Enumerable.Range method will create a Range for you.
Although the Range method is not directly related to the concept of a range for LINQ queries, it can be used here to generate a range to demonstrate how to create a projection. A projection is an anonymous type derived dynamically from elements of existing types. In Listing 2, the Enumerable.Range method is used to create a range of integers from 33 to 42. The first argument is the starting point and the second argument is the count. In the listing, the LINQ statement queries the dynamic range of integers from 33 to 42 and uses the projection syntax to create a new type that is an object containing the number and the word Even or Odd indicating the word’s parity (or evenness or oddness).
Listing 2: Using the Enumerable.Range method to generate a dynamic range and a projection to create a new type containing the number and the word’s parity.
Sub RangeMethod() Dim numbers = From num In Enumerable.Range(33, 10) _ Select New With {.Number = num, .Parity = _ IIf(num Mod 2 = 0, "Even", "Odd")} For Each value In numbers Console.WriteLine(value) Next Console.ReadLine() Sub
A projection is created with the Select New With clause. New fields can be defined with the .Name syntax and the values can be assigned from the range variable or dynamically created. In the example, the .Number field is mapped directly to the range variable and the IIf function tests the range value for evenness—num Mod 2 = 0—and labels the .Parity field with the string “Even” or “Odd” based on the result of the first argument, the test statement.