C# LINQ详解(一)

原文标题:How does it work in C#?-Part 3 (C# LINQ in detail),作者:Mohammand A Rahman.

 

目录 

LINQ 基础 

扩展方法-幕后的工作 

扩展方法列表 

Where and Select 

All 

Average 

Concat 

Contains 

Count 

DefaultIfEmpty 

Distinct 

ElementAt 

Empty 

Except 

First 

FirstOrDefault 

Union 

Intersect 

Last 

LastOrDefault 

LongCount 

Max 

Min 

OfType 

Range 

Repeat 

Reverse 

Single 

Skip 

SkipWhile 

Sum 

ThenBy 

ToArray 

ToDictionary 

ToList 

Zip 

LINQ基础介绍 

         .NET,任何数据结构都是由在mscorlib.dllSystem.Collections.Generic命名空间下的Ienumerable<T>接口得到的. 映射可以访问所有的定义在System.Core.dllSystem.Linq命名空间下的枚举类.这个枚举类是定义在System.Core.dllSystem.Linq命名空间下的一个静态非可继承类.这个枚举类的定义如下: 

.class public abstract auto ansi sealed beforefieldinit System.Linq.Enumerable extends [mscorlib]System.Object 

这个静态枚举类是对Ienumerable<T>接口中不同扩展方法的一个封装.例如下面的例子: 

复制代码
public static bool Contains<TSource>( 

    this IEnumerable<TSource> source, TSource value) 

{ /* code removed*/} 

public static int Count<TSource>( 

    this IEnumerable<TSource> source)  

{ /* code removed*/} 

public static IEnumerable<TSource> Distinct<TSource>( 

    this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)  

{ /* code removed*/} 

// and many more 
复制代码

扩展方法介绍 

Where and Select 

Where Select是两个定义在Ienumerable<TSource>接口中非常重要的方法.它们的定义如下: 

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate) 

所以任何来源于Ienumerable<TSource>接口的数据结构都能访问这个方法,例如List<T>.List<T>类实现了Ienumerable<T>接口,它的定义如下: 

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, Ienumerable 

那么来让我们看一个关于WhereSelect的例子. 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<string> numbers = new List<string>()  
24 
25             {  
26 
27                 "One", "Two", "Three", "Four",  
28 
29                 "Five", "Six", "Seven"  
30 
31             }; 
32 
33  
34 
35             var numbersLengthThree = 
36 
37                 numbers.Where(x => x.Length == 3).Select(x => x).ToList(); 
38 
39  
40 
41             numbersLengthThree.ForEach(x => Console.WriteLine(x)); 
42 
43         } 
44 
45     } 
46 
47 } 
复制代码

上面的代码会产生一个string类型的列表(列表中是数字)存储在List<string>,上面的程序会找出在这些项中字符总数是3的并且将结果存储到另外一个新的列表中.最后在控制台中打印出这些数字.这个程序输出的结果如下: 

One 

Two 

Six 

让我们做一个研究去找出它是如何工作的.在上面的例子中最重要的代码是numbers.Where(x => x.Length == 3).Select(x => x).ToList().下边的图表会为我们解释整个的执行过程. 

通过上面的图表我们可以知道CLR伴随着MulticastDelegate实例(持有着关于<Main>b_1方法的信息)将数字列表作为输入参数传递到Where方法,其中<Main>b_1创建于匿名函数(x=>x.Length==3).通过Where方法它会返回一个WhereListIterator<string>迭代器的实例,这个实例将会作为输入参数传递到Select子句中,伴随着另外一个MulticastDelegate实例(持有着关于<Main>b_2方法的信息),<Main>b_2通过匿名方法(x=>x)创建。Select方法将根据输入内容实例化相关的迭代器。在这种情况下,它会实例化出WhereSelectListIterator<string,string>迭代器。这个迭代器将作为输入参数传递到ToList()方法中。此方法最终通过循环遍历对原始列表进行处理并得到一个基于过滤条件的新列表。 

 

All 

这个方法确定是否所有元素序列都满足某种条件,如果每一个元素都可以满足设定的特殊条件或者它是空,则方法返回true,否则返回false。方法定义如下: 

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 

 

