• LINQ查漏补缺之运算符


    LINQ运算符

    LINQ 过滤运算符 Where

    LINQ中的过滤运算符根据某些给定的标准过滤序列(集合)。

    下表列出了LINQ中所有可用的过滤运算符。

    筛选运算符 描述
    Where 根据谓词函数从集合中返回值。
    OfType 根据指定类型返回集合中的值。 然而,它取决于它们是否能够向指定类型转换。

    Where运算符(Linq扩展方法)基于给定的条件表达式过滤集合并返回新集合。可以将标准指定为lambda表达式或Func委托类型。

    Where扩展方法有以下两个重载。两种重载方法都接受Func委托类型参数。一个重载需要Func <TSource,bool>输入参数,第二个重载方法需要Func <TSource,int,bool>输入参数,其中int用于索引:

    Where方法重载:

    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);
    

    查询语法中的Where子句

    下面的查询示例使用Where运算符从给定的集合(序列)中筛选出青少年的学生。它使用lambda表达式作为谓词函数。

    示例:C# Where子句-LINQ查询语法

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
            new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 } 
        };
    var filteredResult = from s in studentList
                        where s.Age > 12 && s.Age < 20
                        select s.StudentName;
    

    在上面的示例中,filteredResult 将在查询执行后包括以下学生。

    John
    Bill
    Ron
    

    在上面的示例查询中,lambda表达式主体 s.Age > 12 && s.Age < 20 作为评估集合中每个学生的谓词函数传递。Func<TSource, bool>

    另外,您还可以将Func类型委托与匿名方法一起使用,作为如下的谓词函数进行传递(输出是相同的):

    示例:Where子句

    Func<Student,bool> isTeenAger = delegate(Student s) { 
                                        return s.Age > 12 && s.Age < 20; 
                                    };
    var filteredResult = from s in studentList
                         where isTeenAger(s)
                         select s;
    

    你也可以通过Where()方法的重载调用任何与Func形参匹配的方法。

    示例:Where子句

    public static void Main()
    {
        var filteredResult = from s in studentList
                             where isTeenAger(s)
                             select s;
    }
    
    public static bool IsTeenAger(Student stud)
    {
        return stud.Age > 12 && stud.Age < 20;  
    }
    

    方法语法中的where扩展方法

    与查询语法不同,您需要将整个lambda表达式作为谓词函数传递,而不仅仅是LINQ方法语法中的表达式主体。

    示例:C#中方法语法的位置

    var filteredResult = studentList.Where(s => s.Age > 12 && s.Age < 20);
    

    示例:VB.Net中方法语法的位置

    Dim filteredResult = studentList.Where(Function(s) s.Age > 12 And s.Age < 20 )
    

    如上所述,Where扩展方法还具有第二重载,其包括集合中当前元素的索引。如果需要,可以在逻辑中使用该索引。

    以下示例使用Where子句过滤出集合中的奇数元素,仅返回偶数元素。请记住,索引从零开始。

    示例:Linq-C#中的where扩展方法

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    var filteredResult = studentList.Where((s, i) => { 
                if(i % 2 ==  0) // 如果是偶数
                    return true;
                    
            return false;
        });
    foreach (var std in filteredResult)
            Console.WriteLine(std.StudentName);
    

    输出:

    John
    Bill
    Ron
    

    多个where子句

    您可以在单个 LINQ 查询中多次调用 Where() 扩展方法。

    示例:查询语法C#中的多个where子句

    var filteredResult = from s in studentList
                            where s.Age > 12                    
                            where s.Age < 20                    
                            select s;
    

    示例:C#中的方法语法多个where子句

    var filteredResult = studentList.Where(s => s.Age > 12).Where(s => s.Age < 20);
    

    要记住的要点

    1. Where 用于根据给定标准过滤集合。
    2. 其中扩展方法有两种重载方法。使用第二个重载方法可以知道集合中当前元素的索引。
    3. 方法语法需要Where扩展方法中的整个lambda表达式,而查询语法只需要表达式体。
    4. 在单个LINQ查询中,多个Where扩展方法有效。

    LINQ 过滤运算符 OfType

    OfType运算符基于将集合中的元素强制转换为指定类型的能力来过滤筛选集合。

    查询语法中的OfType

    使用OfType运算符根据每个元素的类型筛选上述集合

    示例:C#中的OfType运算符

    IList mixedList = new ArrayList();
    mixedList.Add(0);
    mixedList.Add("One");
    mixedList.Add("Two");
    mixedList.Add(3);
    mixedList.Add(new Student() { StudentID = 1, StudentName = "Bill" });
    var stringResult = from s in mixedList.OfType()
                    select s;
    var intResult = from s in mixedList.OfType()
                    select s;
    

    上面的示例查询将返回mixedList中类型为字符串的项目。执行后,stringResult包含以下元素:

    One
    Two
    0
    3
    Bill
    

    方法语法中的OfType

    您可以在linq方法语法中使用OfType ()扩展方法,如下所示。

    示例:C#中的OfType

    var stringResult = mixedList.OfType<string>();
    

    stringResult将包含以下元素。

    One
    Two
    

    要记住的要点

    1. Where 操作符根据谓词函数筛选集合。
    2. OfType 操作符根据给定的类型对集合进行筛选。
    3. WhereOfType 扩展方法可以在一个LINQ查询中被多次调用

    LINQ 排序运算符 OrderBy和OrderByDescending

    排序运算符以升序或降序排列集合的元素。LINQ包括以下排序运算符。

    运算符 描述
    OrderBy 根据指定的字段按升序或降序对集合中的元素进行排序。
    OrderByDescending 根据指定的字段按降序对集合进行排序。仅在方法语法中有效。
    ThenBy 仅在方法语法中有效。用于按升序进行二次排序。
    ThenByDescending 仅在方法语法中有效。用于按降序进行二次排序。
    Reverse 仅在方法语法中有效。按相反顺序对集合排序。

    OrderBy

    orderderby按升序或降序对集合的值进行排序。默认情况下,它按升序对集合进行排序,因为ascending关键字在这里是可选的。使用降序关键字对集合进行降序排序。

    示例:C#中的查询语法 OrderBy

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    var orderByResult = from s in studentList
                       orderby s.StudentName 
                       select s;
    var orderByDescendingResult = from s in studentList
                       orderby s.StudentName descending
                       select s;
    

    上例中的orderByResult在执行后将包含以下元素:

    Bill
    John
    Ram
    Ron
    Steve
    

    上例中的orderByDescendingResult在执行后将包含以下元素:

    Steve
    Ron
    Ram
    John
    Bill
    

    方法语法中的OrderBy

    OrderBy扩展方法有两个重载。OrderBy扩展方法的第一个重载接受Func委托类型参数。因此,您需要为要对集合进行排序的字段传递lambda表达式。

    OrderBy的第二个重载方法接受IComparer的对象以及Func委托类型,以使用自定义比较进行排序。

    OrderBy重载方法:

    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, 
                Func<TSource, TKey> keySelector);
    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, 
                Func<TSource, TKey> keySelector, 
                IComparer<TKey> comparer);
    

    以下示例使用OrderBy扩展方法按StudentName的升序对studentList集合进行排序。

    示例:C#中方法语法的OrderBy

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    var studentsInAscOrder = studentList.OrderBy(s => s.StudentName);
    

    方法语法不允许decending关键字对集合进行降序排序。使用OrderByDecending()方法。

    OrderByDescending

    OrderByDescending以降序对集合进行排序。

    OrderByDescending仅对方法语法有效。它在查询语法中无效,因为查询语法使用升序和降序属性,如上所示。

    示例:C# 的 OrderByDescending

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    var studentsInDescOrder = studentList.OrderByDescending(s => s.StudentName);
    

    上面示例中的结果在执行后将包含以下元素。

    Steve
    Ron
    Ram
    John
    Bill
    

    请注意,查询语法不支持OrderByDescending。请改用decending关键字。

    多重排序

    您可以在用逗号分隔的多个字段上对集合进行排序。给定的集合将首先基于第一个字段进行排序,然后如果两个元素的第一个字段的值相同,则将使用第二个字段进行排序,依此类推。

    示例:查询语法C#中的多重排序

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }, 
        new Student() { StudentID = 6, StudentName = "Ram" , Age = 18 }
    };
    var orderByResult = from s in studentList
                       orderby s.StudentName, s.Age 
                       select new { s.StudentName, s.Age };
    

    在上面的示例中,studentList集合包括两个相同的StudentName,Ram与Ron。因此,现在,studentList将首先基于StudentName进行排序,然后根据年龄进行升序排列。因此,执行后orderByResult将包含以下元素

    StudentName: Bill, Age: 25
    StudentName: John, Age: 18
    StudentName: Ram, Age: 18
    StudentName: Ram, Age: 20
    StudentName: Ron, Age: 19
    StudentName: Steve, Age: 15
    

    方法语法中的多重排序的工作方式不同。使用ThenBy或ThenByDecenting扩展方法进行二次排序。

    要记住的要点

    1. LINQ包括五个排序运算符:OrderBy,OrderByDescending,ThenBy,ThenByDescending和Reverse
    2. LINQ 查询语法不支持 OrderByDescending,ThenBy,ThenByDescending 和 Reverse。它只支持“ Order By”子句的“ ascending”和“ descending”排序方向。
    3. LINQ查询语法支持多个以逗号分隔的排序字段,而您必须使用ThenBy和ThenByDescending方法进行二次排序。

    LINQ 排序运算符 ThenBy和ThenByDescending

    ThenBy和ThenByDescending扩展方法用于对多个字段排序。

    OrderBy ()方法根据指定的字段按升序对集合进行排序。在 OrderBy 之后使用 ThenBy ()方法按升序对另一个字段上的集合进行排序。Linq 首先根据 OrderBy 方法指定的主字段对集合进行排序,然后根据 ThenBy 方法指定的辅助字段按升序再次对结果集合进行排序。

    以相同的方式,使用ThenByDescending方法以降序应用二次排序。

    下面的示例演示如何使用ThenBy和ThenByDescending方法进行第二级排序:

    示例:ThenBy&ThenByDescending

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 }, 
        new Student() { StudentID = 6, StudentName = "Ram" , Age = 18 }
    };
    var thenByResult = studentList.OrderBy(s => s.StudentName).ThenBy(s => s.Age);
    var thenByDescResult = studentList.OrderBy(s => s.StudentName).ThenByDescending(s => s.Age);
    

    如您在上面的示例中所见,我们首先按排序studentList集合StudentName,然后按排序Age。因此,现在,thenByResult排序后将包含以下元素:

    StudentName: Bill, Age: 25
    StudentName: John, Age: 18
    StudentName: Ram, Age: 18
    StudentName: Ram, Age: 20
    StudentName: Ron, Age: 19
    StudentName: Steve, Age: 15
    

    现在 bydescresult 将包含以下元素。请注意,年龄为20岁的 Ram 比年龄为18岁的 Ram 更早出现,因为它使用了 ThenByDescending 。

    StudentName: Bill, Age: 25
    StudentName: John, Age: 18
    StudentName: Ram, Age: 20
    StudentName: Ram, Age: 18
    StudentName: Ron, Age: 19
    StudentName: Steve, Age: 15
    

    要记住的要点

    1. 默认情况下,OrderBy和ThenBy对集合进行升序排序。
    2. thenBy或ThenByDescending用于方法语法中的第二级排序。
    3. thenByDescending方法在另一个字段上按降序对集合进行排序。
    4. ThenBy或ThenByDescending在查询语法中不适用。
    5. 通过使用逗号分隔字段,在查询语法中应用二级排序。

    LINQ 分组运算符 GroupBy和ToLookup

    分组运算符的作用与SQL查询的GroupBy子句相同。分组运算符根据给定的键创建一组元素。该组包含在实现IGrouping <TKey,TSource>接口的特殊类型的集合中,其中TKey是键值,在其上已形成该组,TSource是与该分组键值匹配的元素的集合。

    分组运算符 描述
    GroupBy GroupBy操作符根据某个键值返回元素组。每个组由 IGrouping<TKey,TElement>对象表示。
    ToLookup ToLookup 与 GroupBy 相同; 唯一的区别是 GroupBy 的执行被延迟,而 ToLookup 的执行是立即的。

    GroupBy

    GroupBy运算符基于某个键值从给定集合返回一组元素。每个组由IGrouping <TKey,TElement>对象表示。另外,GroupBy方法有8个重载方法,因此您可以根据需要在方法语法中使用适当的扩展方法。

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey>? comparer);
    
    public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector);
    
    public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector, IEqualityComparer<TKey>? comparer);
    
    public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector);
    
    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
    
    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector);
    
    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer);
    
    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer);
    

    LINQ查询可以以GroupBy或Select子句结尾。

    GroupBy运算符的结果是组的集合。例如,GroupBy从Student集合返回IEnumerable <IGrouping <TKey,Student >>

    查询语法中的GroupBy

    下面的示例创建一组年龄相同的学生。年龄相同的学生将在同一集合中,每个分组的集合将具有一个密钥和内部集合,其中密钥将是年龄,内部集合将包括年龄与密钥匹配的学生。

    示例:GroupBy使用查询语法C#

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 21 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Abram" , Age = 21 } 
        };
    var groupedResult = from s in studentList
                        group s by s.Age;
    //遍历每组        
    foreach (var ageGroup in groupedResult)
    {
        Console.WriteLine($"Age Group: {ageGroup.Key}"); //每组都有一个钥匙              
        foreach(Student s in ageGroup) // 每组都有内部收藏
            Console.WriteLine($"Student Name: {s.StudentName}", );
    }
    

    输出:

    AgeGroup: 18
    StudentName: John
    StudentName: Bill
    AgeGroup: 21
    StudentName: Steve
    StudentName: Abram
    AgeGroup: 20
    StudentName: Ram
    

    如上例所示,您可以使用“ foreach”循环对组进行迭代,其中每个组都包含一个键和内部集合。

    方法语法中的GroupBy

    GroupBy()扩展方法工作在方法的语法一样。在GroupBy扩展方法中为键选择器字段名称指定lambda表达式。

    示例:C#中 方法语法的GroupBy

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 21 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Abram" , Age = 21 } 
        };
    
    var groupedResult = studentList.GroupBy(s => s.Age);
    foreach (var ageGroup in groupedResult)
    {
        Console.WriteLine("Age Group: {0}", ageGroup.Key);  //每组都有一个钥匙              
        foreach(Student s in ageGroup)  //每个组都有一个内部集合  
            Console.WriteLine("Student Name: {0}", s.StudentName);
    }
    

    输出:

    AgeGroup: 18
    StudentName: John
    StudentName: Bill
    AgeGroup: 21
    StudentName: Steve
    StudentName: Abram
    AgeGroup: 20
    StudentName: Ram
    

    ToLookup

    ToLookup与GroupBy相同;唯一的区别是GroupBy执行被推迟,而ToLookup执行是立即执行。另外,ToLookup仅适用于方法语法。查询语法不支持 ToLookup。

    示例:C#中方法语法的 ToLookup

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 21 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Abram" , Age = 21 } 
        };
    
    var lookupResult = studentList.ToLookup(s => s.age);
    foreach (var group in lookupResult)
    {
        Console.WriteLine("Age Group: {0}", group.Key);  //每组都有一个键              
        foreach(Student s in group)  //每个组都有一个内部集合  
            Console.WriteLine("Student Name: {0}", s.StudentName);
    }
    

    要记住的要点

    1. GroupBy&ToLookup返回一个具有键和基于键字段值的内部集合的集合。
    2. GroupBy 延迟执行,而 ToLookup 立即执行。
    3. LINQ查询语法可以以GroupBy或Select子句结尾。

    LINQ 联接运算符 Join

    联接运算符将两个序列(集合)联接并产生结果。

    联接运算符 用法
    Join Join 运算符根据一个键连接两个序列(集合)并返回结果序列。
    GroupJoin GroupJoin 运算符根据键连接两个序列并返回序列组。它类似于 SQL 的左外联接。

    Join

    Join运算符对两个集合(内部集合和外部集合)进行操作。它返回一个新集合,其中包含两个集合中满足指定表达式的元素。它与SQL的内部(inner join)联接相同。

    Join方法语法

    Join扩展方法有两个重载,如下所示。

    Join重载方法:

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, 
                IEnumerable<TInner> inner, 
                Func<TOuter, TKey> outerKeySelector, 
                Func<TInner, TKey> innerKeySelector, 
                Func<TOuter, TInner, TResult> resultSelector);
                
    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, 
                IEnumerable<TInner> inner, 
                Func<TOuter, TKey> outerKeySelector,            
                Func<TInner, TKey> innerKeySelector, 
                Func<TOuter, TInner, TResult> resultSelector,            
                IEqualityComparer<TKey> comparer);
    

    正如您在第一个重载中看到的,方法接受四个输入参数(除了第一个“this”参数):

    1)inner

    2)outerKeySelector

    3)innerkeyselector

    4)resultSelector

    让我们举一个简单的实例。下面的示例连接两个字符串集合,并返回两个集合中都包含匹配字符串的新集合。

    示例:C# join运算符

    IList<string> strList1 = new List<string>() { 
        "One", 
        "Two", 
        "Three", 
        "Four"
    };
    
    IList<string> strList2 = new List<string>() { 
        "One", 
        "Two", 
        "Five", 
        "Six"
    };
    var innerJoin = strList1.Join(strList2,
                          str1 => str1, 
                          str2 => str2, 
                          (str1, str2) => str1);
    

    输出结果:

    One
    Two
    

    现在,让我们了解使用下面的Student和Standard类的join方法,其中Student类包括与Standard类的StandardID相匹配的StandardID。

    示例类

    public class Student{ 
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public int StandardID { get; set; }
    }
    
    public class Standard{ 
        public int StandardID { get; set; }
        public string StandardName { get; set; }
    }
    

    下面的示例演示LINQ Join查询。

    示例:C# 联接查询

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", StandardID =1 },
        new Student() { StudentID = 2, StudentName = "Moin", StandardID =1 },
        new Student() { StudentID = 3, StudentName = "Bill", StandardID =2 },
        new Student() { StudentID = 4, StudentName = "Ram" , StandardID =2 },
        new Student() { StudentID = 5, StudentName = "Ron"  } 
    };
    
    IList<Standard> standardList = new List<Standard>() { 
        new Standard(){ StandardID = 1, StandardName="Standard 1"},
        new Standard(){ StandardID = 2, StandardName="Standard 2"},
        new Standard(){ StandardID = 3, StandardName="Standard 3"}
    };
    
    var innerJoin = studentList.Join(// 外序列 
                          standardList,  // 内部序列 
                          student => student.StandardID,    // externalKeySelector
                          standard => standard.StandardID,  // innerKeySelector
                          (student, standard) => new  // 结果选择器
                                        {
                                            StudentName = student.StudentName,
                                            StandardName = standard.StandardName
                                        });
    

    在上面的联接查询示例中,studentList是外部序列,因为查询从它开始。Join方法中的第一个参数用于指定内部序列,在上面的示例中该序列为standardList。Join方法的第二个和第三个参数用于指定一个字段,该字段的值应使用lambda表达式匹配,以便将元素包括在结果中。外部序列的键选择器 student => student.StandardID指示StudentList的每个元素的标准ID字段应该与内部序列 standard => standard.StandardID 的键匹配。如果两个键字段的值都匹配,则将该元素包括到结果中。

    Join方法中的最后一个参数是用于表达结果的表达式。在上面的示例中,结果选择器包括两个序列的StudentName和StandardName属性。

    两个序列(集合)的StandardID键必须匹配,否则该项将不包括在结果中。例如,Ron不与任何标准关联,因此Ron不包含在结果集合中。上述示例中的innerJoinResult在执行后将包含以下元素:

    John - Standard 1
    Moin - Standard 1
    Bill - Standard 2
    Ram - Standard 2
    

    联接查询语法

    查询语法中的连接运算符的工作原理与方法语法略有不同。它需要外部序列、内部序列、键选择器和结果选择器“on”关键字用于键选择器,其中“equals”运算符的左侧是outerKeySelector,“equals”运算符的右侧是innerKeySelector。

    语法:join查询语法

    from ... in outerSequence
    
            join ... in innerSequence  
    
            on outerKey equals innerKey
    
            select ...
    

    下面的查询语法中的Join运算符示例,如果Student.StandardID和Standard.StandardID匹配,则返回来自studentList和standardList的元素的集合。

    使用equals运算符匹配查询语法中的键选择器。==无效。

    要记住的要点

    1. Join 和 GroupJoin是连接运算符。

    2. Join 类似于SQL的内部连接。它返回一个新集合,其中包含两个键匹配的集合中的公共元素。

    3. Join 对内部序列和外部序列这两个序列进行运算,并生成结果序列。

    4. Join 查询语法:

      from... in outerSequence
      join... in innerSequence 
      on  outerKey equals innerKey
      select ...
      

    LINQ 联接运算符 GroupJoin

    我们已经在上一节中看到了Join运算符。GroupJoin运算符执行与Join运算符相同的任务,不同之处在于GroupJoin根据指定的组键在组中返回结果。GroupJoin运算符基于键联接两个序列,并通过匹配键将结果分组,然后返回分组的结果和键的集合。

    GroupJoin需要与Join相同的参数。GroupJoin具有以下两种重载方法:

    GroupJoin重载方法:

    public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, 
    IEnumerable<TInner> inner, 
    Func<TOuter, TKey> outerKeySelector, 
    Func<TInner, TKey> innerKeySelector, 
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector);
    
    public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, 
    IEnumerable<TInner> inner, 
    Func<TOuter, TKey> outerKeySelector, 
    Func<TInner, TKey> innerKeySelector, 
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector, 
    IEqualityComparer<TKey> comparer);
    

    正如您在第一个重载中看到的,方法接受五个输入参数(除了第一个“this”参数):

    1)inner

    2)outerKeySelector

    3)inner keyselector

    4)resultSelector。请注意,resultSelector是Func委托类型,它的第二个输入参数是内部序列的IEnumerable类型。

    现在,让我们使用以下Student和Standard类来了解GroupJoin,其中Student类包括与Standard类的StandardID匹配的StandardID。

    示例类

    public class Student{ 
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public int StandardID { get; set; }
    }
    
    public class Standard{ 
        public int StandardID { get; set; }
        public string StandardName { get; set; }
    }
    

    考虑下面的GroupJoin查询示例。

    示例:方法语法C#中的GroupJoin

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", StandardID =1 },
        new Student() { StudentID = 2, StudentName = "Moin", StandardID =1 },
        new Student() { StudentID = 3, StudentName = "Bill", StandardID =2 },
        new Student() { StudentID = 4, StudentName = "Ram",  StandardID =2 },
        new Student() { StudentID = 5, StudentName = "Ron" } 
    };
    
    IList<Standard> standardList = new List<Standard>() { 
        new Standard(){ StandardID = 1, StandardName="Standard 1"},
        new Standard(){ StandardID = 2, StandardName="Standard 2"},
        new Standard(){ StandardID = 3, StandardName="Standard 3"}
    };
    
    var groupJoin = standardList.GroupJoin(studentList,  //内部序列
                                    std => std.StandardID, //outerKeySelector 
                                    s => s.StandardID,     //innerKeySelector
                                    (std, studentsGroup) => new // resultSelector 
                                    {
                                        Students = studentsGroup,
                                        StandarFulldName = std.StandardName
                                    });
    
    foreach (var item in groupJoin)
    { 
        Console.WriteLine(item.StandarFulldName );
    
        foreach(var stud in item.Students)
            Console.WriteLine(stud.StudentName);
    }
    

    输出:

    Standard 1:
    John,
    Moin,
    Standard 2:
    Bill,
    Ram,
    Standard 3:
    

    在上面的GroupJoin查询示例中,standardList是外部序列,因为查询是从外部序列开始的。GroupJoin方法中的第一个参数是指定内部序列,在上面的示例中为studentList。该方法的第二和第三个参数GroupJoin()是指定一个字段,该字段的值应使用lambda表达式进行匹配,以便在结果中包含element。外部序列的键选择器standard => standard.StandardID指示standardList中每个元素的StandardID字段应与内部序列studentList的键匹配student => student.StandardID。如果两个键字段的值都匹配,则将该元素包括到分组集合studentsGroup中,其中键为StandardID。

    Join方法中的最后一个参数是用于表达结果的表达式。在上面的示例中,结果选择器包括分组的集合studentGroup和StandardName。

    结果集将包含具有Students和StandardFullName属性的匿名对象。学生属性将是其StandardID与Standard.StandardID匹配的Student的集合。

    您可以使用“ foreach”循环访问结果。每个元素将具有StandardFullName&Students属性,其中Student将是一个集合。

    示例:在C#中访问GroupJoin结果

    foreach (var item in groupJoinResult)
    { 
        Console.WriteLine(item.StandarFulldName );
    
        foreach(var stud in item.Students)
            Console.WriteLine(stud.StudentName);
    }
    

    查询语法中的GroupJoin

    查询语法中的GroupJoin运算符与方法语法稍有不同。它需要一个外部序列,内部序列,键选择器和结果选择器。“ on”关键字用于键选择器,其中“等于”运算符的左侧是outerKeySelector,而“等于”运算符的右侧是innerKeySelector。使用into关键字创建分组的集合。

    语法:查询语法中的GroupJoin

    from ... in outerSequence
    
    join ... in innerSequence  
    
    on outerKey equals innerKey
    
    into groupedCollection    
    
    select ...
    

    LINQ 投影运算符 Select,SelectMany

    LINQ中有两个可用的投影运算符。

    1)Select

    2)SelectMany

    Select

    Select运算符始终返回IEnumerable集合,该集合包含基于转换函数的元素。它类似于产生平面结果集的SQL的Select子句。

    现在,让我们了解使用以下Student类的Select查询运算符。

    public class Student{ 
        public int StudentID { get; set; }
        public string StudentName { get; set; }    
        public int Age { get; set; }
    }
    
    在查询语法中的Select

    LINQ查询语法必须以SelectGroupBy子句结尾。下面的示例演示了Select 运算符,该运算符返回StudentName的字符串集合。

    示例:在查询语法C#中Select

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John" },
        new Student() { StudentID = 2, StudentName = "Moin" },
        new Student() { StudentID = 3, StudentName = "Bill" },
        new Student() { StudentID = 4, StudentName = "Ram" },
        new Student() { StudentID = 5, StudentName = "Ron" } 
    };
    
    var selectResult = from s in studentList
                       select s.StudentName;
    

    选择运算符可用于根据我们的要求制定结果。它可用于返回自定义类或匿名类型的集合,其中包括根据我们的需要的属性。

    下面的select子句示例返回一个包含Name和Age属性的匿名类型的集合。

    示例:在查询语法C#中Select

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
        new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 } 
    };
    
    // 返回具有Name和Age属性的匿名对象的集合
    var selectResult = from s in studentList
                       select new { Name = "Mr. " + s.StudentName, Age = s.Age }; 
    
    // 迭代selectResult
    foreach (var item in selectResult)
        Console.WriteLine($"Student Name: {item.Name}, Age: {item.Age}");
    

    输出:

    Student Name: Mr. John, Age: 13
    Student Name: Mr. Moin, Age: 21
    Student Name: Mr. Bill, Age: 18
    Student Name: Mr. Ram, Age: 20
    Student Name: Mr. Ron, Age: 15
    
    在方法语法中Select

    Select运算符在方法语法中是可选的。但是,您可以使用它来塑造数据。在以下示例中,Select扩展方法返回具有Name和Age属性的匿名对象的集合:

    示例:*C#*在方法语法中的Select

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 21 } 
    };    
    var selectResult = studentList.Select(s => new { Name = s.StudentName , Age = s.Age  });
    

    SelectMany

    SelectMany 运算符投射基于转换函数的值序列,然后将它们扁平化为一个序列,SelectMany有四个重载方法:

    // 将序列的每个元素投影到 IEnumerable<TResult> 并将结果序列合并为一个序列
    public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector);
    
    public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TResult>> selector);
    
    // 将序列的每个元素投影到 IEnumerable<TCollection>,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数
    public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);
    
    public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);
    

    no code no talk,示例类:

    public class Student{ 
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public Book[] books { get; set; }
    }
    
    public class Book{ 
        public int BookID { get; set; }
        public string BookName { get; set; }
    }
    
    第一个重载

    分别使用LINQ语句和方法调用:

    IList<Student> studentList = new List<Student>() {
        new Student() { StudentID = 1, StudentName = "John",
        books = new Book[] {
            new Book() { BookID = 1, BookName = "21天学废C#" }
        } } ,
        new Student() { StudentID = 2, StudentName = "Moin",
        books = new Book[] {
            new Book() { BookID = 2, BookName = "21天学废C++" },
            new Book() { BookID = 3, BookName = "21天学废Java" },
            new Book() { BookID = 4, BookName = "21天学废Python" },
        } },
        new Student() { StudentID = 3, StudentName = "Bill",
        books = new Book[] {
          new Book() {BookID = 6, BookName = "21天学废踢足球"},
          new Book() {BookID = 7, BookName = "21天学废打足球"},
          new Book() {BookID = 8, BookName = "21天学废游泳"},
        } } ,
        new Student() { StudentID = 4, StudentName = "Ram" ,
        books = new Book[] {
          new Book() {BookID = 9, BookName = "21天学废Rust"}
        } } ,
        new Student() { StudentID = 5, StudentName = "Ron" ,
        books = new Book[] {
          new Book() {BookID = 10, BookName = "21天学废Go"}
        } }
    };
    // LINQ语句方式
    var books = from s in studentList
                from b in s.books
                select b;
    foreach (var item in books)
    {
      Console.WriteLine(item.BookName);
    }
    // 方法调用方式
    var arrs = studentList.SelectMany(s => s.books);
    foreach (var item in arrs)
    {
      Console.WriteLine(item.BookName);
    }
    

    输出结果为:

    21天学废C#
    21天学废C++
    21天学废Java
    21天学废Python
    21天学废踢足球
    21天学废打足球
    21天学废游泳
    21天学废Rust
    21天学废Go
    

    这里把studentList中的元素books对象取出来,而它本来也是一个集合,从结果可以看出SelectMany将拿到的books合并到了一个集合中,我个人简单理解为数组降维

    第二个重载

    相对与第一个重载来说,只是多了Student元素的索引

    第三个重载

    同样使用上面的数据示例,使用LINQ语句为:

    var arrs = from s in studentList
               from b in s.books
               select new { MixedName = $"{s.StudentName} {b.BookName}" };
    
    foreach (var item in arrs)
    {
      Console.WriteLine(item.MixedName);
    }
    

    同样使用上面的数据示例,使用方法调用为:

    var arrs = studentList.SelectMany(s => s.books, (s, b) =>
    new { MixedName = $"{s.StudentName} {b.BookName}" });
    
    foreach (var item in arrs)
    {
      Console.WriteLine(item.MixedName);
    }
    

    输出结果为:

    John 21天学废C#
    Moin 21天学废C++
    Moin 21天学废Java
    Moin 21天学废Python
    Bill 21天学废踢足球
    Bill 21天学废打足球
    Bill 21天学废游泳
    Ram 21天学废Rust
    Ron 21天学废Go
    

    这里把studentList中的元素books对象取出来,而它本来也是一个集合,从结果可以看出SelectMany将拿到的books合并到了一个集合中,我个人简单理解为数组降维,然后再组成一个新的集合返回

    第四个重载

    相对与第三个重载来说,只是多了Student元素的索引

    LINQ 限定运算符

    限定运算符在某些条件下评估序列的元素,然后返回布尔值以指示某些或所有元素都满足条件。

    运算符 描述
    All 检查序列中的所有元素是否满足指定的条件
    Any 检查序列中是否有任一元素满足指定条件
    Contains 检查序列是否包含特定元素

    All

    All运算符在指定条件下评估给定集合中的每个元素,如果所有元素均满足条件,则返回True。

    示例:C# All运算符

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    
    // 检查所有学生是否都是青少年    
    bool areAllStudentsTeenAger = studentList.All(s => s.Age > 12 && s.Age < 20);
    Console.WriteLine(areAllStudentsTeenAger);
    

    输出:

    false
    

    Any

    Any检查元素是否满足给定条件?在以下示例中,“Any”操作用于检查是否有任何学生是青少年。

    示例:Any 运算符 与 C#

    bool isAnyStudentTeenAger = studentList.Any(s => s.age > 12 && s.age < 20);
    

    输出:

    true
    

    c#查询语法不支持限定运算符。

    Contains

    Contains方法用来确定序列是否包含满足指定条件的元素。如果有返回true,否则返回false。以下代码使用默认的String比较器来判断序列中是否含有指定的字符串:

    string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" };
    Console.WriteLine(source1.Contains("A")); //输出 "True"
    Console.WriteLine(source1.Contains("G")); //输出  "False"
    

    LINQ 限定运算符 Contains

    Contains运算符检查集合中是否存在指定的元素,并返回布尔值。

    Contains()扩展方法有以下两个重载。第一个重载方法需要一个值来检入集合,第二个重载方法需要使用IEqualityComparer类型的附加参数来进行自定义相等性比较。

    Contains() 重载:

    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);
    

    如上所述,Contains()扩展方法需要一个值作为输入参数进行检查。值的类型必须与泛型集合的类型相同。下面的示例包含检查集合中是否存在10。请注意,int是泛型集合的一种类型。

    示例:C# 中 Contains运算符

    IList<int> intList = new List<int>() { 1, 2, 3, 4, 5 };
    bool result = intList.Contains(10);  // 返回 false
    

    上面的示例适用于原始数据类型。但是,它不适用于自定义类。看以下示例:

    Error:return false

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    
    Student std = new Student(){ StudentID =3, StudentName = "Bill", Age = 25 };
    bool result = studentList.Contains(std); //returns false
    

    正如您在上面的示例中看到的,Contains返回false,即使studentList中存在“Bill”。这是因为Contains扩展方法只比较对象的引用,而不比较对象的实际值。所以要比较student对象的值,需要通过实现IEqualityComparer接口创建一个类,该接口比较两个student对象的值并返回boolean。

    以下是StudentComparer类,它实现IEqualityComparer接口来比较两个Students对象的值:

    示例:IEqualityComperer

    public class StudentComparer : IEqualityComparer<Student>
    {
      public bool Equals(Student? x, Student? y)
      {
        return x.StudentID == y.StudentID &&
        x.StudentName.Equals(y.StudentName, StringComparison.OrdinalIgnoreCase) &&
        x.Age == y.Age;
      }
    
      public int GetHashCode([DisallowNull] Student obj)
      {
        return obj.GetHashCode();
      }
    }
    

    现在,你可以在Contains扩展方法的第二个重载方法中使用上面的StudentComparer类,该方法接受第二个参数为IEqualityComparer类型,如下所示:

    示例: C#中 Contains 与 Comparer 类

    IList<Student> studentList = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    
    Student std = new Student(){ StudentID =3, StudentName = "Bill", Age = 25 };
    bool result = studentList.Contains(std, new StudentComparer()); //returns true
    

    因此,必须使用Comparer类才能从自定义类的Contains扩展方法中获得正确的结果。

    在C# 查询表达式语法中不支持限定符运算符,可以通过where条件后面增加方法调用,如下:

    // Determine which market have all fruit names length equal to 5
    IEnumerable<string> names = from market in markets
                                where market.Items.All(item => item.Length == 5)
                                select market.Name;
    

    注意事项

    1. All, Any & Contains是LINQ中的限定运算符。
    2. All检查序列中的所有元素是否满足指定条件。
    3. 检查序列中是否有任何元素满足指定条件
    4. Contains 操作符检查指定的元素是否存在于集合中。
    5. 使用派生 IEqualityComparer 和 Contains 的自定义类检查集合中的对象。
    6. 在查询语法中不支持All, Any & Contains。

    LINQ 聚合运算符 Aggregate

    聚合运算符对集合中元素的数值属性执行数学运算,如Average、Aggregate、Count、Max、Min和Sum。

    方法 描述
    Aggregate 对集合中的值执行自定义聚合操作。
    Average 计算集合中数字项的平均值。
    Count 统计集合中的元素。
    LongCount 统计集合中的元素。
    Max 查找集合中的最大值。
    Min 查找集合中的最小值。
    Sum 计算集合中值的总和。

    Aggregate

    聚合方法执行累加操作。聚合扩展方法具有以下重载方法:

    Aggregate()重载:

    public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, 
                                             Func<TSource, TSource, TSource> func);
    
    public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, 
                                             TAccumulate seed, 
                                             Func<TAccumulate, TSource, TAccumulate> func);
    
    public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, 
                                             TAccumulate seed, 
                                             Func<TAccumulate, TSource, TAccumulate> func, 
                                             Func<TAccumulate, TResult> resultSelector);
    

    下面的示例演示了 Aggregate 方法,该方法返回字符串列表中逗号分隔的元素。

    示例:C#方法语法的中Aggregate

    IList<String> strList = new List<String>() { "One", "Two", "Three", "Four", "Five"};
    var commaSeperatedString = strList.Aggregate((s1, s2) => s1 + ", " + s2);
    Console.WriteLine(commaSeperatedString);
    

    输出:

     One, Two, Three, Four, Five
    

    在上面的示例中,Aggregate扩展方法从strList集合返回逗号分隔的字符串。

    strList“ One”的第一项将作为 s1传递,其余项将作为 s2传递。Lambda 表达式(s1,s2) = > s1 + ","+ s2将被视为 s1 = s1 +","+ s2,其中 s1将为集合中的每个项累积。因此,Aggregate 方法将返回逗号分隔的字符串。

    带种子值的聚合方法

    Aggregate的第二个重载方法需要第一个参数来累积种子值。第二个参数是Func类型的委托:

    TAccumulate Aggregate<TSource, TAccumulate>(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);
    

    下面的示例在Aggregate扩展方法中将字符串用作种子值。

    示例:C# 带有种子值的聚合

    // 学生集合
    IList<Student> studentList = new List<Student>>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
            new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 } 
        };
    
    string commaSeparatedStudentNames = studentList.Aggregate<Student, string>(
                                            "Student Names: ",  // 种子价值
                                            (str, s) => str += s.StudentName + "," ); 
    Console.WriteLine(commaSeparatedStudentNames);
    

    输出:

    Student Names: John, Moin, Bill, Ram, Ron,
    

    在上面的示例中,Aggregate 方法的第一个参数是“ Student Names: ”字符串,该字符串将与所有学生名一起累积。Lambda 表达式中的逗号将作为第二个参数传递。

    下面的示例使用 Aggregate 运算符添加所有学生的年龄。

    示例:带有种子值C#的聚合

    // 学生集合
    IList<Student> studentList = new List<Student>>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
        new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 } 
    };
    
    int SumOfStudentsAge = studentList.Aggregate<Student, int>(0, (totalAge, s) => totalAge += s.Age  );
    

    带有结果选择器的聚合方法

    现在,让我们看看第三个重载方法,它需要 Func 委托表达式的第三个参数作为结果选择器,这样您就可以公式化结果。

    示例:C#使用结果选择器进行聚合

    IList<Student> studentList = new List<Student>() {
        new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
        new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram", Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron", Age = 15 }
    };
    
    string commaSeparatedStudentNames = studentList.Aggregate<Student, string, string>(
        "Student Names:", // 种子值
        (str, s) => str += s.StudentName + ",", // 使用种子值返回结果,Student Names:以str的形式进入lambda表达式
        str => str.Substring(0, str.Length - 1)); // 删除最后一个逗号的结果选择器
    
    Console.WriteLine(commaSeparatedStudentNames);
    

    在上面的示例中,我们指定了一个lambda表达式str => str.Substring(0,str.Length - 1 ),该表达式将删除字符串结果中的最后一个逗号。

    输出:

    Student Names:John, Moin, Bill, Ram, Ron
    

    LINQ查询语法不支持聚合运算符。

    LINQ 聚合运算符 Average

    Average 扩展方法计算集合中数值项的平均值。Average 方法返回可空或不可空的十进制值、双值或浮点值。

    下面的示例演示Agerage方法,该方法返回集合中所有整数的平均值。

    示例:平均方法C#

    IList<int> intList = new List<int>>() { 10, 20, 30 };
    var avg = intList.Average();
    Console.WriteLine("Average: {0}", avg);
    

    您可以将类的 int、 decimal、 double 或 float 属性指定为 lambda 表达式,希望获得其平均值。下面的示例演示复杂类型上的 Average 方法。

    示例:C#中的方法语法Average

    IList<Student> studentList = new List<Student>>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
        new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 } 
    };
    
    var avgAge = studentList.Average(s => s.Age);
    Console.WriteLine("Average Age of Student: {0}", avgAge);
    

    输出:

    学生的平均年龄:17.4
    

    C# LINQ不支持查询语法中的 Average 运算符

    LINQ 聚合运算符 Count

    Count运算符返回集合中的元素数或满足给定条件的元素数。

    Count()扩展方法有以下两种重载:

    int Count<TSource>();
    
    int Count<TSource>(Func<TSource, bool> predicate);
    

    Count的第一个重载方法返回指定集合中的元素数,而第二个重载方法返回满足指定条件的元素数(由lambda表达式/predicate 谓词函数给出)。

    下面的示例演示了原语集合上的Count()。

    示例:Count()与C#

    IList<int> intList = new List<int>() { 10, 21, 30, 45, 50 };
    
    var totalElements = intList.Count();
    Console.WriteLine("元素总数: {0}", totalElements);
    
    var evenElements = intList.Count(i => i % 2 == 0);
    Console.WriteLine("偶数元素: {0}", evenElements);
    

    输出:

    元素总数:5
    偶数元素:3
    

    下面的示例演示Count()复杂类型集合上的方法。

    示例:在C#中的Count()

    IList<Student> studentList = new List<Student>>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
        new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
        new Student() { StudentID = 5, StudentName = "Mathew" , Age = 15 } 
    };
    
    var totalStudents = studentList.Count();
    Console.WriteLine("学生总人数: {0}", totalStudents);
    
    var adultStudents = studentList.Count(s => s.Age >= 18);
    Console.WriteLine("成人学生人数: {0}", adultStudents );
    

    输出:

    学生总人数:5
    成人学生人数:3
    

    查询语法中的Count运算符

    C#查询语法不支持聚合运算符。但是,您可以将查询括在方括号中并使用聚合函数,如下所示。

    示例:查询语法C#中的Count运算符,同样计算成人学生人数

    var adultStudents = (from s in studentList
                         where s.Age >= 18
                         select s.Age).Count();
    

    LINQ 聚合运算符 Max

    Max()方法返回集合中最大的数值元素。

    下面的示例演示原始集合上的Max()方法。

    示例:C#中的Max()方法

    IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };
    
    var largest = intList.Max();
    Console.WriteLine("最大元素: {0}", largest);
    
    var largestEvenElements = intList.Max(i =>
    {
      if (i % 2 == 0)
        return i;
    
      return 0;
    });
    Console.WriteLine("最大偶数: {0}", largestEvenElements);
    

    输出:

    最大元素:87
    最大偶数:50
    

    下面的示例演示复杂类型集合上的Max()方法。

    示例:C#方法语法中的Max()方法

    IList<Student> studentList = new List<Student>() {
        new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
        new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram", Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron", Age = 15 }
    };
    
    var oldest = studentList.Max(s => s.Age);
    Console.WriteLine("Oldest Student Age: {0}", oldest);
    

    输出:

    最大的学生年龄:21
    

    Max返回任何数据类型的结果。以下示例显示了如何找到集合中名字最长的学生:

    示例:在C#中Max()

    public class Student : IComparable<Student>
    {
      public int StudentID { get; set; }
      public string StudentName { get; set; }
      public int Age { get; set; }
    
      public int CompareTo(Student? other)
      {
        return this.StudentName.Length >= other?.StudentName.Length ? 1 : 0;
      }
    }
    
    IList<Student> studentList = new List<Student>() {
        new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
        new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
        new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
        new Student() { StudentID = 4, StudentName = "Ram", Age = 20 } ,
        new Student() { StudentID = 5, StudentName = "Ron", Age = 15 },
        new Student() { StudentID = 6, StudentName = "Steve", Age = 17 }
    };
    
    var studentWithLongName = studentList.Max();
    Console.WriteLine($"Student ID: {studentWithLongName?.StudentID}, Student Name: {studentWithLongName?.StudentName}");
    

    输出:

    Student ID: 6, Student Name: Steve
    

    您可以使用与Max相同的方式使用Min扩展方法/运算符。

    根据上面的实例,要找到名字最长的学生,需要实现IComparable接口,并在CompareTo方法中比较学生名字的长度。现在,您可以使用Max()方法,它将使用CompareTo方法来返回适当的结果。

    C#查询语法不支持Max运算符。

    LINQ 聚合运算符 Sum

    Sum()方法计算集合中数字项的总和。

    下面的示例演示原始集合上的Sum()方法。

    示例:LINQ Sum() - C#

    IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };
    
    var total = intList.Sum();
    Console.WriteLine("总计: {0}", total);
    
    var sumOfEvenElements = intList.Sum(i =>
    {
      if (i % 2 == 0)
        return i;
    
      return 0;
    });
    Console.WriteLine("偶数元素的总计: {0}", sumOfEvenElements);
    

    输出:

    总计:243
    偶数元素的总计:90
    

    下面的示例计算学生集合中所有学生的年龄总和,以及成年学生的人数。

    示例:LINQ Sum() 计算学生年龄总和和成年学生人数 - C#

    IList<Student> studentList = new List<Student> () { 
            new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
            new Student() { StudentID = 2, StudentName = "Moin", Age = 21 } ,
            new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
            new Student() { StudentID = 4, StudentName = "Ram", Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron", Age = 15 }
        };
    
    var sumOfAge = studentList.Sum(s => s.Age);
    Console.WriteLine("学生年龄总和: {0}", sumOfAge);
    
    var numOfAdults = studentList.Sum(s =>
    {
      if (s.Age >= 18)
        return 1;
      else
        return 0;
    });
    Console.WriteLine("成年学生人数: {0}", numOfAdults);
    
    学生年龄总和:87
    成年学生人数:3
    

    C# 查询语法中不支持Sum运算符。

    LINQ 元素运算符 ElementAt,ElementAtOrDefault

    元素运算符从序列(集合)中返回特定元素。

    下表列出了LINQ中的所有Element运算符。

    元素运算符(方法) 描述
    ElementAt 返回集合中指定索引处的元素
    ElementAtOrDefault 返回集合中指定索引处的元素;如果索引超出范围,则返回默认值。
    First 返回集合的第一个元素,或满足条件的第一个元素。
    FirstOrDefault 返回集合的第一个元素,或满足条件的第一个元素。如果索引超出范围,则返回默认值。
    Last 返回集合的最后一个元素,或满足条件的最后一个元素
    LastOrDefault 返回集合的最后一个元素,或满足条件的最后一个元素。如果不存在这样的元素,则返回默认值。
    Single 返回集合中的唯一元素,或唯一满足条件的元素。
    SingleOrDefault 返回集合中的唯一元素,或唯一满足条件的元素。如果不存在这样的元素,或者该集合不完全包含一个元素,则返回默认值。

    ElementAt()方法从给定集合返回指定索引中的元素。如果指定的索引超出集合的范围,则它将抛出“索引超出范围(*Index out of range* exception)”异常。请注意,索引是从零开始的索引。

    ElementAtOrDefault()方法还从协作中返回指定索引中的元素,如果指定索引不在集合的范围内,则它将返回数据类型的默认值,而不是引发错误。

    下面的示例演示原始集合上的ElementAt和ElementAtOrDefault方法。

    示例:LINQElementAt()和ElementAtOrDefault()方法 - C#

    IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { "One", "Two", null, "Four", "Five" };
    
    Console.WriteLine("intList中的第一个元素: {0}", intList.ElementAt(0));
    Console.WriteLine("strList中的第一个元素: {0}", strList.ElementAt(0));
    
    Console.WriteLine("intList中的第二个元素: {0}", intList.ElementAt(1));
    Console.WriteLine("strList中的第二个元素: {0}", strList.ElementAt(1));
    
    Console.WriteLine("intList中的第三个元素: {0}", intList.ElementAtOrDefault(2));
    Console.WriteLine("strList中的第三个元素: {0}", strList.ElementAtOrDefault(2));
    
    Console.WriteLine("intList中的第10个元素: {0} - 默认int值", intList.ElementAtOrDefault(9));
    Console.WriteLine("strList中的第十个元素: {0} - 默认字符串值(null)", strList.ElementAtOrDefault(9));
    
    Console.WriteLine("intList. ElementAt (9)抛出异常: 索引超出范围");
    Console.WriteLine("-------------------------------------------------------------");
    Console.WriteLine(intList.ElementAt(9));
    

    输出:

    intList中的第一个元素: 10
    strList中的第一个元素: One
    intList中的第二个元素: 21
    strList中的第二个元素: Two
    intList中的第三个元素: 30
    strList中的第三个元素: 
    intList中的第10个元素: 0 - 默认int值
    strList中的第十个元素:  - 默认字符串值(null)
    intList. ElementAt (9)抛出异常: 索引超出范围
    -------------------------------------------------------------
    Unhandled exception. System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
    

    正如您在上面示例中所看到的那样,intList.ElementAtOrDefault(9)返回0(默认值为int),因为intList不包含第十元素。 然而,intList.ElementAt(9)以相同的方式抛出“索引超出范围”,(9)返回null,它是字符串类型的默认值。 (控制台显示空白空间,因为它不能显示空)

    因此,建议使用 ElementAtOrDefault 扩展方法来消除运行时异常的可能性。

    LINQ 元素运算符 First和FirstOrDefault

    First和FirstOrDefault方法从集合中第零个索引返回一个元素,即第一个元素。另外,它返回满足指定条件的元素。

    元素运算符 描述
    First 返回集合的第一个元素,或满足条件的第一个元素。
    FirstOrDefault 返回集合的第一个元素,或满足条件的第一个元素。如果索引超出范围,则返回默认值。

    First和FirstOrDefault具有两个重载方法。第一个重载方法不使用任何输入参数,并返回集合中的第一个元素。第二个重载方法将lambda表达式作为谓词委托来指定条件,然后返回满足指定条件的第一个元素。

    First() 和FirstOrDefault()重载:

    public static TSource First<TSource>(this IEnumerable<TSource> source);
    public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
    public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source);
    public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
    public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, 
                                                  Func<TSource, bool> predicate);
    public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, 
                                                  Func<TSource, bool> predicate, TSource defaultValue);
    

    First()方法使用lambda表达式或Func委托返回集合的第一个元素,或满足指定条件的第一个元素。如果给定的集合为空或不包含任何满足条件的元素,则它将抛出 InvalidOperation 异常。

    FirstOrDefault()方法与First()方法具有相同的作用。唯一的区别是,如果集合为空或找不到满足条件的任何元素,它将返回集合数据类型的默认值。

    下面的示例演示First()方法。

    示例:LINQ First()方法 - C#

    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    IList<string> emptyList = new List<string>();
    		
    Console.WriteLine("intList中的第一个元素: {0}", intList.First());
    Console.WriteLine("intList中的第一个偶数元素: {0}", intList.First(i => i % 2 == 0));
    
    Console.WriteLine("strList中的第一个元素: {0}", strList.First());
    
    Console.WriteLine("emptyList.First()抛出InvalidOperationException");
    Console.WriteLine("-------------------------------------------------------------");
    Console.WriteLine(emptyList.First());
    

    输出:

    intList中的第一个元素:7 intList中的第一个偶数元素:10 strList中的第一个元素: emptyList.First()抛出InvalidOperationException ----------------------------------------------- -------------- 运行时异常:序列不包含任何元素...
    

    下面的示例演示FirstOrDefault()方法。

    示例:LINQ FirstOrDefault() - C#

    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    IList<string> emptyList = new List<string>();
    		
    Console.WriteLine("intList中的第一个元素: {0}", intList.FirstOrDefault());
    Console.WriteLine("intList中的第一个偶数元素: {0}",intList.FirstOrDefault(i => i % 2 == 0));
    Console.WriteLine("strList中的第一个元素: {0}", strList.FirstOrDefault());
    Console.WriteLine("emptyList中的第一个元素: {0}", emptyList.FirstOrDefault());
    

    输出:

    intList中的第一个元素:7 
    intList中的第一个偶数元素:10 
    strList中的第一个元素:
    emptyList中的第一个元素:
    

    在First()或FirstOrDefault()中指定条件时要小心。如果集合不包含任何满足指定条件的元素或包含null元素,则First()将抛出异常。

    如果集合包含空元素,则 FirstOrDefault ()在计算指定条件时抛出异常。下面的示例演示了这一点。

    示例:LINQFirst()和FirstOrDefault() - C#

    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    		
    Console.WriteLine("intList中大于250的第一个元素: {0}", intList.First( i > 250));
    Console.WriteLine("在 intList 中的第一个偶数元素: {0}", strList.FirstOrDefault(s => s.Contains("T")));
    

    输出:

     Run-time exception: Sequence contains no matching element
    //运行时异常:序列不包含匹配元素
    

    LINQ 元素运算符 Last和LastOrDefault

    元素运算符 描述
    Last 返回集合中的最后一个元素,或满足条件的最后一个元素
    LastOrDefault 返回集合中的最后一个元素,或满足条件的最后一个元素。如果不存在这样的元素,则返回默认值。

    Last和LastOrDefault具有两个重载方法。一个重载方法不使用任何输入参数,而是返回集合中的最后一个元素。第二个重载方法使用lambda表达式指定条件,然后返回满足指定条件的最后一个元素。

    Last() 和 LastOrDefault()重载:

    public static TSource Last<TSource>(this IEnumerable<TSource> source);
    public static TSource Last<TSource>(this IEnumerable<TSource> source, 
                                        Func<TSource, bool> predicate);
    public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source);
    public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
    public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, 
                                                 Func<TSource, bool> predicate, TSource defaultValue);
    

    Last()方法从集合中返回最后一个元素,或者使用lambda表达式或Func委托返回满足指定条件的最后一个元素。如果给定的集合为空或不包含任何满足条件的元素,则它将抛出 InvalidOperation 异常。

    LastOrDefault()方法与Last()方法具有相同的作用。唯一的区别是,如果集合为空或找不到满足条件的任何元素,它将返回集合数据类型的默认值。

    下面的示例演示Last()方法。

    示例:LINQ Last() - C#

    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    IList<string> emptyList = new List<string>();
    		
    Console.WriteLine("intList中的最后一个元素: {0}", intList.Last());
    Console.WriteLine("intList中的最后一个偶数: {0}", intList.Last(i => i % 2 == 0));
    Console.WriteLine("strList中的最后一个元素: {0}", strList.Last());
    Console.WriteLine("emptyList.Last()抛出InvalidOperationException ");
    Console.WriteLine("-------------------------------------------------------------");
    Console.WriteLine(emptyList.Last());
    

    输出:

    intList中的最后一个元素:
    intList中的最后一个偶数:50 
    strList中的最后一个元素:5
    emptyList.Last()抛出InvalidOperationException 
    ----------------------------------------------- --------------
    Run-time exception: Sequence contains no elements...
    

    下面的示例演示LastOrDefault()方法。

    示例:LINQ LastOrDefault()-C#

    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    IList<string> emptyList = new List<string>();
    		
    Console.WriteLine("intList中的最后一个元素: {0}", intList.LastOrDefault());
    Console.WriteLine("intList中的最后一个偶数元素: {0}",intList.LastOrDefault(i => i % 2 == 0));
    Console.WriteLine("strList中的最后一个元素: {0}", strList.LastOrDefault());
    Console.WriteLine("emptyList中的最后一个元素: {0}", emptyList.LastOrDefault());
    

    输出:

    intList中的最后一个元素:
    intList中的最后一个偶数元素:10 
    strList中的最后一个元素:
    emptyList中的最后一个元素:
    

    在Last()或LastOrDefault()中指定条件时要小心。如果集合不包含任何满足指定条件的元素或包含null元素,那么Last()将抛出异常。

    如果集合包含null元素,则LastOrDefault()在评估指定条件时会引发异常。以下示例对此进行了演示。

    示例:LINQLast()和LastOrDefault() - C#

    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    		
    Console.WriteLine("intList中大于250的最后一个元素: {0}", intList.Last(i => i > 250));
    Console.WriteLine("intList中的最后一个偶数元素: {0}", strList.LastOrDefault(s => s.Contains("T")));
    

    输出:

    Run-time exception: Sequence contains no matching element          
    //运行时异常:序列不包含匹配元素
    

    LINQ 元素运算符 Single和SingleOrDefault

    元素运算符 描述
    Single 返回集合中的唯一元素,或唯一满足条件的元素。如果Single()在集合中未找到任何元素或一个以上的元素,则抛出InvalidOperationException。
    SingleOrDefault 与Single相同,不同之处在于它返回指定泛型类型的默认值,而不是在找不到指定条件的元素时抛出异常。但是,如果在集合中为指定条件找到了多个元素,它将抛出InvalidOperationException。

    Single和SingleOrDefault具有两个重载方法。第一个重载方法不使用任何输入参数,并返回集合中的单个元素。第二种重载方法将lambda表达式作为指定条件的谓词委托,并返回满足指定条件的单个元素。

    Single()和SingleOrDefault()重载:

    public static TSource Single<TSource>(this IEnumerable<TSource> source);
    public static TSource Single<TSource>(this IEnumerable<TSource> source, 
                                          Func<TSource, bool> predicate);
    public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source);
    public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
    public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, 
                                                   Func<TSource, bool> predicate);
    public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, 
                                                   Func<TSource, bool> predicate, TSource defaultValue);
    

    Single()返回集合中的唯一元素,或唯一满足指定条件的元素。如果给定的集合不包含任何元素或包含多个元素,则Single()抛出InvalidOperationException。

    SingleOrDefault()方法与Single()方法具有相同的作用。唯一的区别是,如果集合为空,不包含一个以上元素或对于指定条件不包含一个或多个元素,则它返回集合数据类型的默认值。

    示例:Single方法语法 - C#

    IList<int> oneElementList = new List<int>() { 7 };
    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    IList<string> emptyList = new List<string>();
    
    Console.WriteLine("oneElementList 中的唯一元素: {0}", oneElementList.Single());
    Console.WriteLine("oneElementList 中的唯一元素: {0}",oneElementList.SingleOrDefault());
    
    Console.WriteLine("emptyList中的元素: {0}", emptyList.SingleOrDefault());
    Console.WriteLine("intList中唯一小于10的元素: {0}",intList.Single(i => i < 10));
    
    //下面抛出一个异常
    //Console.WriteLine("intList中唯一的元素: {0}", intList.Single());
    //Console.WriteLine("intList中唯一的元素: {0}", intList.SingleOrDefault());
    //Console.WriteLine("emptyList中唯一的元素: {0}", emptyList.Single());
    

    输出:

    oneElementList中的唯一元素:7 
    oneElementList中的唯一元素:7 
    emptyList中的元素:0 
    intList中唯一小于10的元素:7
    

    以下示例代码将引发异常,因为Single()或SingleOrDefault()对于指定条件不返回任何元素或返回多个元素。

    C#:Single()和SingleOrDefault()

    IList<int> oneElementList = new List<int>() { 7 };
    IList<int> intList = new List<int>() { 7, 10, 21, 30, 45, 50, 87 };
    IList<string> strList = new List<string>() { null, "Two", "Three", "Four", "Five" };
    IList<string> emptyList = new List<string>();
    
    //下面抛出错误,因为列表包含多个小于100的元素
    Console.WriteLine("intList中小于100的元素: {0}", intList.Single(i => i < 100));
    //下面抛出错误,因为列表包含多个小于100的元素
    Console.WriteLine("intList中小于100的元素: {0}", intList.SingleOrDefault(i => i < 100));
    //由于列表包含多个元素,下面抛出错误
    Console.WriteLine("intList中唯一的元素: {0}", intList.Single());
    //由于列表包含多个元素,下面抛出错误
    Console.WriteLine("intList中唯一的元素: {0}", intList.SingleOrDefault());
    //下面抛出错误,因为列表不包含任何元素
    Console.WriteLine("emptyList 中的唯一元素: {0}", emptyList.Single());
    

    要记住的要点

    1. Single()要求集合中只有一个元素。
    2. Single() 当集合中没有任何元素或一个以上元素时,抛出异常。
    3. 如果在Single()中指定了一个条件,并且结果不包含任何元素或包含多个元素,则会引发异常。
    4. 如果集合或指定条件中没有元素,SingleOrDefault()将返回泛型集合的数据类型的默认值。
    5. 如果集合或指定条件中有多个元素,SingleOrDefault()将引发异常。

    LINQ 相等运算符 SequenceEqual

    只有一个相等运算符:SequenceEqual。SequenceEqual方法检查两个集合中的元素数量,每个元素的值和元素的顺序是否相等。

    如果集合包含原始数据类型的元素,则它将比较元素的值和数量,而具有复杂类型元素的集合将检查对象的引用。因此,如果对象具有相同的引用,则将它们视为相等,否则将其视为不相等。

    下面的示例演示了带有原始数据类型集合的SequenceEqual方法。

    示例:方法语法C#中的SequenceEqual

    IList<string> strList1 = new List<string>(){"One", "Two", "Three", "Four", "Three"};
    IList<string> strList2 = new List<string>(){"One", "Two", "Three", "Four", "Three"};
    
    bool isEqual = strList1.SequenceEqual(strList2); // 返回true
    Console.WriteLine(isEqual);
    

    输出:

    true
    

    如果元素的顺序不同,则SequenceEqual()方法返回false。

    示例:C#中方法语法的SequenceEqual

    IList<string> strList1 = new List<string>(){"One", "Two", "Three", "Four", "Three"};
    IList<string> strList2 = new List<string>(){ "Two", "One", "Three", "Four", "Three"};
    
    bool isEqual = strList1.SequenceEqual(strList2); // 返回false
    Console.WriteLine(isEqual);
    

    输出:

    false
    

    SequenceEqual扩展方法检查两个对象的引用,以确定两个序列是否相等。这可能会给出错误的结果。看以下示例:

    示例:C#中的SequenceEqual

    Student std = new Student() { StudentID = 1, StudentName = "Bill" };
    IList<Student> studentList1 = new List<Student>(){ std };
    IList<Student> studentList2 = new List<Student>(){ std };
           
    bool isEqual = studentList1.SequenceEqual(studentList2); // 返回true
    
    Student std1 = new Student() { StudentID = 1, StudentName = "Bill" };
    Student std2 = new Student() { StudentID = 1, StudentName = "Bill" };
    
    IList<Student> studentList3 = new List<Student>(){ std1};
    IList<Student> studentList4 = new List<Student>(){ std2 };
           
    isEqual = studentList3.SequenceEqual(studentList4);// 返回false
    

    在上面的示例中,studentList1和studentList2包含相同的学生对象std。因此studentList1.SequenceEqual(studentList2)返回true。但是,stdList1和stdList2包含两个单独的学生对象std1和std2。所以现在,即使std1和std2包含相同的值,stdList1.SequenceEqual(stdList2)也将返回false。

    要比较两个复杂类型(引用类型或对象)集合的值,需要实现IEqualityComperar 接口,如下所示。

    示例:IEqualityComparer C#:

    class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (x.StudentID == y.StudentID && x.StudentName.ToLower() == y.StudentName.ToLower())
                return true;
    
            return false;
        }
    
        public int GetHashCode(Student obj)
        {
            return obj.GetHashCode();
        }
    }
    

    现在,您可以使用SequenceEqual扩展方法中的上述StudentComparer类作为第二个参数来比较值:

    示例:C#使用SequenceEqual比较对象类型元素

    IList<Student> studentList1 = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    
    IList<Student> studentList2 = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    // 以下返回true
    bool isEqual = studentList1.SequenceEqual(studentList2, new StudentComparer());
    

    要记住的要点

    1. SequenceEqual方法比较项目数及其原始数据类型的值。
    2. SequenceEqual方法比较对象对复杂数据类型的引用。
    3. 使用IEqualityComparer类可通过SequenceEqual方法比较两个复杂类型的集合。

    LINQ 串联运算符 Concat

    Concat()方法附加两个相同类型的序列,并返回一个新序列(集合)。

    示例:C#中的Concat

    IList<string> collection1 = new List<string>() { "One", "Two", "Three" };
    IList<string> collection2 = new List<string>() { "Five", "Six"};
    
    var collection3 = collection1.Concat(collection2);
    foreach (string str in collection3)
        Console.WriteLine(str);
    

    输出:

    One
    Two
    Three
    Five
    Six
    

    示例:C#中的Concat方法

    IList<int> collection1 = new List<int>() { 1, 2, 3 };
    IList<int> collection2 = new List<int>() { 4, 5, 6 };
    
    var collection3 = collection1.Concat(collection2);
    foreach (int i in collection3)
        Console.WriteLine(i);
    

    输出:

    1
    2
    3
    4
    5
    6
    

    C#的查询语法不支持Concat运算符。

    LINQ 生成运算符 DefaultIfEmpty

    如果调用DefaultIfEmpty()的给定集合为空,则DefaultIfEmpty()方法将返回一个具有默认值的新集合。

    DefaultIfEmpty()的另一个重载方法接受一个值参数,该参数应替换为默认值。

    看以下示例。

    示例:DefaultIfEmpty - C#

    IList<string> emptyList = new List<string>();
    
    var newList1 = emptyList.DefaultIfEmpty(); 
    var newList2 = emptyList.DefaultIfEmpty("None"); 
    
    Console.WriteLine("Count: {0}" , newList1.Count());
    Console.WriteLine("Value: {0}" , newList1.ElementAt(0));
    
    Console.WriteLine("Count: {0}" , newList2.Count());
    Console.WriteLine("Value: {0}" , newList2.ElementAt(0));
    

    输出:

    Count: 1
    Value:
    Count: 1
    Value: None
    

    在上面的示例中,emptyList.DefaultIfEmpty() 返回一个新的字符串集合,其中一个元素的值为null,因为null是string的默认值。另一种方法emptyList.DefaultIfEmpty("None") 返回一个字符串集合,该字符串集合的一个元素的值为“ None”而不是null。

    下面的示例演示如何在int集合上调用DefaultIfEmpty。

    示例:DefaultIfEmpty - C#

    IList<int> emptyList = new List<int>();
    
    var newList1 = emptyList.DefaultIfEmpty(); 
    var newList2 = emptyList.DefaultIfEmpty(100);
    
    Console.WriteLine("Count: {0}" , newList1.Count());
    Console.WriteLine("Value: {0}" , newList1.ElementAt(0));
    
    Console.WriteLine("Count: {0}" , newList2.Count());
    Console.WriteLine("Value: {0}" , newList2.ElementAt(0));
    

    输出:

    Count: 1
    Value: 0
    Count: 1
    Value: 100
    

    下面的示例演示复杂类型集合的 DefaultIfEmpty() 方法。

    示例:DefaultIfEmpty - C#:

    IList<Student> emptyStudentList = new List<Student>();
    
    var newStudentList1 = studentList.DefaultIfEmpty(new Student());                
    var newStudentList2 = studentList.DefaultIfEmpty(new Student(){ 
                    StudentID = 0, 
                    StudentName = "" });
    
    Console.WriteLine("Count: {0} ", newStudentList1.Count());
    Console.WriteLine("Student ID: {0} ", newStudentList1.ElementAt(0));
    
    Console.WriteLine("Count: {0} ", newStudentList2.Count());
    Console.WriteLine("Student ID: {0} ", newStudentList2.ElementAt(0).StudentID);
    

    输出:

    Count: 1
    Student ID:
    Count: 1
    Student ID: 0
    

    LINQ 生成运算符 Empty, Range, Repeat

    LINQ包括生成运算符DefaultIfEmpty,Empty,Range&Repeat。Empty,Range和Repeat方法不是IEnumerable或IQueryable的扩展方法,而只是在静态类Enumerable中定义的静态方法。

    方法 描述
    Empty 返回一个空集合
    Range 从第一个元素开始,使用指定数量的具有顺序值的元素生成IEnumerable 类型的集合。
    Repeat 生成具有指定元素数的IEnumerable 类型的集合,并且每个元素包含相同的指定值。

    Empty

    Empty()与其他LINQ方法一样,该方法不是IEnumerable或IQueryable的扩展方法。它是Enumerable静态类中包含的静态方法。因此,您可以像其他静态方法(如Enumerable.Empty ())一样调用它。Empty()方法返回指定类型的空集合,如下所示。

    示例:Enumerable.Empty() 方法

    var emptyCollection1 = Enumerable.Empty<string>();
    var emptyCollection2 = Enumerable.Empty<Student>();
    
    Console.WriteLine("Count: {0} ", emptyCollection1.Count());
    Console.WriteLine("Type: {0} ", emptyCollection1.GetType().Name );
    
    Console.WriteLine("Count: {0} ",emptyCollection2.Count());
    Console.WriteLine("Type: {0} ", emptyCollection2.GetType().Name );
    

    输出:

    Type: String[]
    Count: 0
    Type: Student[]
    Count: 0
    

    Range

    Range()方法返回IEnumerable 类型的集合,该集合具有指定数量的元素和从第一个元素开始的顺序值。

    例:Enumerable.Range() 方法使用

    var intCollection = Enumerable.Range(10, 10);
    Console.WriteLine("总计数: {0} ", intCollection.Count());
    
    for(int i = 0; i < intCollection.Count(); i++)
        Console.WriteLine("值,索引位置为 {0} : {1}", i, intCollection.ElementAt(i));
    

    输出:

    总计数: 10
    值,索引位置为 0 : 10
    值,索引位置为 1 : 11
    值,索引位置为 2 : 12
    值,索引位置为 3 : 13
    值,索引位置为 4 : 14
    值,索引位置为 5 : 15
    值,索引位置为 6 : 16
    值,索引位置为 7 : 17
    值,索引位置为 8 : 18
    值,索引位置为 9 : 19
    

    在上面的示例中,Enumerable.Range(10, 10)创建了具有10个整数元素的集合,其顺序值从10开始。第一个参数指定元素的起始值,第二个参数指定要创建的元素数。

    Repeat

    Repeat()方法使用指定数量的元素生成IEnumerable 类型的集合,每个元素包含相同的指定值。

    示例:Enumerable.Repeat()方法

    var intCollection = Enumerable.Repeat<int>(10, 10);
    Console.WriteLine("总数: {0} ", intCollection.Count());
    
    for(int i = 0; i < intCollection.Count(); i++)
        Console.WriteLine("值,索引位置为 {0} : {1}", i, intCollection.ElementAt(i));
    

    输出:

    总数:10
    值,索引位置为 0: 10
    值,索引位置为 1: 10
    值,索引位置为 2: 10
    值,索引位置为 3: 10
    值,索引位置为 4: 10
    值,索引位置为 5: 10
    值,索引位置为 6: 10
    值,索引位置为 7: 10
    值,索引位置为 8: 10
    值,索引位置为 9: 10
    

    在上面的示例中,Enumerable.Repeat(10, 10) 创建具有100个重复值为10的整数类型元素的集合,第一个参数指定所有元素的值,第二个参数指定要创建的元素数。

    LINQ Set运算符 Distinct

    下表列出了LINQ中可用的所有Set运算符。

    集合运算符 用法
    Distinct 返回集合中的非重复值。
    Except 返回两个序列之间的差,这意味着一个集合中的元素不出现在第二个集合中。
    Intersect 返回两个序列的交集,即同时出现在两个集合中的元素。
    Union 返回两个序列中的唯一元素,这意味着出现在两个序列中的唯一元素。

    Distinct

    Distinct扩展方法从给定集合返回一个新的唯一元素集合。

    示例:C#中的Distinct方法

    IList<string> strList = new List<string>(){ "One", "Two", "Three", "Two", "Three" };
    IList<int> intList = new List<int>(){ 1, 2, 3, 2, 4, 4, 3, 5 };
    
    var distinctList1 = strList.Distinct();
    foreach(var str in distinctList1)
        Console.WriteLine(str);
    
    var distinctList2 = intList.Distinct();
    foreach(var i in distinctList2)
        Console.WriteLine(i);
    

    输出:

    One
    Two
    Three
    1
    2
    3
    4
    5
    

    Distinct扩展方法不比较复杂类型对象的值。为了比较复杂类型的值,需要实现IEqualityComparer接口。在下面的示例中,StudentComparer类实现IEqualityComparer来比较Students。

    示例:用C#实现IEqualityComparer

    public class Student 
    {
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public int Age { get; set; }
    }
    
    class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (x.StudentID == y.StudentID 
                    && x.StudentName.ToLower() == y.StudentName.ToLower())
                return true;
    
            return false;
        }
    
        public int GetHashCode(Student obj)
        {
            return obj.StudentID.GetHashCode();
        }
    }
    

    现在,您可以在Distinct()方法中传递上述StudentComparer类的对象作为参数来比较Student对象,如下所示。 示例:C#中的Distinct比较对象

    IList<Student> studentList = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    
    var distinctStudents = studentList.Distinct(new StudentComparer()); 
    foreach(Student std in distinctStudents)
        Console.WriteLine(std.StudentName);
    

    输出:

    John
    Steve
    Bill
    Ron
    

    查询语法中的Distinct运算符

    C# 查询语法不支持 Distinct 运算符。但是,您可以使用 Distinct 方法查询变量或将整个查询包装到括号中,然后调用 Distinct ()。

    LINQ Set运算符 Except

    Except()方法需要两个集合。它返回一个新集合,其中包含来自第一个集合的元素,该元素在第二个集合(参数集合)中不存在。

    示例:C#中方法语法 Except

    IList<string> strList1 = new List<string>(){"One", "Two", "Three", "Four", "Five" };
    IList<string> strList2 = new List<string>(){"Four", "Five", "Six", "Seven", "Eight"};
    
    var result = strList1.Except(strList2);
    foreach(string str in result)
    {
    	Console.WriteLine(str);
    }
    

    输出:

    One
    Two
    Three
    

    Except扩展方法不返回复杂类型集合的正确结果。您需要实现IEqualityComparer接口,以便从Except方法获得正确的结果。

    为 Student 类实现 IEqualityComparer 接口,如下所示:

    示例: C# 使用 Except 方法的 IEqualityComparer

    public class Student 
    {
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public int Age { get; set; }
    }
    
    class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (x.StudentID == y.StudentID && x.StudentName.ToLower() == y.StudentName.ToLower())
                return true;
    
            return false;
        }
    
        public int GetHashCode(Student obj)
        {
            return obj.StudentID.GetHashCode();
        }
    }
    

    现在,您可以在Except扩展方法中通过StudentComparer类,以获取正确的结果:

    示例:C#对象类型为的Except()方法

    IList<Student> studentList1 = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    
    IList<Student> studentList2 = new List<Student>() { 
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    
    var resultedCol = studentList1.Except(studentList2,new StudentComparer()); 
    foreach(Student std in resultedCol)
        Console.WriteLine(std.StudentName);
    

    输出:

    John
    Steve
    

    C #查询语法不支持 Except 运算符。但是,您可以对查询变量使用 Distinct 方法,或者将整个查询包装到括号中,然后调用 Except ()。

    LINQ Set运算符 Intersect

    Intersect扩展方法需要两个集合。它返回一个新集合,其中包含两个集合中都存在的公共元素。看下面的实例。

    例如:在方法语法C#中的Intersect

    IList<string> strList1 = new List<string>() { "One", "Two", "Three", "Four", "Five" };
    IList<string> strList2 = new List<string>() { "Four", "Five", "Six", "Seven", "Eight"};
    
    var result = strList1.Intersect(strList2);
    foreach(string str in result)
            Console.WriteLine(str);
    

    输出:

    Four
    Five
    

    Intersect 扩展方法不返回复杂类型集合的正确结果。为了从 Intersect 方法中得到正确的结果,需要实现 IEqualityComparer 接口。

    为 Student 类实现 IEqualityComparer 接口,如下所示:

    示例:在C#中将IEqualityComparer与Intersect一起使用

    public class Student 
    {
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public int Age { get; set; }
    }
    
    class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (x.StudentID == y.StudentID && 
                            x.StudentName.ToLower() == y.StudentName.ToLower())
                return true;
    
            return false;
        }
    
        public int GetHashCode(Student obj)
        {
            return obj.StudentID.GetHashCode();
        }
    }
    

    现在,您可以在Intersect扩展方法中通过StudentComparer类,以获取正确的结果:

    示例:C#中的Intersect运算符

    IList<Student> studentList1 = new List<Student>() { 
        new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    
    IList<Student> studentList2 = new List<Student>() { 
        new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
        new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
    };
    
    var resultedCol = studentList1.Intersect(studentList2, new StudentComparer()); 
    foreach(Student std in resultedCol)
        Console.WriteLine(std.StudentName);
    

    输出:

    Bill
    Ron
    

    LINQ Set运算符 Union

    Union扩展方法需要两个集合,并返回一个新集合,其中包含两个集合中不同的元素。看下面的实例。

    示例:Union()在C#中

    IList<string> strList1 = new List<string>() { "One", "Two", "three", "Four" };
    IList<string> strList2 = new List<string>() { "Two", "THREE", "Four", "Five" };
    
    var result = strList1.Union(strList2);
    foreach(string str in result)
            Console.WriteLine(str);
    

    输出:

    One
    Two
    three
    THREE
    Four
    Five
    

    Union扩展方法不能为复杂类型的集合返回正确的结果。您需要实现IEqualityComparer接口,以便从Union方法中获取正确的结果。

    为 Student 类实现IEqualityComparer接口,如下所示:

    示例: 使用 IEqualityComparer 的 Union 运算符:

    public class Student 
    {
        public int StudentID { get; set; }
        public string StudentName { get; set; }
        public int Age { get; set; }
    }
    
    class StudentComparer : IEqualityComparer<Student>
    {
        public bool Equals(Student x, Student y)
        {
            if (x.StudentID == y.StudentID && x.StudentName.ToLower() == y.StudentName.ToLower())
                return true;
    
            return false;
        }
    
        public int GetHashCode(Student obj)
        {
            return obj.StudentID.GetHashCode();
        }
    }
    

    现在,您可以在Union扩展方法中传递上述StudentComparer类以获得正确的结果:

    示例:Uion运算符 - C#

    IList<Student> studentList1 = new List<Student>() { 
            new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new Student() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    
    IList<Student> studentList2 = new List<Student>() { 
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new Student() { StudentID = 5, StudentName = "Ron" , Age = 19 } 
        };
    
    var resultedCol = studentList1.Union(studentList2, new StudentComparer()); 
    foreach(Student std in resultedCol)
        Console.WriteLine(std.StudentName);
    

    输出:

    John
    Steve
    Bill
    Ron
    

    查询语法

    C #查询语法不支持联合运算符。但是,您可以对查询变量使用 Union 方法,或者将整个查询包装到方括号中,然后调用 Union ()。

    LINQ 分区运算符 Skip & SkipWhile

    分区运算符将序列(集合)分为两部分,并返回其中一部分。

    方法 描述
    Skip 从序列中的第一个元素开始,将元素跳到指定的位置。
    SkipWhile 根据条件跳过元素,直到元素不满足条件为止。如果第一个元素本身不满足条件,那么它将跳过0个元素并返回序列中的所有元素。
    Take 从序列中的第一个元素开始,将元素带到指定的位置。
    TakeWhile 从第一个元素返回元素,直到元素不满足条件为止。如果第一个元素本身不满足条件,则返回一个空集合。

    Skip

    Skip()方法从第一个元素开始跳过指定数量的元素,并返回其余元素。

    示例:Skip()方法跳过2个元素 -C#

    IList<string> strList = new List<string>(){ "One", "Two", "Three", "Four", "Five" };
    
    var newList = strList.Skip(2);
    foreach(var str in newList)
        Console.WriteLine(str);
    

    输出:

    Three
    Four
    Five
    

    查询语法中的 Skip 运算符

    C # 查询语法不支持 Skip & SkipWhile 运算符。但是,您可以对查询变量使用 Skip/SkipWhile 方法,或者将整个查询包装到括号中,然后调用 Skip/SkipWhile。

    SkipWhile

    顾名思义,LINQ中的SkipWhile()扩展方法将跳过集合中的元素,直到指定的条件为false,它将返回一个包含所有剩余元素的新集合。

    SkipWhile()方法有两种重载方法。一种方法接受Func<TSource, bool>类型的谓词,另一种重载方法接受Func<TSource, int, bool>带元素索引的谓词类型。

    在下面的示例中,SkipWhile()方法跳过所有元素,直到找到长度等于或大于4个字符的字符串。

    示例:在C#中SkipWhile()方法跳过长度小于4的元素,并返回其后的所有元素

    IList<string> strList = new List<string>() { 
                                                "One", 
                                                "Two", 
                                                "Three", 
                                                "Four", 
                                                "Five", 
                                                "Six"  };
    
    var resultList = strList.SkipWhile(s => s.Length < 4);
    foreach(string str in resultList)
            Console.WriteLine(str);
    

    输出:

    Three
    Four
    Five
    Six
    

    在上面的示例中,由于前两个元素的长度小于3,所以 SkipWhile() 跳过前两个元素,并找到长度等于或大于4的第三个元素。一旦找到长度等于或大于4个字符的任何元素,它将不跳过其他任何元素元素,即使小于4个字符。

    现在,看以下示例,该示例中,其中 SkipWhile ()不会跳过任何元素,因为第一个元素的指定条件为 false。

    示例:C#使用SkipWhile方法,不跳过元素

    IList<string> strList = new List<string>() { 
                                                "Three", 
                                                "One", 
                                                "Two", 
                                                "Four", 
                                                "Five", 
                                                "Six"  };
    
    var resultList = strList.SkipWhile(s => s.Length < 4);
    foreach(string str in resultList)
            Console.WriteLine(str);
    

    输出:

    Three
    One
    Two
    Four
    Five
    Six
    

    SkipWhile的第二个重载传递每个元素的索引。看下面的实例

    示例:在C#中使用索引的SkipWhile()方法

    IList<string> strList = new List<string>() { 
                                                "One", 
                                                "Two", 
                                                "Three", 
                                                "Four", 
                                                "Five", 
                                                "Six"  };
    
    var result = strList.SkipWhile((s, i) => s.Length > i);
    foreach(string str in result)
        Console.WriteLine(str);
    

    输出:

    Five
    Six
    

    在上面的示例中,lambda表达式包括元素和元素的索引作为参数。它会跳过所有元素,直到字符串元素的长度大于其索引。

    查询语法中的SkipWhile运算符

    C# 查询语法不支持 Skip & SkipWhile 运算符。但是,您可以对查询变量使用 Skip/SkipWhile 方法,或者将整个查询包装到括号中,然后调用 Skip/SkipWhile ()。

    LINQ 分区运算符 Take & TakeWhile

    分区运算符将序列(集合)分为两部分,并返回其中一部分。

    Take

    Take()扩展方法返回从第一个元素开始的指定数量的元素。

    示例:Take()方法在C#中

    IList<string> strList = new List<string>(){ "One", "Two", "Three", "Four", "Five" };
    
    var newList = strList.Take(2);
    foreach(var str in newList)
        Console.WriteLine(str);
    

    输出:

    One
    Two
    

    C# 查询语法不支持 Take & takedwhile 运算符。但是,您可以对查询变量使用 Take/takedwhile 方法,或者将整个查询包装到括号中,然后调用 Take/takedwhile ()。

    TakeWhile

    TakeWhile()扩展方法返回给定集合中的元素,直到指定的条件为false。如果第一个元素本身不满足条件,则返回一个空集合。

    TakeWhile方法有两个重载方法。一种方法接受Func<TSource, bool>类型的谓词,另一种重载方法接受Func<TSource, int, bool>带元素索引的谓词类型。

    在下面的示例中,TakeWhile()方法返回一个包含所有元素的新集合,直到找到长度小于4个字符的字符串为止。

    示例:C#中的TakeWhile方法返回字符串长度大于4的元素

    IList<string> strList = new List<string>() { 
                                                "Three", 
                                                "Four", 
                                                "Five", 
                                                "Hundred"  };
    
    var result = strList.TakeWhile(s => s.Length > 4);
    foreach(string str in result)
            Console.WriteLine(str);
    

    输出:

    Three
    

    在上面的示例中,TakeWhile()返回仅包含第一元素,因为第二字符串元素不满足该条件。

    TakeWhile 还在谓词函数中传递当前元素的索引。下面的 TakeWhile 方法示例接受元素,直到字符串元素的长度大于它的索引

    示例:C#中的TakeWhile传递索引,返回字符串长度大于索引的元素

    IList<string> strList = new List<string>() { 
                                                "One", 
                                                "Two", 
                                                "Three", 
                                                "Four", 
                                                "Five", 
                                                "Six"  };
    
    var resultList = strList.TakeWhile((s, i) => s.Length > i);
    foreach(string str in resultList)
            Console.WriteLine(str);
    

    输出:

    One
    Two
    Three
    Four
    

    LINQ 转换运算符

    LINQ中的Conversion运算符可用于转换序列(集合)中元素的类型。转换运算符分为三种:As运算符(AsEnumerable和AsQueryable),To运算符(ToArray,ToDictionary,ToList和ToLookup)和转换运算符(Cast和OfType)。

    下表列出了所有转换运算符。

    方法 描述
    AsEnumerable 将输入序列作为 IEnumerable < T> 返回
    AsQueryable 将IEnumerable转换为IQueryable,以模拟远程查询提供程序
    Cast 将非泛型集合转换为泛型集合(IEnumerable到IEnumerable)
    OfType 基于指定类型筛选集合
    ToArray 将集合转换为数组
    ToDictionary 根据键选择器函数将元素放入 Dictionary 中
    ToList 将集合转换为 List
    ToLookup 将元素分组到 Lookup<TKey,TElement>

    AsEnumerable和AsQueryable方法

    AsEnumerable和AsQueryable方法分别将源对象转换或转换为IEnumerable 或IQueryable

    请看以下示例:

    示例:C#中的AsEnumerable和AsQueryable运算符:

    class Program
    {
        static void ReportTypeProperties<T>(T obj)
        {
            Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
            Console.WriteLine("Actual type: {0}", obj.GetType().Name);
        }
    
        static void Main(string[] args)
        {
            Student[] studentArray = { 
                new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
                new Student() { StudentID = 2, StudentName = "Steve",  Age = 21 } ,
                new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
                new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
                new Student() { StudentID = 5, StudentName = "Ron" , Age = 31 } ,
            };   
                
            ReportTypeProperties( studentArray);
            ReportTypeProperties(studentArray.AsEnumerable());
            ReportTypeProperties(studentArray.AsQueryable());   
        }
    }
    

    输出:

    Compile-time type: Student[]
    Actual type: Student[]
    Compile-time type: IEnumerable`1
    Actual type: Student[]
    Compile-time type: IQueryable`1
    Actual type: EnumerableQuery`1
    

    如上例所示,AsEnumerable和AsQueryable方法分别将编译时间类型转换为IEnumerable和IQueryable

    Cast

    Cast的作用与AsEnumerable相同。它将源对象强制转换为IEnumerable

    示例:C#中的 Cast转换运算符

    class Program
    {
    
        static void ReportTypeProperties<T>(T obj)
        {
            Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
            Console.WriteLine("Actual type: {0}", obj.GetType().Name);
        }
    
        static void Main(string[] args)
        {
            Student[] studentArray = { 
                    new Student() { StudentID = 1, StudentName = "John", Age = 18 } ,
                    new Student() { StudentID = 2, StudentName = "Steve",  Age = 21 } ,
                    new Student() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
                    new Student() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
                    new Student() { StudentID = 5, StudentName = "Ron" , Age = 31 } ,
                };   
             
            ReportTypeProperties( studentArray);
            ReportTypeProperties(studentArray.Cast<Student>());
        }
    }
    

    输出:

    Compile-time type: Student[]
    Actual type: Student[]
    Compile-time type: IEnumerable`1
    Actual type: Student[]
    

    studentArray.Cast() 与 (IEnumerable)studentArray 相同,但是 Cast() 可读性更好。

    To运算符:ToArray(),ToList(),ToDictionary()

    顾名思义,ToArray(),ToList(),ToDictionary()方法的源对象转换分别为一个数组,列表或字典。

    To 运算符强制执行查询。它强制远程查询提供者执行查询并从底层数据源(如SQL Server数据库)获取结果。

    示例:C#中的ToArray和ToList

    IList<string> strList = new List<string>() {
                                                "One",
                                                "Two",
                                                "Three",
                                                "Four",
                                                "Three"
                                                };
    
    string[] strArray = strList.ToArray<string>();// 将列表转换为数组
    IList<string> list = strArray.ToList<string>(); // converts array into list
    

    ToDictionary - 将泛型列表转换为泛型词典:

    示例:C#中的 ToDictionary:

    IList<Student> studentList = new List<Student>() { 
                        new Student() { StudentID = 1, StudentName = "John", age = 18 } ,
                        new Student() { StudentID = 2, StudentName = "Steve",  age = 21 } ,
                        new Student() { StudentID = 3, StudentName = "Bill",  age = 18 } ,
                        new Student() { StudentID = 4, StudentName = "Ram" , age = 20 } ,
                        new Student() { StudentID = 5, StudentName = "Ron" , age = 21 } 
                    };
    
    //以下将列表转换成字典,其中StudentId是键
    IDictionary<int, Student> studentDict = studentList.ToDictionary<Student, int>(s => s.StudentID); 
    foreach(var key in studentDict.Keys)
    	Console.WriteLine("Key: {0}, Value: {1}", key, (studentDict[key] as Student).StudentName);
    

    输出:

    Key: 1, Value: John
    Key: 2, Value: Steve
    Key: 3, Value: Bill
    Key: 4, Value: Ram
    Key: 5, Value: Ron
    
  • 相关阅读:
    JVM 常用参数设置(针对 G1GC)
    Java 字符串常量池 及 intern 方法的使用
    JDK 1.8 Metaspace 详解
    JDK 1.8 MetaSpace(元空间)介绍及调优
    Git 统计代码行数
    王永庆传-读书笔记2
    王永庆传-读书笔记1
    董明珠:女人真想干点事,谁也拦不住
    esxi5.5安装nvme驱动
    nvme ssd的一些相关知识点
  • 原文地址:https://www.cnblogs.com/linxmouse/p/16337670.html
Copyright © 2020-2023  润新知