int
Values Using LINQFigure 9.2 shows how to use LINQ to Objects to query an array of integers, selecting elements that satisfy a set of conditions—a process called filtering. Iteration statements that filter arrays focus on the process of getting the results—iterating through the elements and checking whether they satisfy the desired criteria. LINQ specifies the conditions that selected elements must satisfy. This is known as declarative programming—as opposed to imperative programming (which we’ve been doing so far) in which you specify the actual actions to perform a task. The query in lines 22–24 specifies that the results should consist of all the int
s in the values
array that are greater than 4
. It does not specify how those results are obtained—the C# compiler generates all the necessary code, which is one of the great strengths of LINQ. Using LINQ to Objects requires the System.Linq
namespace (line 4).
from
ClauseA LINQ query begins with a from
clause (line 22), which specifies a range variable (value
) and the data source to query (values)
. The range variable represents each item in the data source (one at a time), much like the control variable in a foreach
statement. Since value
is assigned one element at a time from the array values
—an int
array—the compiler infers that value
should be of type int
. You also may declare the range variable’s type explicitly between the from
keyword and the range-variable’s name.
Introducing the range variable in the from
clause allows the IDE to provide Intelli-Sense as you type the rest of the query. When you enter the range variable’s name followed by a dot (.
) in the code editor, the IDE displays the range variable’s methods and properties, making it easier for you to construct queries.
Typically, implicitly typed local variables (declared with var
) are used for the collections of data returned by LINQ queries, as we do in lines 21, 34, 47 and 61. We also use this feature to declare the control variable in the foreach
statements.
where
ClauseIf the condition in the where
clause (line 23) evaluates to true
, the element is selected— i.e., it’s included in the results. Here, the int
s in the array are included in the result only if they’re greater than 4
. An expression that takes a value and returns true
or false
by testing a condition on that value is known as a predicate.
select
ClauseFor each item in the data source, the select
clause (line 24) determines what value appears in the results. In this case, it’s the int
that the range variable currently represents, but you’ll soon see that the select
clause may contain an expression that transforms a value before including it in the results. Most LINQ queries end with a select
clause.
Lines 28–31 use a foreach
statement to display the query results. As you know, a foreach
statement can iterate through the contents of an array, allowing you to process each element in the array. Actually, the foreach
statement can iterate through the contents of arrays, collections and the results of LINQ queries. The foreach
statement in lines 28–31 iterates over the query result filtered
, displaying each of its int
items.
It would be simple to display the integers greater than 4
using a iteration statement that tests each value before displaying it. However, this would intertwine the code that selects elements and the code that displays them. With LINQ, these are kept separate:
the LINQ query specifies how to locate the values and
a loop accesses the results
making the code easier to understand and maintain.
orderby
ClauseThe orderby
clause (line 36) sorts the query results in ascending order. Lines 49 and 64 use the descending
modifier in the orderby
clause to sort the results in descending order. An ascending
modifier also exists but isn’t normally used, because it’s the default. Any value that can be compared with other values of the same type may be used with the or-derby
clause. A value of a simple type (e.g., int
) can always be compared to another value of the same type; we’ll discuss how to compare values of reference types in Chapter 12.
The queries in lines 48–50 and 62–65 generate the same results, but in different ways. Lines 48–50 uses LINQ to sort the results of the query filtered
from lines 22–24. The second query uses both the where
and orderby
clauses. Because queries can operate on the results of other queries, it’s possible to build a query one step at a time, passing the results of queries between methods for further processing.
IEnumerable<T>
As we mentioned, the foreach
statement can iterate through the contents of arrays, collections and LINQ query results. Actually, foreach
iterates over any so-called IEnumerable<T>
object, which just happens to be what most LINQ queries return.
IEnumerable<T>
is an interface. Interfaces define and standardize the ways in which people and systems can interact with one another. For example, the controls on a radio serve as an interface between radio users and the radio’s internal components. The controls allow users to perform a limited set of operations (e.g., changing the station, adjusting the volume, and choosing between AM and FM), and different radios may implement the controls in different ways (e.g., using push buttons, dials or voice commands). The interface specifies what operations a radio permits users to perform but does not specify how the operations are implemented. Similarly, the interface between a driver and a car with a manual transmission includes the steering wheel, the gear shift, the clutch, the gas pedal and the brake pedal. This same interface is found in nearly all manual-transmission cars, enabling someone who knows how to drive one manual-transmission car to drive another.
Software objects also communicate via interfaces. A C# interface describes a set of methods and properties that can be called on an object—to tell the object, for example, to perform some task or return some piece of information. The IEnumerable<T>
interface describes the functionality of any object that can be iterated over and thus offers methods and properties to access each element. A class that implements an interface must declare all the methods and properties described by that interface.
Most LINQ queries return an IEnumerable<T>
object—some queries return a single value (e.g., the sum of an int
array’s elements). For queries that return an IEnumerable<T>
object, you can use a foreach
statement to iterate over the query results. The notation <T>
indicates that the interface is a generic interface that can be used with any type of data (for example, int
s, string
s or Employee
s). You’ll learn more about the <T>
notation in Section 9.4. In Section 12.7, we’ll discuss interfaces and show how to define your own interfaces. In Chapter 20, we’ll cover generics in detail.