这个方法被用在判断是否一个元素序列满足某种条件。序列中的每一个元素都会经过判断。在下面的程序中,我创建了一个包含OneTwoThree等项目的List<string>实例,下面的程序将找出在这个序列中是否包含不少于3个字符的元素。 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<string> numbers = new List<string>()  
24 
25             {  
26 
27                 "One", "Two", "Three", "Four",  
28 
29                 "Five", "Six", "Seven"  
30 
31             }; 
32 
33  
34 
35             if (numbers.All<string>(x => x.Length >= 3)) 
36 
37                 Console.WriteLine("All numbers have at least three characters."); 
38 
39         } 
40 
41     } 
42 
43 } 
复制代码

 

上面的程序将会输出:All numbers have at least three characters. 

因为All方法都将匹配序列中的元素对于指定的条件是否有效。它的工作原理如下图: 

图像

从上面的图表我们可以知道CLR会通过数字列表作为输入参数传递到All()中,伴随着MulticasDelegate类的实例(这个实例通过匿名函数(x=>x.Length>=3)来创建)。在All()方法中,CLR将通过指定的条件找出序列中的每一个元素是否满足条件。 

 

Any 

这个方法确定序列中的元素是否存在或者满足某种特定的条件。方法定义如下: 

public static bool Any<TSource>(this IEnumerable<TSource> source) 

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 

 

上面的两个方法将会执行下面的情况: 

第一种重载会找出元素序列是否包含元素,第二种重载将会找出序列中是否有满足条件的元素。我写了一个小程序去解释这两种方法工作的细节。 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<string> numbers = new List<string>()  
24 
25             {  
26 
27                 "One", "Two", "Three", "Four",  
28 
29                 "Five", "Six", "Seven"  
30 
31             }; 
32 
33  
34 
35             if (numbers.Any<string>()) 
36 
37                 Console.WriteLine("The sequence contains item."); 
38 
39  
40 
41             if (numbers.Any<string>(x => x.Length >= 3)) 
42 
43                 Console.WriteLine("The sequence contains at least a item which has three or more characters"); 
44 
45  
46 
47         } 
48 
49     } 
50 
51 } 
复制代码

 

上面的程序将会做如下输出: 

The sequence contains item. 

The sequence contains at least a item which has three or more characters 

Press any key to continue . . . 

 

Average 

Average方法会计算在序列中的数字的平均值。这个方法的定义如下: 

public static double Average(this IEnumerable<int> source) 

public static decimal Average<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector) 

 

下面是一个关于Average方法的列子: 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> numbers = new List<int>()  
24 
25             {  
26 
27                 1,2,3,4,5,6,7,8,9,10 
28 
29             }; 
30 
31  
32 
33             Console.WriteLine("Average of the numbers :{0}", numbers.Average()); 
34 
35  
36 
37             Console.WriteLine("Average of the original numbers x2 :{0}",  
38 
39                               numbers.Average((x => x * 2))); 
40 
41  
42 
43         } 
44 
45     } 
46 
47 } 
复制代码

 

上面的程序将会做如下输出: 

Average of the numbers :5.5 

Average of the original numbers x2 :11 

Press any key to continue . . . 

 

Concat 

这个方法的作用是连接(拼接)两个序列.这个方法通过"延迟执行"(deferred execution)来执行.它的返回值是包含需要执行特定操作所有信息的迭代器类型的一种实例.C#,对象在调用它的GetEnumerator方法或者使用foreach语句时才会执行Concat方法. 

Concat<TSource>(Ienumerable<TSource>,Ienumerable<TSource>)方法不同于Union(),因为Concat<TSource>(Ienumerable<TSource>,Ienumerable<TSource>)方法会返回序列中所有的原始元素,Union则返回序列中不重复(独一无二)的元素.方法定义如下: 

public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) 

 

下面的程序将告诉我们怎样使用Concat方法. 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> listOne = new List<int>()  
24 
25             {  
26 
27                 1,2,3,4,5 
28 
29             }; 
30 
31  
32 
33             IList<int> listTwo = new List<int>()  
34 
35             {  
36 
37                 6,7,8,9,10 
38 
39             }; 
40 
41  
42 
43             var result = listOne.Concat(listTwo).ToList(); 
44 
45             result.ForEach(x=> Console.WriteLine(x)); 
46 
47  
48 
49  
50 
51         } 
52 
53     } 
54 
55 } 
复制代码

 

