【C#】LinQ
https://www.cnblogs.com/lifepoem/archive/2011/10/25/2223765.html
1.前言
sequence是实现了IEnumerable
查询语法:类似SQL语句。
方法语法:System.Linq.Enumerable中定义了40个查询运算符。链式查询运算符。(https://www.cnblogs.com/lifepoem/archive/2011/10/27/2226556.html)
两种语法互补使用,方法语法使用的更多。
LINQ中最基本的数据单元是sequences和elements。
2.方法语法
(1)查询运算符签名
Func<TSource, bool>委托匹配 TSource => bool表达式,接受TSource输入参数,返回一个bool值。当结果为true时,表示该元素会包含在输出sequence中。
public static IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate) //静态方法写法: IEnumerable<string> query = System.Linq.Enumerable.Where(names, n=>n.Contains('a')); //Where第一个参数为实现了IEnumerable的集合,第二个参数为返回值为bool的委托。 //扩展方法写法: IEnumerable<string> query=name.Where(n=>n.Contains('a')); //扩展方法的写法可以方便的写成链式操作 //使用var简写 var query = name.Where(n=>n.Contains('a'));
大部分查询运算符都接受一个lambda表达式作为参数,Lambda表达式格式为:(parameters) => expression-or-statement-block。
正是扩展方法让LINQ查询运算符的链接成为了可能。
(2)常用查询运算符
而对Where查询运算符来讲,它并不需要对输出element进行类型推断,因为它只是对输入elements进行过滤而不作转换,因此输出element和输入element具有相同的数据类型。
where 从句表示一种筛选过程,当条件满足的时候,这样的对象才可能被迭代返回出来。如果多次条件筛选的话,那就相当于是 && 处理一样的效果。
var student = from student in students where student.Age >= 18 where student.Chinese + student.English + student.Math >= 180 //多次的Where相当于查询条件的&& select student.Name;
对于OrderBy查询运算符来讲,Func<TSource, TKey>把输入元素映射至一个排序键值。TKey由Lambda表达式的结果推断出来,比如我们可以按长度或按字母顺序对names数组进行排序。
First()——返回sequence中的第一个element
Last()——返回最后一个element
ElementAt()——返回某个index的element
OrderBy(lambda)——按照lambda指定的属性排序
Count()——对sequence计数
Min()——返回sequence的min
Max()
Contains(T ele)——返回是否包含某个ele
Any()——判断sequence中是否存在满足条件的ele
int[] intArray = new int[] { 0, 1, 2, 3 }; var items = intArray.Any(i => ((i % 2) == 0)); foreach (int item in items) Console.WriteLine(item); Console.ReadLine();
All()——判断sequence中是否所有ele都满足条件
Concat()——两个sequence连接在一起
Union()——两个sequence取并集
3.查询语法
(1)查询语法及使用
查询表达式要求要以Select或Group结束。
(2)方法语法和查询语法结合
方法语法有查询运算符,有的查询运算符没有对应的查询语法,如:Count()、First()、Last()。这种需要先以查询语法来写,最后结合查询运算符。适用于较为复杂的查询需求。
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" }; // 计算包含字母”a”的姓名总数 int matches = (from n in names where n.Contains("a") select n).Count(); // 3 // 按字母顺序排序的第一个名字 string first = (from n in names orderby n select n).First(); // Dick
4.Linq的延迟执行
https://www.cnblogs.com/lifepoem/archive/2011/10/29/2228589.html
他们不是在查询创建的时候执行,而是在遍历的时候执行(换句话说,当enumerator的MoveNext方法被调用时)。
(1)重复查询
延迟执行带来的一个影响是,当我们重复遍历查询结果时,查询会被重复执行。
调用ToArray、ToList可令Linq立刻执行,将查询结果保存到Array和List<>中。
(2)局部变量捕获
如果查询的lambda表达式引用了程序的局部变量时,如果在查询定义之后改变了该变量的值,那么查询结果也会随之改变。
int[] numbers = { 1, 2 }; int factor = 10; IEnumerable<int> query = numbers.Select(n => n * factor); factor = 20; foreach (int n in query) Console.Write(n + "|"); // 20|40|
这个特性在我们通过foreach循环创建查询时会变成一个真正的陷阱。假如我们想要去掉一个字符串里的所有元音字母,我们可能会写出如下的query:
复制代码
IEnumerable
query = query.Where(c => c != 'a'); query = query.Where(c => c != 'e'); query = query.Where(c => c != 'i'); query = query.Where(c => c != 'o'); query = query.Where(c => c != 'u'); foreach (char c in query) Console.Write(c); //Hw r y, frnd.
复制代码
尽管程序结果正确,但我们都能看出,如此写出来的程序不够优雅。所以我们会自然而然的想到使用foreach循环来重构上面这段程序:
复制代码
IEnumerable
foreach(char vowel in "aeiou") query = query.Where(c => c != vowel); foreach (char c in query) Console.Write(c); //How are yo, friend.
复制代码
结果中只有字母u被过滤了,咋一看,有没有吃一惊呢!但只要仔细一想就能知道原因:因为vowel定义在循环之外,所以每个lambda表达式都捕获了同一变量。当我们的query执行时,vowel的值是什么呢?不正是被过滤的字母u嘛。要解决这个问题,我们只需把循环变量赋值给一个内部变量即可,如下面的temp变量作用域只是当前的lambda表达式。
复制代码
IEnumerable
foreach (char vowel in "aeiou") { char temp = vowel; query = query.Where(c => c != temp); } foreach (char c in query) Console.Write(c); //Hw r y, frnd.
复制代码
(3)查询运算符的模型和延迟执行
和传统的集合类型如array,linked list不同,一个装饰者sequence并没有自己用来存放元素的底层结构,而是包装了我们在运行时提供的另外一个sequence。此后当我们从装饰者sequence中请求数据时,它就会转而从包装的sequence中请求数据。
比如调用Where会创建一个装饰者sequence,其中保存了输入sequence的引用、lambda表达式还有其他提供的参数。下面的查询对应的装饰者sequence如图所示:
IEnumerable<int> lessThanTen = new int[] { 5, 12, 3 }.Where(n => n < 10);
当我们遍历lessThanTen时,实际上我们是在通过Where装饰者从Array中查找数据。
而查询运算符链接创建了一个多层的装饰者,每个查询运算符都会实例化一个装饰者来包装前一个sequence,比如下面的query和对应的多层装饰者sequence:
IEnumerable<int> query = new int[] { 5, 12, 3 } .Where(n => n < 10) .OrderBy(n => n) .Select(n => n * 10);
在我们遍历query时,我们其实是在通过一个装饰者链来查询最初的array。
需要注意的是,如果在上面的查询后面加上一个转换运算符如ToList,那么query会被立即执行,这样,单个list就会取代上面的整个对象模型。
5.过滤运算符
https://www.cnblogs.com/lifepoem/archive/2011/11/16/2250676.html
6.排序、分组
- OrderBy, ThenBy——升序排序
ThenBy只会对那些在前一次排序中拥有相同键值的elements进行重新排序,我们可以连接任意数量的ThenBy运算符。// 先按长度排序,然后按第二个字符排序,再按第一个字符排序 IEnumerable<string> query = names.OrderBy (s => s.Length).ThenBy (s => s[1]).ThenBy (s => s[0]); - OrderByDescending, ThenByDescending——对一个sequence按降序排序
- Reverse——按倒序返回一个sequence
自定义排序规则
要排序先要定义比较规则。
基本型别都提供了默认的比较算法,如string提供了按字母进行比较,int提供了按整数大小进行比较。
- 当我们创建了自己的实体类,如Student,默认想要对其按照年龄进行排序,则需要为实体类继承IComparable接口,实现CompareTo方法。
class Student:IComparable { public string Name { get; set; } public int Age { get; set; } #region IComparable Members public int CompareTo(object obj) { Student student = obj as Student; if (Age > student.Age) { return 1; } else if (Age == student.Age) { return 0; } else { return -1; } //return Age.CompareTo(student.Age); } #endregion }
- 如果不想使用年龄作为比较器了,那怎么办。这个时候IComparer的作用就来了,可使用IComparer来实现一个自定义的比较器。
class SortName: IComparer { #region IComparer Members public int Compare(object x, object y) { Student s1 = x as Student; Student s2 = y as Student; return s1.Name.CompareTo(s2.Name); } #endregion }
在排序的使用为Sort方法提供此比较器:
studentList.Sort(new SortName());
- 上面的代码我们使用了一个已经不建议使用的集合类ArrayList。当泛型出来后,所有非泛型集合类已经建议不尽量使用了。进行了装箱和拆箱。而这是会影响性能的。如果我们的集合中有成千上万个复杂的实体对象,则在排序的时候所耗费掉的性能就是客观的。而泛型的出现,就可以避免掉拆箱和装箱。故上文代码中的ArrayList,应该换成List
,对应的,我们就该实现IComparable 和IComparer 。
class Student:IComparable<Student> { public string Name { get; set; } public int Age { get; set; } #region IComparable<Student> Members public int CompareTo(Student other) { return Age.CompareTo(other.Age); } #endregion } class SortName: IComparer<Student> { #region IComparer<Student> Members public int Compare(Student x, Student y) { return x.Name.CompareTo(y.Name); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)