原文标题: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会(common language runtime公共语言运行时)通过数字列表作为输入参数传递到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,表示在条件范围内一共有多少个元素.
让我们做一个例子:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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 }