上面的程序将会做如下输出: 

1 

2 

3 

4 

5 

6 

7 

8 

9 

10 

Press any key to continue . . . 

Concat方法的工作原理如下图所示: 

图像

从上图我们可以知道CLR通过将listOnelistTwo作为输入参数传递到Concat方法中,并且从Concat方法中返回一个ConcatIterator实例作为输出参数给这个方法的调用者.虽然这将通过延迟执行模式来执行,但是ToList()方法也会通过listOnelistTwoConcatIterator类的执行逻辑来开始处理并得到最终的集合。 

 

Contains 

这个方法用来判断在一个序列(集合)中是否存在一个特殊的元素.这个方法有两种重载方式,第一种是通过默认的比较器来判断序列(集合)中是否有特殊的元素,另外一种是通过自定义IEqualityComparer<T>来确定序列(集合)中是否有特殊的元素.它们的方法定义如下: 

public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value) 

public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer) 

 

这个方法将会查找在集合中是否有一个特殊的值存在.为了解释这个方法我写了一个小程序: 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7  
 8 
 9 namespace Chapter_5 
10 
11 { 
12 
13     class Program 
14 
15     { 
16 
17         static void Main(string[] args) 
18 
19         { 
20 
21             IList<int> listOne = new List<int>()  
22 
23             {  
24 
25                 1,2,3,4,5 
26 
27             }; 
28 
29  
30 
31             var resultAsTrue = listOne.Contains(2); 
32 
33             var resultAsFalse = listOne.Contains(200); 
34 
35             Console.WriteLine("{0}\n{1}", resultAsTrue, resultAsFalse); 
36 
37         } 
38 
39     } 
40 
41 } 
复制代码

 

上面的程序将会做如下输出: 

True 

False 

Press any key to continue . . 

因此,在上述程序中当编译器找到Contains方法的第一种重载方式,它会执行如下步骤:CLR会通过Contains方法在集合中查找一个特定的值.这种查找会有两个方向,一种是如果输入参数是一个空值(null value),(CLR)会在集合中循环遍历是空值的项,如果其中一项是空值,就返回true,反之返回false.另一种情况是当不是空值的时候,CLR会对集合中的项依次进行比较,根据匹配值返回一个布尔类型作为应答. 

Contains方法的一个近似代码如下所示: 

复制代码
 1 public bool Contains(T item) 
 2 
 3 { 
 4 
 5     if (item == null) 
 6 
 7     { 
 8 
 9         for (int j = 0; j < this._size; j++) 
10 
11         { 
12 
13             if (this._items[j] == null) 
14 
15             { 
16 
17                 return true; 
18 
19             } 
20 
21         } 
22 
23         return false; 
24 
25     } 
26 
27  
28 
29     EqualityComparer<T> comparer = EqualityComparer<T>.Default; 
30 
31     for (int i = 0; i < this._size; i++) 
32 
33     { 
34 
35         if (comparer.Equals(this._items[i], item)) 
36 
37         { 
38 
39             return true; 
40 
41         } 
42 
43     } 
44 
45     return false; 
46 
47 } 
复制代码

 

 

Count 

Count方法将返回序列(集合)中元素的个数.这个方法的定义如下: 

public static int Count<TSource>(this IEnumerable<TSource> source) 

public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 

 

上面的两个重载方法将会做如下操作.第一种重载直接返回序列(集合)中元素的个数,第二种重载返回一个数字,表示在序列(集合)中有多少元素满足指定的条件。 

Count方法将找出在集合中一共有多少个项.例如在下面的程序中我创建了一个string类型的集合,我用Count()来找出在这个集合里有多少项以及有多少项是超过3个字符的. 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<string> listOne = new List<string>()  
24 
25             {  
26 
27                 "One","Two","Three" 
28 
29             }; 
30 
31  
32 
33             var result = listOne.Count(); 
34 
35  
36 
37             var fourOrMoreCharacters = listOne.Count(item => item.Length > 3); 
38 
39             Console.WriteLine("{0}\n{1}", result,fourOrMoreCharacters); 
40 
41         } 
42 
43     } 
44 
45 } 
复制代码

 

