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.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 } 

时间: 2024-11-23 12:00:14

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

C# LINQ详解(转)

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 Inte

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. 概述 LINQ的全称是Language Integrated Query,中文译成"语言集成查询".LINQ作为一种查询技术,首先要解决数据源的封装,大致使用了三大组件来实现这个封装,分别是LINQ to Object.LINQ to ADO.NET.LINQ to XML.它们和.NET

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频

Linq实战 之 DataSet操作详解

Linq实战 之 DataSet操作详解  一:linq to Ado.Net 1. linq为什么要扩展ado.net,原因在于给既有代码增加福利.FCL中在ado.net上扩展了一些方法. 简单一点的说: 就是在DatTable 和 DataRow 上面做了一些扩展. 二:扩展方法一览 1. AsEnumerable 2. Field 三:扩展类一览 DataTableExtensions 扩展 => public static EnumerableRowCollection<DataRo

Linq实战 之 Linq to Sql及Entity Framework操作详解

Linq实战 之 Linq to Sql及Entity Framework操作详解 一:linq to db的框架 1. linq to sql 2. linq to ado.net entity framework linq to sql是一个团队 ef 是一个团队... linq to sql => ef 团队. linq to sql 适合一些小型的项目 => sqlserver ef 适合中形的项目,而且可以支持 sqllite,mysql,sqlserver 掌柜的项目开发中:使用的

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

本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to Entities 使用LINQ to Entities操作实体 LINQ操作 懒加载和预加载 插入数据 更新数据 删除数据 本章小结 本人的实验环境是VS 2013 Update 5,windows 10,MSSQL Server 2008. 上一篇<Code First开发系列之领域建模和管理实体关系>,我们主要介绍了EF中“约定大于配置”的概念,如何创建数据

详解Linq to SQL

第一部分,什么是Linq to sql Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据,并提供了丰富的查询功能,它和Linq to xml.Linq to objects.Linq to dataset.Linq to entities等组成了强大的LINQ. 要学好LINQ查询语法,就不得不先理解C# 3.0的一些新特性,下面一一简单介绍. 第二部分,隐含类型局部变量 var age

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

示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集成查询).通过LINQ,我们可以使用相同API操作不同的数据源.接下来就让我们看看LINQ是什么以及如何使用? 再此之前,需要先了解的相关技术 1. 隐式类型.匿名类型.对象初始化器 1) 隐式类型,使用var关键字创建,C#编译器会根据用于初始化局部变量的初始值推断出变量的数据类型.(不过我个人认

Linq之查询表达式语法详解

1.闲言碎语 由于项目的需要接触到Linq,刚开始有些不适应,好多概念都很模糊.不过经过一段时间的摸索,慢慢地对Linq有了一个更加深入的了解.在此记录一下备忘.      2.查询表达式语法 执行Linq有两种方式,一种是方法形式eg:names.Contains('K');,另一种就是查询表达式eg:var query=from n in names select n; 下面用代码来详细解释. string[] fruitName = { "Apple", "Pear&q