65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 讲座 - 第 11 讲:LINQ to 0bjects 第二部分:非延迟运算符

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8投票s)

2016 年 9 月 13 日

CPOL

11分钟阅读

viewsIcon

10053

downloadIcon

189

C# 系列的下一篇文章。继续介绍 LINQ to Objects 的 LINQ 运算符

全部课程集

引言

这是关于 LINQ to Objects 的第二篇文章,也是关于该主题的第一篇文章的延续。在这里,我将专注于回顾 LINQ 中提供的其余运算符,并重点介绍非延迟运算符。

非延迟运算符

在上一篇文章中,我回顾了延迟运算符。如果您查看它们,您会发现它们都返回 IEnumerable<T> 或 OrderedSequence<T>。与延迟运算符相反,非延迟运算符返回的不是 IEnumerable<T> 或 OrderedSequence<T> 的数据类型。

 

让我们回顾一下当前可用的所有非延迟运算符

在大多数示例中,我们将使用以下数据结构,并在其上调用要回顾的运算符

            //LINQ TO OBJECTS
            string[] CarBrands = { "Mersedes", "Ford", "Lexus", "Toyota",
                                   "Honda", "Hyunday", "BMW", "KIA", "Chevrolet",
                                   "Tesla", "Lamborghini", "Ferrari", "Lincoln",
                                    "Cadillac"};
  • ToArray - 创建与输入序列类型相同的数组。其原型如下:
    • public static T[] ToArray<T>(this IEnumerable<T> source); - 此函数接受类型为 T 的输入序列,并将其转换为类型为 T 的数组。

     代码

            Console.WriteLine("\n-----------------TOARRAY");
            List<string> Names = new List<string>();
            Names.Add("Sergey");
            Names.Add("Alexander");
            Names.Add("Maria");
            Names.Add("John");
            Names.Add("Bruce");
            Names.Add("Emily");
            Console.WriteLine("Names type = " + Names.GetType().ToString());
            var newNames = Names.ToArray();
            Console.WriteLine("newNames type = " + newNames.GetType().ToString());

     结果

  • ToList - 将类型为 T 的输入序列转换为相同类型的列表。其原型如下:
    • public static List<T> ToList<T>(this IEnumerable<T> source); - 此函数接受类型为 T 的输入序列,并将其转换为类型为 T 的列表。

     代码

            Console.WriteLine("\n-----------------TOLIST");
            Console.WriteLine("CarBrands type = " + CarBrands.GetType().ToString());
            var newCarBrands = CarBrands.ToList();
            Console.WriteLine("newCarBrands type = " + newCarBrands.GetType().ToString());

     结果

  • ToDictionary - 将类型为 T 的输入序列转换为类型为 T、键为 K 的字典。这里有 4 种原型
    • public static Dictionary<K, T> ToDictionary<T, K>(this IEnumerable<T> source,Func<T, K> keySelector); - 此函数接受类型为 T 的输入序列和一个委托 - keySelector,用于从每个序列元素中提取键,并将其用作返回的字典中的键。返回的字典与输入序列的类型相同。在返回的字典中,使用默认比较器 EqualityComparer<K>.Default 来比较键。
    • public static Dictionary<K, T> ToDictionary<T, K>(this IEnumerable<T> source,Func<T, K> keySelector,IEqualityComparer<K> comparer); - 此原型与上一个原型完全相同,但它允许您提供一个实现 IEqualityComparer<K> 接口的比较器对象来比较字典键。
    • public static Dictionary<K, E> ToDictionary<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector); - 此原型与第一个原型完全相同,但它允许您提供一个 elementSelector 委托,该委托会将序列的输入元素从类型 T 转换为类型 E。结果字典将包含类型为 K 的键和类型为 E 的元素。
    • public static Dictionary<K, E> ToDictionary<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector,IEqualityComparer<K> comparer); - 第四个原型是最通用的。它是第二个和第三个选项的组合。在这里,您可以实现自己的字典键比较器,并实现 elementSelector 将输入序列的元素转换为存储在结果字典中的不同类型。

     代码

            Console.WriteLine("\n-----------------TODICTIONARY");
            Console.WriteLine("-----------------first version");
            Dictionary<int, string> outDict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s));
            foreach (var element in outDict)
            {
                Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
            }
            Console.WriteLine("-----------------second version");
            IntComparer comp = new IntComparer();
            outDict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s),comp);
            foreach (var element in outDict)
            {
                Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
            }
            Console.WriteLine("-----------------third version");
            Dictionary<int, int> dict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s),el => el.Length);
            foreach (var element in dict)
            {
                Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
            }
            Console.WriteLine("-----------------fourth version");
            dict = CarBrands.ToDictionary(s => Array.IndexOf(CarBrands, s), el => el.Length,comp);
            foreach (var element in dict)
            {
                Console.WriteLine("Key = " + element.Key + " Value = " + element.Value);
            

     结果

  • ToLookup - 从输入序列创建查找。它也有 4 种原型
    • public static ILookup<K, T> ToLookup<T, K>(this IEnumerable<T> source,Func<T, K> keySelector); - 此函数接受类型为 T 的输入序列和一个委托 - keySelector,用于从每个序列元素中提取键,并将其用作返回的查找中的键。返回的字典与输入序列的类型相同。在返回的字典中,使用默认比较器 EqualityComparer<K>.Default 来比较键。
    • public static ILookup<K, T> ToLookup<T, K>(this IEnumerable<T> source,Func<T, K> keySelector,IEqualityComparer<K> comparer); - 此原型与上一个原型完全相同,但它允许您提供一个实现 IEqualityComparer<K> 接口的比较器对象来比较查找键。
    • public static ILookup<K, E> ToLookup<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector); - 此原型与第一个原型完全相同,但它允许您提供一个 elementSelector 委托,该委托会将序列的输入元素从类型 T 转换为类型 E。结果查找将包含类型为 K 的键和类型为 E 的元素。
    • public static ILookup<K, E> ToLookup<T, K, E>(this IEnumerable<T> source,Func<T, K> keySelector,Func<T, E> elementSelector,IEqualityComparer<K> comparer); - 第四个原型是最通用的。它是第二个和第三个选项的组合。在这里,您可以实现自己的查找键比较器,并实现 elementSelector 将输入序列的元素转换为存储在结果查找中的不同类型。

     代码

            Console.WriteLine("\n-----------------TOLOOKUP");
            Console.WriteLine("-----------------first version");
            ILookup<int, string> outLook = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s));
            IEnumerable<string> car = outLook[4];
            foreach (var str in car)
            {
                Console.WriteLine("Fifth car is = " + str);
            }
            Console.WriteLine("-----------------second version");
            outLook = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s),comp);
            car = outLook[3];
            foreach (var str in car)
            {
                Console.WriteLine("Fourth car is = " + str);
            }
            Console.WriteLine("-----------------third version");
            ILookup<int, int> outLook2 = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s),el => el.Length);
            IEnumerable<int> carl = outLook2[4];
            foreach (var i in carl)
            {
                Console.WriteLine("Fifth car lengh is = " + i);
            }
            Console.WriteLine("-----------------fourth version");
            outLook2 = CarBrands.ToLookup(s => Array.IndexOf(CarBrands, s), el => el.Length,comp);
            carl = outLook2[3];
            foreach (var i in carl)
            {
                Console.WriteLine("Fourth car lengh is = " + i);
            }

     结果

  • SequenceEqual - 此运算符确定两个输入序列是否相等。它有两个原型
    • public static bool SequenceEqual<T>(this IEnumerable<T> first,IEnumerable<T> second); - 此函数使用 System.Object.Equals 方法比较输入序列的所有元素。如果所有元素都相等,则返回 true。
    • public static bool SequenceEqual<T>(this IEnumerable<T> first,IEnumerable<T> second,IEqualityComparer<T> comparer); - 此函数与第一个函数相同,但它允许您定义自己的序列元素比较器。

     代码

            Console.WriteLine("\n-----------------SEQUENCEEQUAL");
            Console.WriteLine("-----------------first version");
            int[] seq1 = { 0, 5, 25 };
            int[] seq2 = { 0, 5, 25 };
            string[] Cars2 = { "Ford", "Acura"};
            Console.WriteLine(CarBrands.SequenceEqual(Cars2));//prints false
            Console.WriteLine(seq1.SequenceEqual(seq2));//prints true
            Console.WriteLine("-----------------second version");
            Console.WriteLine(seq1.SequenceEqual(seq2,comp));//prints true

     结果

  • First - 返回序列的第一个元素或匹配输入谓词的元素。如果在空序列上调用它,它将引发 System.InvalidOperationException 异常。它有两个原型
    • public static T First<T>(this IEnumerable<T> source); - 仅返回序列的第一个元素
    • public static T First<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 从序列中返回与谓词匹配的第一个元素

     代码

            Console.WriteLine("\n-----------------FIRST");
            Console.WriteLine("-----------------first version");
            Console.WriteLine(CarBrands.First());//prints Merseds
            Console.WriteLine("-----------------second version");
            Console.WriteLine(CarBrands.First(s=>s.StartsWith("BM")));//prints BMW

     结果

  • FirstOrDefault - 返回序列的第一个元素或匹配输入谓词的元素。它类似于 First 运算符,但如果序列为空,则返回 default(T)。如您所记得的,对于所有引用类型,默认值为 null。它有两个原型
    • public static T FirstOrDefault<T>(this IEnumerable<T> source); - 仅返回序列的第一个元素
    • public static T FirstOrDefault<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 从序列中返回与谓词匹配的第一个元素

     代码

            Console.WriteLine("\n-----------------FIRSTORDEFAULT");
            Console.WriteLine("-----------------first version");
            Console.WriteLine(CarBrands.FirstOrDefault());//prints Merseds
            string [] seqstr = {};
            try 
            {
                Console.WriteLine(seqstr.First());
            }
            catch (System.InvalidOperationException e) 
            {
                Console.WriteLine("Fisrt operator on empty sequence raises exception");
            }
            if(String.IsNullOrEmpty(seqstr.FirstOrDefault()))
            {
                Console.WriteLine("We got default value of string = null");
            }
            Console.WriteLine("-----------------second version");
            Console.WriteLine(CarBrands.FirstOrDefault(s => s.StartsWith("BM")));//prints BMW

     结果

  • Last - 返回序列的最后一个元素或匹配输入谓词的元素。如果在空序列上调用它,它将引发 System.InvalidOperationException 异常。它有两个原型
    • public static T Last<T>(this IEnumerable<T> source); - 仅返回序列的第一个元素
    • public static T Last<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 从序列中返回与谓词匹配的第一个元素

     代码

            Console.WriteLine("\n-----------------LAST");
            Console.WriteLine("-----------------first version");
            Console.WriteLine(CarBrands.Last());//prints Cadillac
            Console.WriteLine("-----------------second version");
            Console.WriteLine(CarBrands.Last(s => s.StartsWith("L")));//prints Lincoln

     结果

  • LastOrDefault - 返回序列的最后一个元素或匹配输入谓词的元素。它类似于 Last 运算符,但如果序列为空,则返回 default(T)。如您所记得的,对于所有引用类型,默认值为 null。它有两个原型
    • public static T LastOrDefault<T>(this IEnumerable<T> source); - 仅返回序列的最后一个元素
    • public static T LastOrDefault<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 从序列中返回与谓词匹配的最后一个元素

     代码

            Console.WriteLine("\n-----------------LASTORDEFAULT");
            Console.WriteLine("-----------------first version");
            Console.WriteLine(CarBrands.LastOrDefault());//prints Cadillac
            try
            {
                Console.WriteLine(seqstr.Last());
            }
            catch (System.InvalidOperationException e)
            {
                Console.WriteLine("Last operator on empty sequence raises exception");
            }
            if (String.IsNullOrEmpty(seqstr.LastOrDefault()))
            {
                Console.WriteLine("We got default value of string = null");
            }
            Console.WriteLine("-----------------second version");
            Console.WriteLine(CarBrands.LastOrDefault(s => s.StartsWith("L")));//prints Lincoln

     结果

  • Single - 返回仅包含一个元素的输入序列的单个元素,或返回其中的单个元素。它有两个原型
    • public static T Single<T>(this IEnumerable<T> source); - 仅返回序列的最后一个元素
    • public static T Single<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 从序列中返回与谓词匹配的最后一个元素

     代码

            Console.WriteLine("\n-----------------SINGLE");
            Console.WriteLine("-----------------first version");
            string[] singleElementSequence = { "single one"};
            Console.WriteLine(singleElementSequence.Single());//prints - single one
            Console.WriteLine("-----------------second version");
            Console.WriteLine(CarBrands.Single(s => s.StartsWith("Lin")));//prints Lincoln

     结果

  • SingleOrDefault - 与前一个运算符类似,它返回仅包含一个元素的输入序列的单个元素,或返回其中的单个元素。唯一的区别是当找不到搜索元素时的行为方式,在这种情况下,它返回 default(T)。它有两个原型
    • public static T SingleOrDefault<T>(this IEnumerable<T> source); - 仅返回序列的最后一个元素
    • public static T SingleOrDefault<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 从序列中返回与谓词匹配的最后一个元素

     代码

            Console.WriteLine("\n-----------------SINGLEORDEFAULT");
            Console.WriteLine("-----------------first version");
            string[] singleElementSequence2 = {};
            if (String.IsNullOrEmpty(singleElementSequence2.SingleOrDefault()))
            {
                Console.WriteLine("We got default value of string = null");
            }
            Console.WriteLine("-----------------second version");
            if (String.IsNullOrEmpty(singleElementSequence2.SingleOrDefault(s=> s.StartsWith("strange string"))))
            {
                Console.WriteLine("We again got default value of string = null");
            }

     结果

  • ElementAt - 返回源在序列中特定位置的元素。它有两个原型
    • public static T ElementAt<T>(this IEnumerable<T> source,int index); - 接受输入索引并返回该位置的元素

     代码

            Console.WriteLine("\n-----------------ELEMENTAT");
            Console.WriteLine("Element at position 2 is: " + CarBrands.ElementAt(2));//prints - Lexus

     结果

  • - 与前一个运算符类似,它返回输入序列特定索引的元素,或返回其中的单个元素。唯一的区别是当输入索引错误或输入序列为 null 时的行为方式,在这种情况下,它返回 default(T)。它有一个原型
    • public static T ElementAtOrDefault<T>(this IEnumerable<T> source,int index); - 仅返回序列的最后一个元素

     代码

            Console.WriteLine("\n-----------------ELEMENTATORDEFAULT");
            if (String.IsNullOrEmpty(CarBrands.ElementAtOrDefault(-1)))
            {
                Console.WriteLine("We got default value of string = null");
            }

     结果

  • Any - 如果输入序列中的任何元素匹配条件,则返回 true。它有两个原型
    • public static bool Any<T>(this IEnumerable<T> source); - 此函数在序列中至少存在任何元素时返回 true
    • public static bool Any<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 如果输入序列中的至少一个元素导致谓词返回 true,则返回 true

     代码

            Console.WriteLine("\n-----------------ANY");
            Console.WriteLine("-----------------first version");
            string[] emptySequence = { };
            if (!emptySequence.Any())
            {
                Console.WriteLine("Input sequence is empty");
            }
            Console.WriteLine("Has CarBrands any elements? : " + CarBrands.Any());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("Does car brands has something that starts with B: " + CarBrands.Any(s=> s.StartsWith("B")));

     结果

  • All - 如果输入序列中的所有元素都匹配条件,则返回 true。它具有以下原型
    • public static bool All<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 如果输入序列中的所有元素都导致谓词返回 true,则返回 true

     代码

            Console.WriteLine("\n-----------------ALL");
            Console.WriteLine("Do all elements of CarBrands has more than 3 symbols: " + CarBrands.All(s => {if(s.Length>3)return true;return false;}));
            Console.WriteLine("Do all elements of CarBrands has more than 2 symbols: " + CarBrands.All(s => { if (s.Length > 2)return true; return false; }));

     结果

  • Contains - 如果输入序列中的任何元素包含输入值,则返回 true。它具有以下原型
    • public static bool Contains<T>(this IEnumerable<T> source,T value); - 如果输入序列中的任何元素与输入值匹配,则返回 true
    • public static bool Contains<T>(this IEnumerable<T> source,T value, IEqualityComparer<T> comparer); - 与前一个版本相同,但可以定义自己的比较器

     代码

            Console.WriteLine("\n-----------------Contains");
            Console.WriteLine("-----------------first version");
            Console.WriteLine("Does CarBrands contain BMW: " + CarBrands.Contains("BMW"));
            int[] ints = { 0, 1, 2, 3, 4, 5 };
            Console.WriteLine("-----------------second version");
            Console.WriteLine("Does ints contain 5 with comparer: " + ints.Contains(5,comp));

     结果

  • Count - 返回输入序列中的元素数量
    • public static int Count<T>(this IEnumerable<T> source);
    • public static int Count<T>(this IEnumerable<T> source,Func<T, bool> predicate); - 如果第一个原型非常直接,那么第二个就非常有趣了。它返回输入序列中与特定谓词条件匹配的元素数量。我将此函数视为实际编程中最方便和最有用的函数之一,因为它大大简化了您的生活。

     代码

            Console.WriteLine("\n-----------------Count");
            Console.WriteLine("-----------------first version");
            Console.WriteLine("CarBrands count: " + CarBrands.Count());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("CarBrands count where length > 4: " + CarBrands.Count(s=> s.Length > 4));

     结果

  • LongCount - 以 long 类型返回输入序列中的元素数量。与 count 类似,也有两个原型
    • public static long LongCount<T>(this IEnumerable<T> source);
    • public static long LongCount<T>(this IEnumerable<T> source,Func<T, bool> predicate);

     代码

            Console.WriteLine("\n-----------------LongCount");
            Console.WriteLine("-----------------first version");
            Console.WriteLine("CarBrands count: " + CarBrands.LongCount());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("CarBrands count where length > 5: " + CarBrands.LongCount(s => s.Length > 5));

     结果

   

  • Sum - 返回包含数值(int、long、double 或 decimal)的输入序列元素的总和。它具有以下原型
    • public static Numeric Sum(this IEnumerable<Numeric> source);
    • public static Numeric Sum<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - 此函数允许您实现一个 selector 委托,该委托仅选择您想要的值,在这种情况下,您可以将其应用于非 Numeric 序列

     代码

            Console.WriteLine("\n-----------------Sum");
            double[] doubles = { 0.1, 1.2, 2.3, 3.5, 4.6, 5.3 };
            Console.WriteLine("-----------------first version");
            Console.WriteLine("Sum of doubles is: " + doubles.Sum());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("Sum of chars in CarBrands is: " + CarBrands.Sum(s=> s.Length));

     结果

  • Min - 返回输入序列中的最小值。它具有以下原型
    • public static Numeric Min(this IEnumerable<Numeric> source); - 最简单的数值序列。
    • public static T Min<T>(this IEnumerable<T> source); - 与第一个相同,但适用于非数值类型
    • public static Numeric Min<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - 可以应用于非数值序列,但选择器应从该序列中提取某个数值字段。
    • public static S Min<T, S>(this IEnumerable<T> source,Func<T, S> selector); - 此函数类似于第二个,但允许您使用选择器委托

     代码

            Console.WriteLine("\n-----------------Min");
            Console.WriteLine("-----------------first version");
            Console.WriteLine("Min of doubles is: " + doubles.Min());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("Min in CarBrands: " + CarBrands.Min());
            Console.WriteLine("-----------------third version");
            Console.WriteLine("Min in CarBrands by length is: " + CarBrands.Min(s => s.Length));
            Console.WriteLine("-----------------fourth version");
            Console.WriteLine("Min in CarBrands (we imagine this is class and return string value  for name from it) : " + CarBrands.Min(s => s.ToString()));

     结果

  • Max - 返回输入序列中的最大值。它具有以下原型
    • public static Numeric Max(this IEnumerable<Numeric> source); - 最简单的数值序列。
    • public static T Max<T>(this IEnumerable<T> source); - 与第一个相同,但适用于非数值类型
    • public static Numeric Max<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - 可以应用于非数值序列,但选择器应从该序列中提取某个数值字段。
    • public static S Max<T, S>(this IEnumerable<T> source,Func<T, S> selector); - 此函数类似于第二个,但允许您使用选择器委托

     代码

            Console.WriteLine("\n-----------------Max");
            Console.WriteLine("-----------------first version");
            Console.WriteLine("Mxn of doubles is: " + doubles.Max());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("Max in CarBrands: " + CarBrands.Max());
            Console.WriteLine("-----------------third version");
            Console.WriteLine("Max in CarBrands by length is: " + CarBrands.Max(s => s.Length));
            Console.WriteLine("-----------------fourth version");
            Console.WriteLine("Max in CarBrands (we imagine this is class and return string value  for name from it) : " + CarBrands.Max(s => s.ToString()));

     结果

  • Average - 返回包含数值(int、long、double 或 decimal)的输入序列元素的平均值。它具有以下原型
    • public static Numeric Average(this IEnumerable<Numeric> source);
    • public static Numeric Average<T>(this IEnumerable<T> source,Func<T, Numeric> selector); - 此函数允许您实现一个 selector 委托,该委托仅选择您想要的值,在这种情况下,您可以将其应用于非 Numeric 序列

     代码

            Console.WriteLine("\n-----------------Average");
            Console.WriteLine("-----------------first version");
            Console.WriteLine("Average of doubles is: " + doubles.Average());
            Console.WriteLine("-----------------second version");
            Console.WriteLine("Average of lengths in CarBrands is: " + CarBrands.Average(s => s.Length));

     结果

结论

 在使用非延迟运算符时,您应该非常小心。大多数运算符以所谓的“良好”方式工作。一旦您输入错误的数据或运算符无法正确处理数据,它就会引发异常。在决定使用某个运算符之前,请打开其文档,确保您了解极端情况以及它如何处理问题情况,以及在这些情况下会生成哪些异常或返回值。

来源

  1. Pro LINQ Language Integrated Query in C# 2010 Adam Freeman and Joseph C. Rattz, Jr.
  2. https://msdn.microsoft.com
  3. https://codeproject.org.cn/Articles

 

© . All rights reserved.