Книга: C# 2008 Programmer
LINQ and Extension Methods
Разделы на этой странице:
LINQ and Extension Methods
Chapter 4 explored extension methods and how you can use them to extend functionality to an existing class without needing to subclass it. One of the main reasons why the extension method feature was incorporated into the C# 3.0 language was because of LINQ.
Consider the earlier example where you have an array called allNames
containing an array of strings. In .NET, objects that contain a collection of objects must implement the IEnumerable
interface, so the allNames
variable implicitly implements the IEnumerable
interface, which only exposes one method — GetEnumerator
. But when you use IntelliSense in Visual Studio 2008 to view the list of methods available in the allNames
object, you see a list of additional methods, such as Select
, Take
, TakeWhile
, Where
, and so on (see Figure 14-2).
Figure 14-2
In C# 3.0, all these additional methods are known as extension methods, and they are extended to objects that implement the IEnumerable interface. These extension methods are the LINQ standard query operators.
In Visual Studio 2008, all extension methods are denoted by an additional arrow icon, as shown in Figure 14-3.
Figure 14-3
To add extension methods to objects implementing the IEnumerable interface, you need a reference to System.Core.dll and import the namespace by specifying the namespace:
using System.Linq;
The following table lists the LINQ standard query operators.
Operator Type | Operator Name |
---|---|
Aggregation | Aggregate , Average , Count , LongCount , Max , Min , Sum |
Conversion | Cast , OfType , ToArray , ToDictionary , ToList , ToLookup , ToSequence |
Element | DefaultIfEmpty , ElementAt , ElementAtOrDefault , First , FirstOrDefault , Last , LastOrDefault , Single , SingleOrDefault |
Equality | EqualAll |
Generation | Empty , Range , Repeat |
Grouping | GroupBy |
Joining | GroupJoin , Join |
Ordering | OrderBy , ThenBy , OrderByDescending , ThenByDescending , Reverse |
Partitioning | Skip , SkipWhile , Take , TakeWhile |
Quantifiers | All , Any , Contains |
Restriction | Where |
Selection | Select , SelectMany |
Set | Concat , Distinct , Except , Intersect , Union |
Deferred Query Execution
The query variable itself only stores the query; it does not execute the query or store the result.
Take another look at the preceding example:
int[] nums = {
12, 34, 10, 3, 45, 6, 90, 22, 87, 49, 13, 32
};
var oddNums = nums.Where
(n => n % 2 == 1).OrderByDescending(n => n);
The oddNums
variable simply stores the query (not the result of the query). The query is only executed when you iterate over the query variable, like this:
foreach (int n in oddNums)
Console.WriteLine(n);
This concept is known as deferred execution, and it means that every time you access the query variable, the query is executed again. This is useful because you can just create one query and every time you execute it you will always get the most recent result.
To prove that deferred execution really works, the following program first defines a query and then prints out the result using a foreach
loop. Twenty is added to each element in the array, and then the foreach
loop is executed again.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication5 {
class Program {
static void Main(string[] args) {
int[] nums = {
12, 34, 10, 3, 45, 6, 90, 22, 87, 49, 13, 32
};
var oddNums =
nums.Where(n => n % 2 == 1).OrderByDescending(n => n);
Console.WriteLine("First execution");
Console.WriteLine("---------------");
foreach (int n in oddNums) Console.WriteLine(n);
//---add 20 to each number in the array---
for (int i = 0; i < 11; i++) nums[i] += 20;
Console.WriteLine("Second execution");
Console.WriteLine("----------------");
foreach (int n in oddNums) Console.WriteLine(n);
Console.ReadLine();
}
}
}
The program prints out the following output:
First execution
---------------
87
49
45
13
3
Second execution
107
69
65
33
23
Because the output for the second foreach
loop is different from the first, the program effectively proves that the query is not executed until it is accessed.
Deferred execution works regardless of whether you are using the query or method syntax.
Forced Immediate Query Execution
One way to force an immediate execution of the query is to explicitly convert the query result into a List
object. For example, the following query converts the result to a List
object:
var oddNums = nums.Where
(n => n % 2 == 1).OrderByDescending(n => n).ToList();
In this case, the query is executed immediately, as proven by the following program and its output: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication5 {
class Program {
static void Main(string[] args) {
int[] nums = {
12, 34, 10, 3, 45, 6, 90, 22, 87, 49, 13, 32
};
var oddNums = nums.Where
(n => n % 2 == 1).OrderByDescending(n => n).ToList();
Console.WriteLine("First execution");
Console.WriteLine("---------------");
foreach (int n in oddNums) Console.WriteLine(n);
//---add 20 to each number in the array---
for (int i = 0; i < 11; i++) nums[i] += 20;
Console.WriteLine("Second execution");
Console.WriteLine("----------------");
foreach (int n in oddNums) Console.WriteLine(n);
Console.ReadLine();
}
}
}
Here's the program's output:
First execution
---------------
87
49
45
13
3
Second execution
87
49
45
13
3
The output of the first and second execution is the same, proving that the query is executed immediately after it's defined.
To force a LINQ query to execute immediately, you can use aggregate functions so that the query must iterate over the elements at once. An aggregate function takes a collection of values and returns a scalar value.
Aggregate functions are discussed in more detail later in this chapter.
Following is an example that uses the Count()
aggregate function. The program selects all the odd numbers from an array and then counts the total number of odd numbers. Each number is then multiplied by two (which makes them all become even numbers).
static void Main(string[] args) {
int[] nums = {
12, 34, 10, 3, 45, 6, 90, 22, 87, 49, 13, 32
};
var oddNumsCount = nums.Where
(n => n % 2 == 1).OrderByDescending(n => n).Count();
Console.WriteLine("First execution");
Console.WriteLine("---------------");
Console.WriteLine("Count: {0}", oddNumsCount);
//---add 20 to each number in the array---
for (int i = 0; i < 11; i++)
nums[i] *= 2; //---all number should now be even---
Console.WriteLine("Second execution");
Console.WriteLine("----------------");
Console.WriteLine("Count: {0}", oddNumsCount);
Console.ReadLine();
}
The output shows that once the query is executed, its value does not change:
First execution
---------------
Count: 5
Second execution
----------------
Count: 5
- Разработка приложений баз данных InterBase на Borland Delphi
- Open Source Insight and Discussion
- Introduction to Microprocessors and Microcontrollers
- Chapter 6. Traversing of tables and chains
- Chapter 8. Saving and restoring large rule-sets
- Chapter 11. Iptables targets and jumps
- Chapter 5 Installing and Configuring VirtualCenter 2.0
- Chapter 16. Commercial products based on Linux, iptables and netfilter
- Appendix A. Detailed explanations of special commands
- Appendix B. Common problems and questions
- Appendix E. Other resources and links
- IP filtering terms and expressions