上面的程序会做如下输出: 

3 

1 

Press any key to continue . . . 

在这个例子中我使用了Count方法的两种重载方式. 

 

DefaultIfEmpty 

这个方法会返回一个IEnumerable<T>类型的元素或者当序列(集合)为空事放回一个默认的单例集合.这个方法的定义如下: 

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source) 

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source, TSource defaultValue) 

 

上面的两种重载方式将会这样做:第一种重载将返回指定的元素序列(集合)或者当序列(集合)为空的情况下返回一个单例集合中类型参数的默认值.第二种重载将返回指定的元素序列(集合)或者在序列(集合)为空的情况下返回一个单例集合中特定的值. 

这个方法可以用在一个不含有任何项的列表(集合)并且如果我们在这个列表中使用这个方法,它将返回缺省值.让我们看看在下面的程序中如何使用DefaultIfEmpty方法. 

复制代码
 1 using System.Collections; 
 2 
 3 using System.Collections.Generic; 
 4 
 5 using System.Linq; 
 6 
 7  
 8 
 9 namespace Chapter_5 
10 
11 { 
12 
13     class Program 
14 
15     { 
16 
17         static void Main(string[] args) 
18 
19         { 
20 
21  
22 
23             IList<Person> persons = new List<Person>(); 
24 
25             IList<int> numbers = new List<int>(); 
26 
27             IList<string> names = new List<string>(); 
28 
29  
30 
31             var defaultPersons = persons.DefaultIfEmpty(); 
32 
33  
34 
35             var defaultNumbers = numbers.DefaultIfEmpty().ToList(); 
36 
37  
38 
39             var defaultNames = names.DefaultIfEmpty(); 
40 
41         } 
42 
43     } 
44 
45  
46 
47     class Person 
48 
49     { 
50 
51         public string Name 
52 
53         { 
54 
55             get; 
56 
57             set; 
58 
59         } 
60 
61  
62 
63         public string Address 
64 
65         { 
66 
67             get; 
68 
69             set; 
70 
71         } 
72 
73  
74 
75         public int Age 
76 
77         { 
78 
79             get; 
80 
81             set; 
82 
83         } 
84 
85     } 
86 
87 } 
复制代码

 

在上面的程序中我声明了personnumbersnames三个集合,分别是Personintstring类型。这三个集合不包含任何项,它们的Count属性将会返回0.当我使用DefaultIfEmpty方法在任意一个集合上时,CLR会做如下步骤去执行。首先CLR会复制一份集合到DefaultIfEmpty方法,从这个方法中,CLR会返回一个包含defaultvalue和源值的DefaultIfEmptyIterator<TSource>迭代器的实例。The defaultvalue property will contain the default value of the type of list and source will be the original list. CLR会把DefaultIfEmptyItereator传递到ToList()方法中,它会通过把DefaultIfEmptyItereator作为输入参数调用集合类,在这个类中,CLR将遍历原始列表(集合)并处理结果。 

图像

DefaultIfEmptyTterator相近的代码如下: 

复制代码
 1 private static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue) 
 2 
 3 { 
 4 
 5     using (IEnumerator<TSource> iteratorVariable0 = source.GetEnumerator()) 
 6 
 7     { 
 8 
 9         if (iteratorVariable0.MoveNext()) 
10 
11             do 
12 
13             { 
14 
15                 yield return iteratorVariable0.Current; 
16 
17             } 
18 
19             while (iteratorVariable0.MoveNext()); 
20 
21         else 
22 
23             yield return defaultValue; 
24 
25     } 
26 
27 } 
复制代码

 

 

Distinct 

这个方法将从序列(集合)中返回去重复(Distinct)元素。这个方法的定义是: 

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source) 

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer) 

 

这个方法有两种重载方式,第一种重载通过默认比较器从序列(集合)中返回去重复的元素。第二种重载通过使用自定义IEquaityComparer<T>从序列(集合)中返回去重复的元素。 

