生成器方法Generation Methods。集合运算符用语对两个sequence进行操作Zip运算符同步遍历两个sequence像一个拉链一样返回的sequence基于在每一个元素对上应用lambda表达式转换方法用来将实现了IEnumerableT的sequence转换到其他类型的集合或从其他类型的集合转换到sequence生成器方法/Generation Methods用来创建简单的本地sequence。集合运算符/Set OperatorsIEnumerableTSource, IEnumerableTSource→IEnumerableTSourceOperator说明SQL语义Concat连接两个sequences的所有元素UNION ALLUnion连接两个sequences的所有元素但去除重复的元素UNIONIntersect返回在两个sequence中都存在的元素WHERE ... IN (...)Except返回存在于第一个sequence而不存在于第二个sequence中的元素EXCEPTorWHERE ... NOT IN (...)Concat和UnionConcat返回第一个sequence中的所有元素后接第二个sequence中的所有元素即连接两个sequence。Union完成相同的工作但是会去除重复的元素。int[] seq1 { 1, 2, 3 }, seq2 { 3, 4, 5 }; IEnumerableint concat seq1.Concat(seq2); // { 1, 2, 3, 3, 4, 5 } IEnumerableint union seq1.Union(seq2); // { 1, 2, 3, 4, 5 }如果两个sequence的类型不同但是他们的元素共享同一个基类型时我们通常明确指定类型参数。比如在使用反射API时方法和属性分别用MethodInfo和PropertyInfo类型表示他们的共同基类是 MemberInfo。我们可以在调用Concat时明确指定基类型来连接方法和属性MethodInfo[] methods typeof(string).GetMethods(); PropertyInfo[] props typeof(string).GetProperties(); IEnumerableMemberInfo both methods.ConcatMemberInfo(props);下面的示例中我们在连接之前先对Methods进行了过滤var methods typeof(string).GetMethods().Where(m !m.IsSpecialName); var props typeof(string).GetProperties(); var both methods.ConcatMemberInfo(props);有意思的是这个示例可以在C# 4.0中编译但不能在C# 3.0中编译因为它依赖于接口类型参数协变methods的类型是IEnumerableMethodInfo在其转换为IEnumerableMemberInfo时需要C# 4.0提供的协变功能。这个例子很好的说明了协变如何让事情按我们期望的方式来工作。Intersect和ExceptIntersect返回两个sequence中都存在的元素Except返回存在于第一个sequence而不存在于第二个sequence中的元素int[] seq1 { 1, 2, 3 }, seq2 { 3, 4, 5 }; IEnumerableint commonality seq1.Intersect(seq2), // { 3 } difference1 seq1.Except(seq2), // { 1, 2 } difference2 seq2.Except(seq1); // { 4, 5 }Enumerable.Except的内部工作方式是把第一个sequence中的所有元素装载到一个dictionary中然后从这个dictionary中移除所有存在于第二个sequence中的元素。它等价于SQL中的NOT EXISTS或NOT IN子查询SELECT number FROM numbers1Table WHERE number NOT IN (SELECT number FROM numbers2Table)The Zip OperatorIEnumerableTFirst, IEnumerableTSecond→IEnumerableTResultZip运算符在Framework 4.0被添加进来。它同步遍历两个sequence像一个拉链一样返回的sequence基于在每一个元素对上应用lambda表达式。如下例int[] numbers { 3, 5, 7 }; string[] words { three, five, seven, ignored }; IEnumerablestring zip numbers.Zip (words, (n, w) n w); // 产生的sequence包含如下元素 // 3three // 5five // 7seven任何输入sequence中额外的元素不能组成元素对会被忽略。Zip运算符在查询数据库时不被支持。转换方法/Conversion MethodsLINQ主要用来处理sequences即IEnumerableT类型的集合。 转换方法用来将sequence转换到其他类型的集合或从其他类型的集合转换到sequence。方法说明OfType把IEnumerable转换到IEnumerableT丢弃错误的类型元素Cast把IEnumerable转换到IEnumerableT如果存在错误的类型元素则抛出异常ToArray把IEnumerableT转换到T[]ToList把IEnumerableT转换到ListTToDictionary把IEnumerableT转换到DictionaryTKey,TValueToLookup把IEnumerableT转换到ILookupTKey,TElementAsEnumerable向下转换到IEnumerableTAsQueryable转换到IQueryableTOfType和CastOfType和Cast接收一个非泛型的IEnumerable集合返回一个泛型的IEnumerableT这样我们就可以对其进行查询了。ArrayList classicList new ArrayList(); // in System.Collections classicList.AddRange(new int[] { 3, 4, 5 }); IEnumerableint sequence1 classicList.Castint();Cast和OfType的不同行为表现在当遇到一个类型不兼容的输入元素时Cast抛出一个异常而OfType忽略不兼容的元素。继续上一个示例DateTime offender DateTime.Now; classicList.Add(offender); IEnumerableint sequence2 classicList.OfTypeint(), // OK - ignores offending DateTime sequence3 classicList.Castint(); // Throws exception判断元素类型是否兼容的规则与C#的is操作符一致我们可以通过OfType的内部实现来进行验证public static IEnumerableTSource OfTypeTSource(IEnumerable source) { foreach (object element in source) if (element is TSource) yield return (TSource)element; }Cast具有相同的实现除了它里面省略了类型兼容性的检测public static IEnumerableTSource CastTSource(IEnumerable source) { foreach (object element in source) yield return (TSource)element; }这些实现的一个结果是我们无法使用Cast来进行数值或定制的转换对这些我们必须使用Select运算符。换句话说Cast不具备C#中cast操作符的灵活性int i 3; long l i; // 隐式数值转换 int-long int i2 (int)l; // 显式数值转换 long-int现在我们再来看看OfType和Cast转换int sequence到long sequence的行为int[] integers { 1, 2, 3 }; IEnumerablelong test1 integers.OfTypelong(); IEnumerablelong test2 integers.Castlong();当我们遍历结果时test1产生0个元素的sequence而test2会抛出异常。我们再次审视OfType的实现原因显而易见。用int代入TSource之后下面的表达式(element is long)返回false因为int和long之间并没有继承关系。而test2抛出异常的原因就没那么明显了注意Cast的实现中元素的类型为object。当TSource是一个值类型时 CLR认为这是一个拆箱转换。而拆箱操作要求精确的类型匹配所以当TSource是一个int时object-to-long拆箱会失败并抛出异常。可以通过下面的示例进行验证int value 123; object element value; long result (long)element; // exception正如我们前面的建议此问题的解决方案是使用SelectIEnumerablelong castLong integers.Select(s (long)s);当我们需要把一个泛型的输入sequence中的元素向下转换转为更具体的类型时OfType和Cast也会非常有用。比如如果我们有一个IEnumerableFruit的输入sequenceOfTypeApple仅返回apples这在我们稍后会讲到的LINQ to XML中会特别有效。Cast有对应的查询表达式语法支持只需在范围变量之前指定一个类型即可var query from TreeNode node in myTreeView.Nodes ...ToArray, ToList, ToDictionary, 和ToLookupToArray和ToList分别把结果输出到一个数组或泛型列表。这些运算符会强制对输入sequence进行立即遍历除非间接通过子查询或表达式树。关于他们的示例请参考LINQ之路 6延迟执行(Deferred Execution)ToDictionary和ToLookup接受如下参数参数类型输入sequence / Input sequenceIEnumerableTSource键选择器 / Key selectorTSource TKey元素选择器 / Element selector(optional)TSource TElement比较器 / Comparer (optional)IEqualityComparerTKey