Employee
Objects Using LINQLINQ is not limited to querying arrays of simple types such as int
s. It can be used with arrays of any data type, including string
s and user-defined classes. It cannot be used when a query does not have a defined meaning—for example, you cannot use orderby
on values that are not comparable. Comparable types in .NET are those that implement the IComparable
interface, which is discussed in Section 20.4. All built-in types, such as string
, int
and double
implement IComparable
. Figure 9.3 presents an Employee
class. Figure 9.4 uses LINQ to query an array of Employee
objects.
The where
clause in line 30 (Fig. 9.4) accesses the range variable’s properties. The compiler infers that the range variable is of type Employee
, because employees
was defined as an array of Employee
objects (lines 11–18). Any bool
expression can be used in a where
clause. Line 30 uses the &&
(conditional AND) operator to combine conditions. Here, only employees that have a salary between $4,000 and $6,000 per month, inclusive, are included in the query result, which is displayed in lines 36–39.
Line 44 uses orderby
to sort the results according to multiple properties—specified in a comma-separated list. Here, the employees are sorted alphabetically by last name. Employee
s that have the same last name are sorted by first name.
Any
, First
and Count
Extension MethodsLine 51 introduces the Any
method, which returns true
if the query to which it’s applied has at least one element. The query result’s First
method (line 53) returns the first element in the result. You should check that the query result is not empty (line 51) before calling First
, which throws an InvalidOperationException
if the collection is empty.
We’ve not specified the class that defines methods First
and Any
. Your intuition probably tells you they’re methods of interface IEnumerable<T>
, but they aren’t. They’re actually extension methods that enhance a class’s capabilities without modifying the class’s definition. The LINQ extension methods can be used as if they were methods of IEnumerable<T>
. Section 10.14 shows how to create extension methods.
LINQ defines many more extension methods, such as Count
, which returns the number of elements in the results. Rather than using Any
, we could have checked that Count
was nonzero, but it’s more efficient to determine whether there’s at least one element than to count all the elements. The LINQ query syntax is actually transformed by the compiler into extension method calls, with the results of one method call used in the next. It’s this design that allows queries to be run on the results of previous queries, as it simply involves passing the result of a method call to another method. For a complete list of IEnumerable<T>
extension methods, visit
https://msdn.microsoft.com/library/9eekhta0
Line 63 uses the select
clause to select the range variable’s LastName
property rather than the range variable itself. This causes the results of the query to consist of only the last names (as string
s), instead of complete Employee
objects. Lines 67–70 display the unique last names. The Distinct
extension method (line 67) removes duplicate elements, causing all elements in the resulting collection to be unique.
select
Clause of a LINQ QueryThe last LINQ query in the example (lines 74–75) selects the properties FirstName
and LastName
. The syntax
new {e.FirstName, e.LastName}
creates a new object of an anonymous type (a type with no name), which the compiler generates for you, based on the properties listed in the curly braces ({}
). In this case, each new object of the anonymous type is initialized with the FirstName
and LastName
values from the corresponding Employee
object. These selected properties can then be accessed when iterating over the results. Implicitly typed local variables allow you to use anonymous types because you do not have to explicitly state the type when declaring such variables.
When the compiler creates an anonymous type, it automatically generates a ToString
method that returns a string
representation of the object. You can see this in the program’s output—it consists of the property names and their values, enclosed in braces. Anonymous types are discussed in more detail in Chapter 22.
The query in lines 74–75 is an example of a projection, which transforms an object into a new form. In this case, the transformation creates new objects containing only the FirstName
and LastName
properties, but projections also can manipulate the data. For example, a projection that includes the MonthlySalary
could give all employees a 10% raise by multiplying their MonthlySalary
properties by 1.1
with the expression
e.MonthlySalary * 1.1M
You can specify a new name for a selected property in an anonymous type. For example, if line 75 is written as
new {First = e.FirstName, Last = e.LastName}
the anonymous type would have properties named First
and Last
, rather than FirstName
and LastName
. If you don’t specify a new name, the property’s original name is used.