原文标题: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.dll中System.Collections.Generic命名空间下的Ienumerable<T>接口得到的. 映射可以访问所有的定义在System.Core.dll中System.Linq命名空间下的枚举类.这个枚举类是定义在System.Core.dll中System.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

那么来让我们看一个关于Where和Select的例子.

 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) 

这个方法被用在判断是否一个元素序列满足某种条件。序列中的每一个元素都会经过判断。在下面的程序中,我创建了一个包含One、Two、Three等项目的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通过将listOne和listTwo作为输入参数传递到Concat方法中,并且从Concat方法中返回一个ConcatIterator实例作为输出参数给这个方法的调用者.虽然这将通过延迟执行模式来执行,但是ToList()方法也会通过listOne和listTwo的ConcatIterator类的执行逻辑来开始处理并得到最终的集合。

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 }

在上面的程序中我声明了person、numbers、names三个集合,分别是Person、int和string类型。这三个集合不包含任何项,它们的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 . . .

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

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

这个程序创建了一个包含One、Two、Three元素的数字集合。在这个数字列表中我尝试去访问在位置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

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

Union

这个方法将会Union(并集)两个序列(集合).例如,我们有两个集合,A={1,2,3,4,5,6,7}和B={5,6,7,8,9},并集A和B则返回{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

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

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 }

C# LINQ详解(一)的更多相关文章

  1. C# LINQ详解(转)

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

  2. LINQ 详解

    LINQ,语言集成查询(Language INtegrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作 ...

  3. C# LINQ 详解 From Where Select Group Into OrderBy Let Join

    目录 1. 概述 2. from子句 3. where子句 4. select子句 5. group子句 6. into子句 7. 排序子句 8. let子句 9. join子句 10. 小结 1. ...

  4. C# 基础知识系列-7 Linq详解

    前言 在上一篇中简单介绍了Linq的入门级用法,这一篇尝试讲解一些更加深入的使用方法,与前一篇的结构不一样的地方是,这一篇我会先介绍Linq里的支持方法,然后以实际需求为引导,分别以方法链的形式和类S ...

  5. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  6. Code First开发系列之管理数据库创建,填充种子数据以及LINQ操作详解

    返回<8天掌握EF的Code First开发>总目录 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to ...

  7. 8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

    本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来. 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LI ...

  8. C#中的Linq to Xml详解

    这篇文章主要介绍了C#中的Linq to Xml详解,本文给出转换步骤以及大量实例,讲解了生成xml.查询并修改xml.监听xml事件.处理xml流等内容,需要的朋友可以参考下 一.生成Xml 为了能 ...

  9. Linq之查询表达式语法详解

    1.闲言碎语 由于项目的需要接触到Linq,刚开始有些不适应,好多概念都很模糊.不过经过一段时间的摸索,慢慢地对Linq有了一个更加深入的了解.在此记录一下备忘.      2.查询表达式语法 执行L ...

随机推荐

  1. Linux使用wake_up_interruptible()唤醒注册到等待队列上的进程

    http://blog.sina.com.cn/s/blog_4770ef020101h48l.html     功能:唤醒注册到等待队列上的进程 原型:     #include     void ...

  2. SQLite入门与分析(九)---VACUUM命令分析

    VACUUM命令是SQLite的一个扩展功能,模仿PostgreSQL中的相同命令而来.若调用VACUUM带一个表名或索引名, 则将整理该表或索引.在SQLite 1.0中,VACUUM命令调用 gd ...

  3. Little-endian的一个好处:在变量指针转换的时候地址保持不变

    Big-endian 的内存顺序和数字的书写顺序是一致的,方便阅读理解.Little-endian 在变量指针转换的时候地址保持不变,比如 int64* 转到 int32* 各有利弊,统一就好,目前看 ...

  4. (转载)NET流操作

    http://www.oseye.net/user/kevin/blog/86 概念 数据流(Stream)是对串行传输数据的一种抽象表示,是对输入/输出的一种抽象.数据有来源和目的地,衔接两者的就是 ...

  5. 关于捕获键盘信息的processDialogkey方法

    在一些控件里的keydown方法,没有办法捕获所有的按键消息 比如自己写一个窗体控件库,继承了UserControl 但是没有办法捕获一些键,比如方向键等 所以必须重载 processDialogke ...

  6. IPAddress

    Console.WriteLine("BitConverter.IsLittleEndian = {0}", BitConverter.IsLittleEndian); Conso ...

  7. BOM List demo

    select level level_id,        t.*   from (select msi1.segment1 farther_item,                msi1.inv ...

  8. Android开发之BroadcastReceiver的使用

    1.静态注册. 在manifest中注册. <receiver android:name="com.exce.learnbroadcastreceiver.MyReceiver&quo ...

  9. ORA-12545:Connect failed beacuse target host or object does not exist

    更换计算机名,重新启动系统后 oracle 的监听器就无法正常启动, 总是提示ORA-12545:Connect failed beacuse target host or object does n ...

  10. c#浅谈反射内存的处理

    这段时间由于公司的项目的要求,我利用c#的反射的机制做了一个客户端框架.客户端里的所有的模块都是以一定形式进行提供,例如:FORM,UserControl. 在做的过程中很简单与愉快.具体的过程如下: ...