这个方法将会从列表(集合)中返回同一的项。如果我们有一个包含重复项的列表(集合),那么这个方法将会过滤掉重复的项并返回一个只包含单一值的新列表(集合)。我们来看下面的程序。 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> numbers = new List<int>() 
24 
25             { 
26 
27                 1,1,1,2,2,2,3,3,3 
28 
29             }; 
30 
31  
32 
33             var distinctedNumbers = numbers.Distinct().ToList(); 
34 
35             distinctedNumbers.ForEach(x=>Console.WriteLine(x)); 
36 
37         } 
38 
39     } 
40 
41 } 
复制代码

 

程序会输出: 

1 

2 

3 

Press any key to continue . . . 

当上面程序运行的时候,它会只产生{123},下面的图表描述了Distinct方法如何工作。 

图像

要执行Distinct方法,CLR会这样做:第一,CLR会拷贝一份原始列表(集合)并作为输入参数传递到Distinct方法并且在内部调用Distinct<TSource>方法,这将返回一个DistinctIterator<TSource>类的实例,但是这个迭代器因为“延迟执行”不会执行(要执行DistinctIterator迭代器,我们需要调用ToList()方法或做ForEach)。第二,从ToList()方法,CLR会通过在第一步中创建的DistinctIterator作为输入参数调用列表(集合)类。这个列表(集合)类将会循环遍历DistinctIterator的实例。在DistinctIterator中的迭代逻辑会创建一个新的Set<TSource>实例并循环遍历原始列表然后将重复的项添加到先前创建的Set<TSource>实例中。内部的这个Set<TSource>类会使用AddFind方法从给定的序列(集合)中把项添加到内部的数组中直到数组里没有了重复项。这步操作会在CLR到达列表(集合)最后是才会停止并得到一个去重复的列表(集合)。因此,Distinct方法在list中会这样执行: 

复制代码
 1 private static IEnumerable<TSource> DistinctIterator<TSource>( 
 2 
 3     IEnumerable<TSource> source, IEqualityComparer<TSource> comparer) 
 4 
 5 { 
 6 
 7     Set<TSource> iteratorVariable0 = new Set<TSource>(comparer); 
 8 
 9     foreach (TSource iteratorVariable1 in source) 
10 
11     { 
12 
13         if (iteratorVariable0.Add(iteratorVariable1)) 
14 
15         { 
16 
17             yield return iteratorVariable1; 
18 
19         } 
20 
21     } 
22 
23 } 
复制代码

 

 

ElementAt 

在序列(集合),这个方法返回一个特定索引的一个元素.这个方法的定义是: 

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index) 

public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index) 

 

在下面的示例中使用了Ienumerable<TSource>ElementAt方法. 

复制代码
 1 using System; 
 2 
 3 using System.Collections.Generic; 
 4 
 5 using System.Linq; 
 6 
 7   
 8 
 9 namespace Chapter_5 
10 
11 { 
12 
13     class Program 
14 
15     { 
16 
17         static void Main(string[] args) 
18 
19         { 
20 
21             IList<string> numbers = new List<string>() 
22 
23             { 
24 
25                 "One","Two","Three" 
26 
27             }; 
28 
29   
30 
31             var elementAt = numbers.ElementAt(1); 
32 
33   
34 
35             Console.WriteLine(elementAt); 
36 
37         } 
38 
39     } 
40 
41 } 
复制代码

 

这个程序创建了一个包含OneTwoThree元素的数字集合。在这个数字列表中我尝试去访问在位置1处的元素并将结果存储到elementAt变量中,然后在控制台输出.这个程序的输出结果是: 

Two 

Press any key to continue . . . 

 

Empty 

Empty返回一个指定类型参数的空Ienumerable<T>. 

方法定义如下: 

public static IEnumerable<TResult> Empty<TResult>() 

 

下面的示例说明了Empty方法的用法. 

复制代码
 1 using System; 
 2 
 3 using System.Linq; 
 4 
 5  
 6 
 7 namespace Chapter_5 
 8 
 9 { 
10 
11     class Program 
12 
13     { 
14 
15         static void Main(string[] args) 
16 
17         { 
18 
19             var emptyList = Enumerable.Empty<int>(); 
20 
21  
22 
23             Console.WriteLine(emptyList.Count()); 
24 
25         } 
26 
27     } 
28 
29 } 
复制代码

 

