c# linq查询语句详细使用介绍

本文介绍Linq的使用方法

  1. linq介绍

    LINQ只不过是实现IEnumerable和IQueryable接口的类的扩展方法的集合。
    LINQ可以查询IEnumerable集合或者IQueryable数据源
        查询语法
            List<string> list = new List<string>() { "a", "b", "cb", "d", "e" };
            var result = from s in list where s.Contains("b") select s;
        方法语法
            方法语法的使用就是调用Enumberable或者Queryable的扩展方法
            List<string> list = new List<string>() { "a", "b", "cb", "dc", "e" };
            var result = list.Where(s => s.Contains("c"));
            上面Where中传递的是一个lambda表达式是匿名函数的简写形式,完整写法如下
                var result = list.Where(delegate (string s) {
                    return s.Contains("c");
                });
    
  2. 标准的查询操作符

    ** 过滤操作符
        > Where操作符
            在查询语句中使用where在后面加上过滤条件(函数)就可以了
                List<string> list = new List<string>() { "a", "b", "cb", "dc", "ae" };
                Func<string, bool> isFilter = s => s.Contains("a");
                var result = from s in list where isFilter(s) select s;
            在扩展方法中使用where有两个重载 
                不带索引的重载
                    List<string> list = new List<string>() { "a", "b", "cb", "dc", "e" };
                    var result = list.Where(s => s.Contains("c"));
                带索引的重载
                    List<string> list = new List<string>() { "a", "b", "cb", "dc", "e" };
                    int isum = 0;
                    IEnumerable<string> result = list.Where((s, i) =>
                    {
                        if (s.Contains("c"))
                        {
                            isum++;
                            return true;
                        }
                        return false;
                    }).ToList(); // 这里不写ToList()的话linq语句不执行
                    return Ok(isum);
            无论是查询语句还是扩展方法,where都可以使用多个
                IEnumerable<string> result = list.Where(s => s.Contains("c")).Where(s => s.Contains("b"));
        > OfType操作符
            使用OfType操作符可以根据数据类型来过滤数据
            在查询语句中使用
                ArrayList list = new ArrayList() { 2, "a", 5};
                var result = from s in list.OfType<int>() select s; // 将ArrayList所有的int类型数据筛选出来
            在扩展方法中使用
                ArrayList list = new ArrayList() { 2, "a", 5};
                var result = list.OfType<string>();
    ** 排序操作符
        > OrderBy语句
            根据指定的字段升序或者降序排序
            IList<TestClass> list = new List<TestClass>() {
                new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                new TestClass() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
                new TestClass() { StudentID = 3, StudentName = "Bill",  Age = 15 } ,
                new TestClass() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
                new TestClass() { StudentID = 5, StudentName = "Ron" , Age = 19 }
            };
            在查询语句中使用
                var result = from s in list orderby s.Age select s; // 默认是升序排序,ascending可以指定也可以不写
                var result = from s in list orderby s.Age descending select s; // 指定descending实现降序
                可以指定多条列进行排序
                    var result = from s in list orderby s.Age, s.StudentID descending select s;
                    如果s.Age是相同的,那么根据s.StudentID对相同的数据进行排序
            在扩展语句中使用
                重载一,使用默认规则实现简单的升序排序
                    var result = list.OrderBy(s => s.Age); // 根据Age字段进行升序排序,不支持降序
                重载二,可以传递IComparer泛型实例,实现自定义排序规则
                    先定义一个实现IComparer的类
                        public class CompareClass : IComparer<string>
                        {
                            public int Compare(string x, string y)
                            {
                                string str1 = x.Substring(1, 1);
                                string str2 = y.Substring(1, 1);
                                return int.Parse(str1).CompareTo(int.Parse(str2)); // 升序,如果调换str1和str2的位置,可以实现降序
                            }
                        }
                    实现排序
                        IList<TestClass> list = new List<TestClass>() {
                            new TestClass() { StudentID = 1, StudentName = "A3_John", Age = 18 } ,
                            new TestClass() { StudentID = 2, StudentName = "A4_Steve",  Age = 15 } ,
                            new TestClass() { StudentID = 3, StudentName = "A2_Bill",  Age = 25 } ,
                            new TestClass() { StudentID = 4, StudentName = "A5_Ram" , Age = 20 } ,
                            new TestClass() { StudentID = 5, StudentName = "A6_Ron" , Age = 19 }
                        };
                        var result = list.OrderBy(s => s.StudentName, new CompareClass());
        > OrderByDescending语句
            根据指定的字段降序排序,只能在扩展方法中使用
            var result = list.OrderByDescending(s => s.Age);
        > ThenBy语句
            用于二级排序的升序,只能在扩展方法中使用
            var result = list.OrderBy(s => s.Age).ThenBy(s => s.StudentID);
            对s.Age排序后的结果中的相同项按照s.StudentID进行升序排序
        > ThenByDescending语句
            用于二级排序的降序,只能在扩展方法中使用
            var result = list.OrderBy(s => s.Age).ThenByDescending(s => s.StudentID);
            对s.Age排序后的结果中的相同项按照s.StudentID进行降序排序
        > Reverse语句
            反序排序,只能用于扩展方法中
            var result = list.Reverse();
            对原始集合进行反转排序操作
    ** 分组操作符
        > GroupBy操作符
            GroupBy根据指定的列将相同项分成一组,返回的结果是IEnumerable <IGrouping<TKey, TSource>>类型
            每组是IGrouping<TKey, TSource>类型,TKey是键的类型,TSource是结果集合的类型
            IList<TestClass> list = new List<TestClass>() {
                new TestClass() { StudentID = 1, StudentName = "A3_John", Age = 18 } ,
                new TestClass() { StudentID = 2, StudentName = "A4_Steve",  Age = 15 } ,
                new TestClass() { StudentID = 3, StudentName = "A2_Bill",  Age = 15 } ,
                new TestClass() { StudentID = 4, StudentName = "A5_Ram" , Age = 20 } ,
                new TestClass() { StudentID = 5, StudentName = "A6_Ron" , Age = 20 }
            };
            在查询语句中使用
                var result = from s in list group s by s.Age;
                foreach(var ageGroup in result)
                {
                    Trace.WriteLine(ageGroup.Key); // Key是分组的Age值
                    foreach( TestClass s in ageGroup)
                    {
                        // ...
                    }   
                }
            在扩展方法中使用
                var result = list.GroupBy(s => s.Age);
        > ToLookup操作符
            只能在扩展方法中使用
                var result = list.ToLookup(s => s.Age);
                ToLookup和GroupBy功能是一样的,但是ToLookup是立即执行的,而GroupBy之后的result是没有值的,只会在使用的时候执行并存储数据
    ** 连接操作符
        > Join操作符
            作用和sql的 inner Join 一样
            在扩展方法中使用
                例子一:
                    第一个参数是inner表,第二个参数是outer表的列,第三个参数是inner表的列,第四个参数是结果集的格式
                    当第二个参数和第三个参数对应的列值相同,那么数据筛选出来,然后经过第四个参数处理后返回
                    List<string> list1 = new List<string>() { "a", "b", "c", "d", "e" };
                    List<string> list2 = new List<string>() { "a", "b", "e", "f", "a" };
                    var result = list1.Join(list2, s1 => s1, s2 => s2, (s1, s2) => s1);
                例子二:
                    IList<Demo1> studentList1 = new List<Demo1>() {
                        new Demo1() { StudentID = 1, StudentName = "John", StandardID =1 },
                        new Demo1() { StudentID = 2, StudentName = "Moin", StandardID =1 },
                        new Demo1() { StudentID = 3, StudentName = "Bill", StandardID =2 },
                        new Demo1() { StudentID = 4, StudentName = "Ram" , StandardID =2 },
                        new Demo1() { StudentID = 5, StudentName = "Ron"  }
                    };
                    IList<Demo2> standardList2 = new List<Demo2>() {
                        new Demo2(){ StandardID = 1, StandardName="Standard 1"},
                        new Demo2(){ StandardID = 2, StandardName="Standard 2"},
                        new Demo2(){ StandardID = 3, StandardName="Standard 3"}
                    };
                    var result = studentList1.Join(standardList2, s1 => s1.StandardID, s2 => s2.StandardID, (s1, s2) =>
                    {
                        return new
                        {
                            StudentName = s1.StudentName,
                            StandardName = s2.StandardName
                        };
                    });
            在查询语句中使用
                使用上面的studentList1和standardList2为例
                    var result = from s1 in studentList1
                         join s2 in standardList2 on s1.StandardID equals s2.StandardID
                         select new
                         {
                             StudentName = s1.StudentName,
                             standardName = s2.StandardName
                         };
        > GroupJoin操作符
            作用和sql的 left join,right join类似
            在扩展方法中使用
                还是使用上面的studentList1和standardList2为例
                var result = studentList1.GroupJoin(standardList2, s1 => s1.StandardID, s2 => s2.StandardID, (s1, s2Obj) =>
                {
                    return new
                    {
                        studentName = s1.StudentName,
                        standardName = s2Obj // s2Obj自动将 s2 转成 IEnumerable类型
                    };
                });
            在查询语句中使用
                var result = from s1 in studentList1
                             join s2 in standardList2 on s1.StandardID equals s2.StandardID into s3
                             select new
                             {
                                 standard = s3,
                                 studentName = s1.StudentName
                             };
    ** 投影操作符
        > select操作符
            和sql中的select类似
            在查询语句中使用
                var result = from item in someList select item;
                var result = from item in someList select new { column1 = item.Name, column2 = item.Age };
            在扩展方法中使用
                在扩展方法中Select方法仅仅用来格式化数据,在查询数据时是可选项
                var result = list.Select(s => new
                {
                    Name = s.StudentName,
                    Age = s.Age
                });
        > SelectMany方法
            SelectMany方法只能在扩展方法中使用
            SelectMany的作用是将指定列对应的IEnumerable列值合并成一个集合,用法如下
                定义两个类
                    public class Students
                    {
                        public string Name { get; set; }
                        public int Age { get; set; }
                    }
                    public class ClassNames
                    {
                        public IEnumerable<Students> students { get; set; }
                        public string Name { get; set; }
                    }
                使用SelectMany
                    List<ClassNames> selectManyTest = new List<ClassNames>() { };
                    selectManyTest.Add(new ClassNames() { students = new List<Students>() { new Students() { Name = "小张", Age = 11 } }, Name = "班级一" });
                    selectManyTest.Add(new ClassNames() { students = new List<Students>() { new Students() { Name = "小王", Age = 21 } }, Name = "班级二" });
                    selectManyTest.Add(new ClassNames() { students = new List<Students>() { new Students() { Name = "小红", Age = 22 } }, Name = "班级三" });
                    var result = selectManyTest.SelectMany(s => s.students);
                result的结果 [{"Name":"小张","Age":11},{"Name":"小王","Age":21},{"Name":"小红","Age":22}]
                这种用法其实和Select的作用是一致的 var result = selectManyTest.Select(s => s.students); 关键是下面的这种用法
                var result = selectManyTest.SelectMany(s => s.students, (s1, s2) =>
                {
                    return new
                    {
                        班级名称 = s1.Name,
                        学生姓名 = s2.Name,
                        学生年龄 = s2.Age
                    };
                }); 
                结果是 [{"班级名称":"班级一","学生姓名":"小张","学生年龄":11},{"班级名称":"班级二","学生姓名":"小王","学生年龄":21},{"班级名称":"班级三","学生姓名":"小红","学生年龄":22}]
                从这里可以看出SelectMany的优势是将嵌套的复杂类型综合到一个集合中
    ** 量词操作符
        IList<TestClass> studentList = new List<TestClass>() {
            new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
            new TestClass() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
            new TestClass() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
            new TestClass() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
            new TestClass() { StudentID = 5, StudentName = "Ron" , Age = 19 }
        };
        > ALL操作符
            指定筛选条件如果元素全部满足则返回true
            bool result = studentList.All(s => s.Age > 18 && s.Age < 20); // false
        > Any操作符
            指定筛选条件只要有一个满足就返回true
            bool result = studentList.Any(s => s.Age > 18 && s.Age < 20); // true
        > Contains操作符
            值类型用法
                List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
                bool result = list.Contains(4);
            引用类型用法
                引用类型默认比较的是引用,如果要实现自定义比较引用类型,就要实现一个IEqualityComparer类
                创建一个IEqualityComparer类
                    public class StudentComparer : IEqualityComparer<TestClass>
                    {
                        public bool Equals(TestClass x, TestClass y)
                        {
                            if (x.StudentID == y.StudentID &&
                                x.StudentName.ToLower() == y.StudentName.ToLower() &&
                                x.Age == y.Age)
                            {
                                return true;
                            }
                            return false;
                        }
                        public int GetHashCode(TestClass obj)
                        {
                            Trace.WriteLine(obj.GetHashCode());
                            return obj.GetHashCode();
                        }
                    }
                使用
                    bool result = studentList.Contains(new TestClass() { StudentID = 2, StudentName = "Steve", Age = 15 }, new StudentComparer()); // true
    ** 聚合操作符
        在linq中聚合函数不能直接使用在查询语句中,但是可以像下面这种方式使用
        var result = (from s in studentList where s.Age > 20 select s).Average(s => s.Age);
        > Aggregate操作符
            用来累积结果
            重载一
                List<string> list = new List<string> { "a", "b", "c", "d" };
                var result = list.Aggregate((s1, s2) =>
                {
                    return s1 + s2;
                }); // "abcd"
            重载二,需要一个seed作为初始值
                List<string> list = new List<string> { "a", "b", "c", "d" };
                var result = list.Aggregate("结果是:", (s1, s2) =>
                {
                    return s1 + s2;
                }); // "结果是:abcd"
            重载三,需要一个Func代理函数用来对计算的结果做处理
                List<string> list = new List<string> { "a", "b", "c", "d" };
                var result = list.Aggregate(String.Empty, (s1, s2) =>
                {
                    return s1 += s2 + ",";
                }, res => res.Substring(0, res.Length - 1)); // "a,b,c,d"
        > Average操作符
            用来计算集合中的数字值的平均值
            计算由数字组成的集合的平均值
                List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
                var result = list.Average();
            传递lambda表达式,用来计算引用类型中的数字的平均值
                var result = studentList.Average(s => s.Age);
        > Count操作符
            返回集合的元素的个数
                var result = studentList.Count();
            返回满足给定条件的元素个数
                var result = studentList.Count(s => s.Age > 19);
        > Max操作符
            返回数字集合的最大值
                List<int> list = new List<int>() { 1, 2, 3, 7, 4, 5, 6 };
                var result = list.Max(); // 最大值 7
                var result = list.Max(s => s * 2); // 将元素乘2后计算最大值,14
            返回引用类型集合数字属性的最大值
                var result = studentList.Max(s => s.Age);
            对于引用类型可以实现自定义的Max比较
                先声明一个类,实现了 IComparable 接口
                    public class TestClass: IComparable<TestClass>
                    {
                        public int StudentID { get; set; }
                        public string StudentName { get; set; }
                        public int Age { get; set; }
                        public int CompareTo(TestClass other) // 用来筛选出StudentName长度最长的一项
                        {
                            if(this.StudentName.Length >= other.StudentName.Length)
                            {
                                return 1;
                            }
                            return 0;
                        }
                    }
                使用
                    IList<TestClass> studentList = new List<TestClass>() {
                        new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 2, StudentName = "Steve",  Age = 15 } ,
                        new TestClass() { StudentID = 3, StudentName = "Bill",  Age = 25 } ,
                        new TestClass() { StudentID = 4, StudentName = "Ram" , Age = 20 } ,
                        new TestClass() { StudentID = 5, StudentName = "Ron" , Age = 19 }
                    };
                    var result = studentList.Max(); // 直接调用Max即可
            Min的使用方式和Max是一直的,不再赘述
        > Sum操作符
            返回数字集合的总和
                List<int> list = new List<int>() { 1, 2, 3, 7, 4, 5, 6 };
                var result = list.Sum(); // 返回总和
                var result = list.Sum(s => s * 2 + 1); // 返回每一项乘2加1后的总和
            返回应用类型集合数字属性的总和
                var result = studentList.Sum(s => s.Age); // 计算年龄的总和
                var result = studentList.Sum(s => {
                    if(s.Age > 18)
                    {
                        return 1;
                    }
                    return 0;
                }); // 计算年龄大于18的人数
    ** 元素操作符
        List<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };
        > ElementAt操作符
            根据索引找出元素,超出范围会报错
            var result = intList.ElementAt(1); 
        > ElementAtOrDefault操作符
            根据索引找出元素,超出范围返回默认值,整数默认值是0,字符串默认值是null
            var result = intList.ElementAtOrDefault(9); 
        > First操作符
            返回第一个元素或者第一个满足条件的元素,如果给定的集合是空的,或者没有一个元素满足条件,那么会报错
            var result = intList.First(); // 返回集合中的第一个元素
            var result = intList.First(s => s > 30); // 返回满足条件的第一个元素
            Last的用法和First一致
        > FirstOrDefault操作符
            返回第一个元素或者第一个满足条件的元素,如果给定的集合是空的,或者没有一个元素满足条件那么返回默认值
            用法和上面一样
            LastOrDefault的用法和FirstOrDefault一致
        > Single操作符
            返回集合中的唯一一个元素,或者指定条件后唯一一个元素,如果没有元素或者多于一个元素那么报错
            List<int> intList = new List<int>() { 10 };
            var result = intList.Single();
        > SingleOrDefault操作符
            作用和Single类似,如果没有找到元素,那么返回默认值,然而,如果有多个元素那么同样报错
            List<int> intList = new List<int>() { 10,20 };
            var result = intList.SingleOrDefault(s => s > 10);
    ** 相等运算符
        > SequenceEqual
            用于比较两个集合中的元素是否相等,如果元素是值类型,那么比较元素的值和顺序,如果是引用类型那么比较值的引用
            值类型比较
                List<int> intList1 = new List<int>() { 10,20 };
                List<int> intList2 = new List<int>() { 10, 20 };
                var result = intList1.SequenceEqual(intList2); // true
            引用类型比较  
                List<TestClass> TestList1 = new List<TestClass>() { new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } };
                List<TestClass> TestList2 = new List<TestClass>() { new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } };
                var result = TestList1.SequenceEqual(TestList2); // false
            自定义引用类型比较
                如果不想比较引用类型的时候比较引用地址,那么可以使用自定义比较
                首先,创建一个实现了IEqualityComparer接口的类
                    private class SequenceEqualComparer : IEqualityComparer<TestClass>
                    {
                        public bool Equals(TestClass x, TestClass y)
                        {
                            if(x.StudentID == y.StudentID && x.StudentName == y.StudentName && x.Age == y.Age)
                            {
                                return true;
                            }
                            return false;
                        }
                        public int GetHashCode(TestClass obj)
                        {
                            return obj.GetHashCode();
                        }
                    }
                使用
                    List<TestClass> TestList1 = new List<TestClass>() { new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } };
                    List<TestClass> TestList2 = new List<TestClass>() { new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } };
                    var result = TestList1.SequenceEqual(TestList2, new SequenceEqualComparer()); // true
    ** 串联运算符
        > Concat运算符
            用于连接两个集合
            var result = TestList1.Concat(TestList2);
    ** 生成运算符
        > DefaultIfEmpty操作符
            如果调用此方法的集合是空的则返回一个新的集合,新集合包含一个默认值元素
            不指定默认值
                List<string> list = new List<string>();
                var result = list.DefaultIfEmpty(); // 结果 [null]
            指定默认值
                var result = list.DefaultIfEmpty("haha"); // 结果 ["haha"]
            本质
                DefaultIfEmpty本质作用是将一个集合拷贝了一份,如果集合是空的自动填充一个默认值
                List<string> list = new List<string>() { "a" };
                var temp = list.DefaultIfEmpty("haha");
                var result = temp == list; // false
        > Empty, Range, Repeat 操作符
            这三个运算符不是IEnumerable 和 IQueryable的扩展方法,而是Enumerable的静态方法
            Empty操作符
                返回一个空的集合
                var result = (Enumerable.Empty<string>()).GetType().Name; // "String[]"
            Range操作符
                返回一个集合,元素从指定的数字开始,按照指定的个数依次递增
                var result = Enumerable.Range(10, 10); // [10,11,12,13,14,15,16,17,18,19]
            Repeat操作符
                返回一个集合,元素是指定个数的相同的指定元素
                var result = Enumerable.Repeat<string>("a", 10); // ["a","a","a","a","a","a","a","a","a","a"]
    ** 集操作符
        > Distinct
            取出集合中重复项,并返回一个新的集合
            对值类型集合的操作
                List<string> list = new List<string>() { "a", "b", "c", "d", "b", "c" };
                var result = list.Distinct(); // ["a","b","c","d"]
            对引用类型集合的操作,默认无效,需要自己实现IEqualityComparer类
                首先实现IEqualityComparer类
                    Equals,GetHashCode方法会自动调用,当GetHashCode返回的整数(引用)不一致,那么会执行Equals方法
                    private class DistinctComparer : IEqualityComparer<TestClass>
                    {
                        public bool Equals(TestClass x, TestClass y)
                        {
                            if(x.StudentID == y.StudentID && x.StudentName == y.StudentName && x.Age == y.Age)
                            {
                                return true;
                            }
                            return false;
                        }
                        public int GetHashCode(TestClass obj)
                        {
                            return obj.StudentID.GetHashCode(); // 注意,表示如果 StudentID的GetHashCode 相同,那么过滤掉这个数据
                        }
                    }
                使用
                    List<TestClass> studentList = new List<TestClass>() {
                        new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 2, StudentName = "Steve",  Age = 15 } 
                    };
                    var result = studentList.Distinct(new DistinctComparer());
        > Except
            返回一个新的集合,集合的成员是list1在list2中不存在的元素
            对值类型集合的操作
                List<string> list1 = new List<string>() { "a", "b", "c" };
                List<string> list2 = new List<string>() { "c", "d", "a" };
                var result = list1.Except(list2); // ["b"]
            对引用类型集合的操作,同样也要自己实现IEqualityComparer类
                创建一个IEqualityComparer类,和上面一模一样
                    private class ExceptComparer : IEqualityComparer<TestClass>
                    {
                        public bool Equals(TestClass x, TestClass y)
                        {
                            if (x.StudentID == y.StudentID && x.StudentName == y.StudentName && x.Age == y.Age)
                            {
                                return true;
                            }
                            return false;
                        }
                        public int GetHashCode(TestClass obj)
                        {
                            return obj.StudentID.GetHashCode(); // 注意,表示如果 StudentID的GetHashCode 相同,那么过滤掉这个数据
                        }
                    }
                使用
                    List<TestClass> studentList1 = new List<TestClass>() {
                        new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 2, StudentName = "Steve",  Age = 15 }
                    };
                    List<TestClass> studentList2 = new List<TestClass>() {
                        new TestClass() { StudentID = 1, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 3, StudentName = "John", Age = 18 } ,
                        new TestClass() { StudentID = 4, StudentName = "Steve",  Age = 15 }
                    };
                    var result = studentList1.Except(studentList2, new ExceptComparer()); // [{"StudentID":2,"StudentName":"Steve","Age":15}]
        > Intersect
            相当于取交集
            值类型集合操作
                List<string> list1 = new List<string>() { "a", "b", "c" };
                List<string> list2 = new List<string>() { "c", "d", "a" };
                var result = list1.Intersect(list2); // ["a","c"]
            引用类型集合操作,同样也是要创建IEqualityComparer类,实现和上面是一模一样,不再赘述
            使用
                var result = studentList1.Intersect(studentList2, new IntersectComparer());
                // 返回结果 [{"StudentID":1,"StudentName":"John","Age":18}]
        > Union
            将两个集合拼接起来,并且剔除掉重复项
            值类型集合操作
                List<string> list1 = new List<string>() { "a", "b", "c", "a", "b" };
                List<string> list2 = new List<string>() { "c", "d", "a" };
                var result = list1.Union(list2); // ["a","b","c","d"]
            引用类型集合操作,同样也是要创建IEqualityComparer类,实现和上面是一模一样,不再赘述
            使用
                var result = studentList1.Union(studentList2, new UnionComparer());
                返回结果如下
                /*
                    [
                        {"StudentID":1,"StudentName":"John","Age":18},
                        {"StudentID":2,"StudentName":"Steve","Age":15},
                        {"StudentID":3,"StudentName":"John","Age":18},
                        {"StudentID":4,"StudentName":"Steve","Age":15}
                    ]
                */
    ** 分区操作符
        > Skip操作符
            过滤掉集合中指定个数的元素,返回剩余元素组成的集合
            List<string> list = new List<string>() { "a", "b", "c" };
            var result = list.Skip(2); // ["c"]
        > SkipWhile操作符
            指定条件过滤,如果条件满足全部过滤掉,直到条件为false,返回剩余元素组成的集合
            List<int> list = new List<int>() { 1, 2, 5, 2, 4, 5, 6 };
            var result = list.SkipWhile((s, i) => s < 3); // [5,2,4,5,6]
        > Take操作符
            功能和Skip相反,获取指定数量的元素,并返回新的集合
            List<string> list = new List<string>() { "a", "b", "c" };
            var result = list.Take(2); // ["a","b"]
        > TakeWhile操作符
            功能和SkipWhile相反,获取满足指定条件的元素,直到条件不满足,返回新的集合
            List<int> list = new List<int>() { 1, 2, 5, 2, 4, 5, 6 };
            var result = list.TakeWhile((s, i) => s < 3); // [1,2]
    ** 转换运算符
        > AsEnumerable操作符
            将集合强制转换成IEnumerable集合
            int[] arr = new int[] { 1, 2, 3, 4, 5 };
            var result = arr.AsEnumerable();
        > AsQueryable操作符
            将集合强制转换成IQueryable集合
            List<int> list = new List<int>() { 1, 2, 5, 2, 4, 5, 6 };
            var result = list.AsQueryable();
        > Cast操作符
            作用和AsEnumerable是一样的
            int[] arr = new int[] { 1, 2, 3, 4, 5 };
            var result = arr.Cast<int>(); 等效于 var result = (IEnumerable<int>)arr;
        > ToArray,ToList,ToDictionary扩展方法
            To系列的操作,会强制让sql语句执行结果并且将数据缓存在本地
            int[] arr = new int[] { 1, 2, 3, 4, 5 };
            ToArray使用
                int[] result = arr.ToArray<int>();
            ToList使用
                List<int> result = arr.ToList<int>();
            ToDictionary使用
                var result = arr.ToDictionary<int, int>(s => s);
    
  3. Linq中的Expression类型

    由Func和Action类型的委托构建的lambada表达式在编译期间会转换成可执行的代码,linq引入新的Expression类型,将lambda表达式转换成Expression树而不是可执行代码
    传统写代理的方式
        Func<int, bool> isTest = s => s > 5;
    Expression写代理的方式
        Expression<Func<int, bool>> isTest = s => s > 5;
    调用Expression代理
        调用之前需要先编译一下
        Func<int, bool> isIWant = isTest.Compile();
        bool result = isIWant(3);
    为什么要有表达式树?
        编译的区别
            Func编译后的代码 {Method = {Boolean <.ctor>b__9_0(Int32)}}
            Expression编译后的代码 {s => (s > 5)}
            很明显的感觉到,Func编译后被改了,表达式树更加的透明,你可以轻松的调用Expression的方法访问方法的每一个部分
        处理代码的区别
            Func值适合当前程序集中运行,也就是说只适合内存中的集合,因为如果涉及数据库查询的话,数据库无法获知编译后的IL代码到底是个啥
            Expression表达式树,专门用来处理类似数据库的操作
        执行效率
            Func要想操作数据库,需要借助Entity Frameworks将可执行代码编译成Sql查询字符串发送到sql server数据库中
            使用Expression表达式树,可以轻松的将lambda表达式转换成Sql语句,因为Expression内置了很多属性和方法方便获取表达式的每一部分,所以执行效率更快
        总之,一句话,操作数据库使用Expression更好
    
  4. 延迟执行linq查询

    linq表达式不会立即执行,而是用到了才会调用,从而保证每次使用linq时都是最新的数据
    默认情况下linq的延迟加载不会有什么特殊的效果,只是简单的延迟而已,但是你可以自定义扩展方法使用yield关键字实现自己的延迟效果
    首先,要定义扩展方法,需要一个顶级静态类
        public static class EnumerableExtensionMethodsDemo
        {
            public static IEnumerable<int> GetIWant(this List<int> test)
            {
                foreach (int item in test)
                {
                    if(item > 5 && item < 10)
                    {
                        yield return item;
                    }
                }
            }
        }
    使用
        List<int> list = new List<int>() { 1, 2, 10, 50, 30, 5, 7, 9, 11 };
        var result = from s in list.GetIWant() select s;
        foreach (int i in result) 
        {
            // 在这里才会调用result查询语句
        }
    
  5. 立即执行linq查询

    上面讲到的以to开头的扩展方法,都可以让linq语句立即执行
    
  6. linq中使用let关键字

    List<string> list = new List<string>() { "sa", "rb", "bc", "sd" };
    var result = from s in list where s.ToLower().StartsWith("s") select s.ToLower();
    上面这个查询语句中 ToLower 使用了两次,很啰嗦,可以使用let简化
    var result = from s in list
                 let lowerItem = s.ToLower()
                 where lowerItem.StartsWith("s")
                 select lowerItem;
    
  7. into关键字

    into关键字单独使用的作用,是将上个查询语句的结果传递到下一个查询语句中,两个查询语句中的变量互不影响
    List<string> list = new List<string>() { "sa", "rb", "bc", "sd" };
    var result = from s in list
                 where s.ToLower().StartsWith("s")
                 select s
                 into s
                 where s.ToLower().EndsWith("a")
                 select s; // ["sa"]
    
原文地址:https://www.cnblogs.com/ye-hcj/p/8098006.html