在上面的代码中我用Empty()方法创建了一个int类型的空集合,程序会做如下输出: 

0 

Press any key to continue . . . 

 

Except 

Except方法可以用在从一个集合中删除一个项集合.它放回一个由两个序列产生的集合差.方法定义是: 

1 public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) 
2 
3 public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) 

 

第一种重载是利用默认的比较器生成两个序列的集合差.第二种重载是利用IEQualityComparer<T>生成两个序列的集合差.Except方法可以用在从一个集合中删除一个项集合.比如,我有个由{1,2,3,4,5,6,7}组成的集合A和由{1,2,3}组成的集合B,A 除了(except) B 将会输出{4,5,6,7},下面的程序说明了Except的用法. 

复制代码
 1 using System.Collections; 
 2 
 3 using System.Collections.Generic; 
 4 
 5 using System.Linq; 
 6 
 7 using System; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> firstNumbers = new List<int>() 
24 
25             { 
26 
27                 1,2,3,4,5,6,7 
28 
29             }; 
30 
31             IList<int> secondNumbers = new List<int>() 
32 
33             { 
34 
35                 1,2,3 
36 
37             }; 
38 
39  
40 
41             var result = firstNumbers.Except(secondNumbers).ToList(); 
42 
43             result.ForEach(x => Console.WriteLine(x)); 
44 
45         } 
46 
47     } 
48 
49 } 
复制代码

 

程序将会输出: 

4 

5 

6 

7 

Press any key to continue . . . 

当上面的程序执行的时候会产生{4,5,6,7},下面的图表展示了Except方法是如何工作的. 

图像

 

 

First 

这个方法会返回序列中的第一个元素.方法定义是: 

public static TSource First<TSource>(this IEnumerable<TSource> source) 

public static TSource First<TSource>(this IEnumerable<TSource> source,Func<TSource, bool> predicate) 

 

第一种重载将找出在序列中的第一个元素.第二种重载将根据条件找出第一个元素.我写了一个小程序去解释First方法的执行步骤. 

复制代码
 1 using System.Collections; 
 2 
 3 using System.Collections.Generic; 
 4 
 5 using System.Linq; 
 6 
 7 using System; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> numbers = new List<int>() 
24 
25             { 
26 
27                 1,2,3,4,5,6,7 
28 
29             }; 
30 
31  
32 
33             var firstItem = numbers.First(); 
34 
35             var firstItemBasedOnConditions = numbers.First(item => item > 3); 
36 
37  
38 
39             Console.WriteLine("{0}\n{1}", 
40 
41                 firstItem, 
42 
43                 firstItemBasedOnConditions 
44 
45                 ); 
46 
47         } 
48 
49     } 
50 
51 } 
复制代码

 

程序将会输出: 

1 

4 

Press any key to continue . . . 

 

FirstOrDefault 

它返回序列中第一个元素或者当没有元素未被找到时放回默认值.这个方法是FirstDefault的综合,这里不再叙述. 

 

Union 

这个方法将会Union(并集)两个序列(集合).例如,我们有两个集合,A={1,2,3,4,5,6,7}B={5,6,7,8,9},并集AB则返回{1,2,3,4,5,6,7,8,9}..NET,它将连接两个列表(集合)并生成一个新列表(集合). 

public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) 

public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first,IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) 

 

下面的程序说明了Union的用法. 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> firstList = new List<int>()  
24 
25             {  
26 
27                 1,2,3,4 
28 
29             }; 
30 
31  
32 
33             IList<int> secondList = new List<int>()  
34 
35             {  
36 
37                7,9,3,4,5,6,7 
38 
39             }; 
40 
41  
42 
43             var result = firstList.Union(secondList); 
44 
45             result.ToList().ForEach(x => Console.WriteLine(x)); 
46 
47         } 
48 
49     } 
50 
51 } 
复制代码

 

程序将会输出: 

1 

2 

3 

4 

7 

9 

5 

6 

Press any key to continue . . . 

 

 

Intersect 

它将产生两个序列的交集.方法定义是: 

复制代码
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) 

public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first,  

              Enumerable<TSource> second,IEqualityComparer<TSource> comparer) 
复制代码

 

第一种重载将会利用默认平等比较器来创建两个序列的交集,第二种重载是利用IEqualityComparet<T>比较器去比较值然后产生两个序列的交集.它产生两个序列的交集.这个交集将会包含在两个列表中相同的元素.例如,如果我们有一个A列表{1,2,3,4,5}B列表{4,5},取两者交集之后就会生成{4,5}. 

图像

看下面的程序 

复制代码
 1 using System; 
 2 
 3 using System.Collections.Generic; 
 4 
 5 using System.Linq; 
 6 
 7   
 8 
 9 namespace Chapter_5 
10 
11 { 
12 
13     class Program 
14 
15     { 
16 
17         static void Main(string[] args) 
18 
19         { 
20 
21             IList<int> listA = new List<int>() { 1, 2, 3, 4, 5 }; 
22 
23             IList<int> listB = new List<int>() { 4, 5 }; 
24 
25   
26 
27             var intersectResult = listA.Intersect(listB); 
28 
29   
30 
31             intersectResult.ToList().ForEach(x => Console.Write("{0}\t",x)); 
32 
33             Console.WriteLine(); 
34 
35         } 
36 
37     } 
38 
39 } 
复制代码

 

程序会输出: 

4       5 

Press any key to continue . . . 

 

 

Last 

它返回序列中最后一个元素.方法定义是: 

public static TSource Last<TSource>(this IEnumerable<TSource> source) 

public static TSource Last<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 

 

第一种重载将会找出在序列中最后一个元素,第二种重载将根据条件找出最后一个元素.我写了一个程序去解释这个方法如何使用: 

复制代码
 1 using System; 
 2 
 3 using System.Collections.Generic; 
 4 
 5 using System.Linq; 
 6 
 7 using System.Text; 
 8 
 9 using System.Threading.Tasks; 
10 
11  
12 
13 namespace Chapter1 
14 
15 { 
16 
17     class Program 
18 
19     { 
20 
21         static void Main(string[] args) 
22 
23         { 
24 
25             IList<int> numbers = new List<int>() 
26 
27             { 
28 
29                 1,2,3,4,5,6,7 
30 
31             }; 
32 
33  
34 
35             var lastItem = numbers.Last(); 
36 
37  
38 
39             var lastItemBasedOnConditions = numbers.Last(item => item > 3); 
40 
41  
42 
43             Console.WriteLine(lastItem); 
44 
45             Console.WriteLine(lastItemBasedOnConditions); 
46 
47         } 
48 
49     } 
50 
51 } 
复制代码

 

将会输出: 

7 

7 

Press any key to continue . . . 

 

 

LastOrDefault 

它返回序列中最后一个元素或者当没有元素未被找到时放回默认值.这个方法是LastDefault的综合,这里不再叙述. 

 

LongCount 

它会返回一个Int64去表示序列中元素的个数,方法定义是: 

public static long LongCount<TSource>(this IEnumerable<TSource> source) 

public static long LongCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 

 

第一种重载放回一个Int64,表示在序列中一共有多少个元素.第二种重载是根据条件放回一个Int64,表示在条件范围内一共有多少个元素. 

让我们做一个例子: 

复制代码
 1 using System; 
 2 
 3 using System.Collections; 
 4 
 5 using System.Collections.Generic; 
 6 
 7 using System.Linq; 
 8 
 9  
10 
11 namespace Chapter_5 
12 
13 { 
14 
15     class Program 
16 
17     { 
18 
19         static void Main(string[] args) 
20 
21         { 
22 
23             IList<int> firstList = new List<int>()  
24 
25             {  
26 
27                 1,2,3,4 
28 
29             }; 
30 
31  
32 
33             Console.WriteLine(firstList.LongCount()); 
34 
35         } 
36 
37     } 
38 
39 } 
复制代码

 




posted @ 2015-08-28 11:32  老皮肉  阅读(4625)  评论(0编辑  收藏  举报