C# Linq
linq可以对多种数据源和对象进行查询,如数据库、数据集、XML文档、数组等。
通过对linq的使用,可以减少代码量并优化检索操作。
LINQ关键字
from 指定数据源和范围变量
where 根据布尔表达式(由逻辑与 或 等组成)从数据源中筛选元素
select 指定查询结果中的元素所具有的类型或表现形式
group 对对查询结果按照键值进行分组
into 提供一个标示符,它可以充当对 join group 或 select 子句结果的引用
orderby 对查询出的元素进行排序
join 按照两个指定匹配条件来联接俩个数据源
let 产生一个用于查询表达式中子表达式查询结果的范围变量
1. 组成
// 1,获取数据源 List<int> numbers = new List<int>() { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; // 2,创建查询 var numQuery = from num in numbers where num % 2 == 0 select num; // 3,执行查询 foreach (var num in numQuery) { Console.WriteLine("{0,1}", num); }
static void Main(string[] args) { //获取数据源 string[] names = { "Alen", "Zhen", "Chen","Song", "Hel", "Ken", "Aln", "Zen", "Cen", "He", "Ke", }; //创建查询 var queryResults = from n in names where n.StartsWith("S") select n; Console.WriteLine("Names beginning with S:"); //执行查询 foreach (var item in queryResults) { Console.WriteLine(item); } Console.Write("Program finished,press Enter/Reurn to continue:"); Console.ReadLine(); }
2. 语句使用
//1.用var关键字声明结果变量 //2.指定数据源:from 子句 //3.指定条件:where 子句 //4.指定元素:select子句 //5.完成:使用foreach循环 string[] names = { "Alono", "Zheng", "Yuan", "Song", "Simle", "Hsieh", "Small", "She", "Sza", "Sam", "Fa", "Iyl" }; var queryResults = from n in names where n.StartsWith("S") orderby n descending//按照最后一个字母排序 ordeby n.Substring(n.Length - 1) select n;//查询语法 var queryResults1 = names.OrderBy(n => n).Where(n => n.StartsWith("S"));//方法语法 Lambda 表达式 // var queryResults = names.OrderByDescending(n => n).Where(n => n.StartsWith("S")); foreach (var item in queryResults) Console.WriteLine(item); Console.ReadKey();
2.1 from语句
创建一个LINQ表达式必须要以from子句开头
单个from语句
//获取数据 string[] a = { "日本","德国","英国","美国","澳大利亚"}; //创建查询,创建一个LINQ表达式必须要以from子句开头 var myQuery = from n in a where n.IndexOf("国") > 0 select n; //执行查询 foreach(var item in myQuery) { Console.WriteLine(item); } Console.ReadKey();
复合from子句
如果一个集合中的某个对象依旧是一个集合,就可以使用from
子句进行筛选。
public class CustomerInfo1 { public string Name { get; set; } public int Age { get; set; } public List<string> TelTable { get; set; } } class Test03_MultiFrom { static void Main(string[] args) { //获取数据源 List<CustomerInfo1> customer = new List<CustomerInfo1> { new CustomerInfo1{Name = "郭啸天",Age = 100,TelTable = new List<string>{"123321","321123"}}, new CustomerInfo1{Name = "郭靖",Age = 80,TelTable = new List<string>{"456654","654456"}}, new CustomerInfo1{Name = "郭芙", Age = 60,TelTable = new List<string>{"789987","987789"}} }; //创建查询 var query = from CustomerInfo1 ci in customer from tel in ci.TelTable where tel.IndexOf("789987") > -1 select ci;
var query1=customer.SelectMany(c => c.TelTable, (c,r) => new { CustomerInfo1 = c, Tel = r }).Where(u=>u.Tel.IndexOf("789987")>-1); //执行查询 foreach (var item in query) { Console.WriteLine("姓名:{0} 年龄:{1}", item.Name, item.Age); foreach (var tel in item.TelTable) { Console.WriteLine("电话:{0}", tel); } } Console.ReadKey(); } }
2.2 where 语句
where子句的作用就是筛选元素,除了开始和结束位置, where子句几乎可以出现在LINQ表达式的任意位置。一个LINQ表达式中可以有where子句,也可以没有;可以有一个,可以有多个;多个where子句之间的关系相当于逻辑“与”,每个where子句可以包含1个或多个逻辑表达式,这些条件成为“谓词”,多个谓词之间用布尔运算符隔开,比如逻辑“与”用&&,逻辑“或”用||,而不是用SQL中的AND或OR。
//查询语句
var racrers = from r in Formulal.GetChampions() where r.Wins > 15 && (r.Country == "China" || r.Country == "UK") select r;
//扩展方法
var racres2 = Formulal.GetChampions() .Where(r => r.Wins > 15 && (r.Country == "China" || r.Country == "UK")) .Select(r => r);
这两种执行的结果都是一样的。但是并不是所有的LINQ查询都可以使用LINQ查询表达式完成,也不是所有的LINQ扩展方法都能够映射到LINQ查询表达式语句上。LINQ扩展方法使用的范围要比查询表达式更广泛,尤其是一些高级查询,只能或更多的是使用扩展方法完成。同时,这两种形式也可以组合使用。
//使用索引筛选,只能使用扩展方法,不能使用Linq查询语句 var racers3 = Formulal.GetChampions() .Where((r, index) => r.LastName.StartsWith("A") && index % 2 != 0);
2.3 排序子句
OrderBy/OrderByDescending/ThenBy/ThenByDescending
OrderBy()
:按升序对序列的元素进行排序。
ThenBy()
:按升序对序列中的元素进行后续排序。
OrderByDescending()
:按降序对序列的元素进行排序。
ThenByDescending()
:按降序对序列中的元素进行后续排序。
//查询语句 var racers3 = (from r in Formulal.GetChampions() orderby r.Country, r.LastName, r.FirstName ascending select r).Take(10);
//扩展方法 var racers4 = Formulal.GetChampions() .OrderBy(r => r.Country) .ThenBy(r => r.LastName) .ThenByDescending(r => r.FirstName) .Take(10);
2.4 分组
GroupBy
如果要根据一个关键字值 对查询结果分组,可以使用group
子句。对应的扩展方法为GroupBy()
。
//查询语句 var countries = from r in Formulal.GetChampions() group r by r.Country into g //将分组信息放入标识符g中 orderby g.Count() descending, g.Key where g.Count() >= 2 select new { Country = g.Key, Count = g.Count() }
//扩展方法 var countries2 = Formulal.GetChampions() .GroupBy(r => r.Country) .OrderByDescending(g => g.Count()) .ThenBy(g => g.Key) .Where(g => g.Count() >= 2) .Select(g => new { Country = g.Key, Count = g.Count() });
2.5 Let子句 及 匿名类属性
上述的分组查询语句,中间多次调用了Count
方法,使用let
子句可以改变这种方式。let
允许在LINQ查询中定义变量。
var countries = from r in Formulal.GetChampions() group r by r.Country into g let count = g.Count() orderby count descending, g.Key where count >= 2 select new { Country = g.Key, Count = count };
使用方法语法时,为了避免Count
方法被调用多次,可以使用Select
方法创建一个匿名类型,将Count
方法的结果作为匿名类的属性进行传递
var countries2 = Formulal.GetChampions() .GroupBy(r => r.Country) .Select(g => new { Group = g, Count = g.Count() }) .OrderByDescending(g => g.Count) .ThenBy(g => g.Group.Key) .Where(g => g.Count >= 2) .Select(g => new { Country = g.Group.Key, Count = g.Count });
注意:应考虑根据let
子句或Select
方法创建的临时对象的数量 。查询大列表时,创建的大量对象需要以后进行垃圾收集 ,这可能对性能产生巨大影响。
2.6 join子句
var racersAndTeams = (from r in racers join t in teams on r.Year equals t.Year orderby t.Year select new { r.Year, Champion = r.Name, Constructor = t.Name }).Take(10);
左外连接
var racersAndTeams = (from r in racers join t in teams on r.Year equals t.Year into rt from t in rt.DefaultIfEmpty() orderby r.Year select new { r.Year, Champion = r.Name, Constructor = t == null ? "no constructor" : t.Name }).Take(10);
2.7 集合操作
Intersect()
:通过使用的默认相等比较器对值进行比较,生成两个序列的交集。
Distinct()
:返回序列中通过使用指定的非重复元素。
Union()
:通过使用默认的相等比较器生成的两个序列的并集。
Except()
:通过使用默认的相等比较器对值进行比较,生成两个序列的差集。
List<Order> orders = new List<Order>{ new Order {ID="A",Amount=100}, new Order {ID ="B",Amount =200}, new Order {ID ="H",Amount =300}}; var customersIDs = from c in customers select c.ID; var ordersIDs = from o in orders select o.ID; var customersWithOrders = customersIDs.Intersect(ordersIDs);//Intersect() foreach (var item in customersWithOrders ) Console.WriteLine(item ); Console.WriteLine("-------------------------"); var ordersNoCustomers = ordersIDs.Except(customersIDs);//Except() foreach (var item in ordersNoCustomers ) Console.WriteLine(item ); Console.WriteLine("-------------------------"); var allCustomersOrders = ordersIDs.Union(customersIDs);//Union() foreach (var item in allCustomersOrders) Console.WriteLine(item); //Console.ReadKey();
2.8 分区
Take和TakeWhile,Skip和SkipWhile
Take():从序列的开头返回指定的数量的连续元素。
TakeWhile():只要指定的条件为true,就返回序列中的元素,然后跳过其余元素。
Skip():跳过指定的数量的序列中的元素,然后返回剩余元素。
SkipWhile():只要指定的条件为真,就会跳过序列中的元素,然后返回剩余的元素。
int pageSize = 5; int numberPages = (int)Math.Ceiling(Formulal.GetChampions().Count() / (double)pageSize); for (int page = 0; page < numberPages; page++) { Console.WriteLine("Page "+page); var racers = (from r in Formulal.GetChampions() orderby r.LastName, r.FirstName select r.FirstName + " " + r.LastName) .Skip(page * pageSize).Take(pageSize); foreach(var name in racers) { Console.WriteLine(name); } Console.WriteLine( ); }
2.9 聚合操作符
聚合操作符(如Count、Sum、Min、Max、Average和Aggregate操作符)不返回一个序列,而返回一个值。
Count():返回序列中的元素数。
Sum():计算一系列数值的总和。
Min():返回值序列中的最小值。
Max():返回值序列中的最大值。
Average():计算一系列数值的平均值。
Aggregate(): 对一个序列应用累加器函数。
Console.WriteLine("Count():"); var query = from r in Formulal.GetChampions() let numberYears = r.Years.Count() where numberYears >= 3 orderby numberYears descending, r.LastName select new { Name = r.FirstName + " " + r.LastName, TimesChampion = numberYears }; foreach(var r in query) { Console.WriteLine(r.Name+" "+r.TimesChampion); } Console.WriteLine(); Console.WriteLine("Sum():"); var countries = (from c in from r in Formulal.GetChampions() group r by r.Country into c select new { Country = c.Key, Wins = (from r1 in c select r1.Wins).Sum() } orderby c.Wins descending, c.Country select c).Take(5); foreach(var country in countries) { Console.WriteLine(country.Country+" "+country.Wins); }
==============================================相关知识点扩展==================================================
相关知识点扩展
1 var 、 dynamic 及匿名类型
var model = new //3.0 { Id = 2, Name = "undefined", Age = 25, ClassId = 2, }
Console.WriteLine(model.Id); //编译错误
Console.WriteLine(model, Name); //编译错误
dynamic model = new //4.0 dynamic避开编译器检查
{
Id = 2,
Name = "undefined",
Age = 25,
ClassId = 2,
}
Console.WriteLine(model.Id); //编译不会报错
Console.WriteLine(model, Name); //编译不会报错
2. 扩展方法
数据准备
/// <summary> /// 学生实体 /// </summary> public class Student { public int Id { get; set; } public int ClassId { get; set; } public string Name { get; set; } public int Age { get; set; } public void Study() { Console.WriteLine("{0} {1}跟着Eleven老师学习.net高级开发", this.Id, this.Name); } public void StudyHard() { Console.WriteLine("{0} {1}跟着Eleven老师努力学习.net高级开发", this.Id, this.Name); } ////学习实战班 //public void StudyPractise() //{ // Console.WriteLine("{0} {1}跟着Mark老师学习.net实战开发", this.Id, this.Name); //} }
/// <summary> /// 扩展方法:静态类里面的静态方法,第一个参数类型前面加上this /// </summary> public static class ExtendMethod { public static void StudyPractise(this Student student) { Console.WriteLine("{0} {1}跟着Mark老师学习.net实战开发", student.Id, student.Name); } }
方法调用
//扩展方法调用,很像实例方法,就像扩展了Student的逻辑
//1 第三方的类,不适合修改源码,可以通过扩展方法增加逻辑
//优先调用实例方法,最怕扩展方法增加了,别人类又修改了
//2 适合组件式开发的扩展(.NetCore),定义接口或者类,是按照最小需求,但是在开发的时候又经常需要一些方法,就通过扩展方法 context.Response.WriteAsync 中间件的注册
//3 扩展一些常见操作
//会污染基础类型,一般少为object 没有约束的泛型去扩展
student.Study();
ExtendMethod.StudyPractise(student);
student.StudyPractise();
3. yield 迭代器
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MyLinq { /// <summary> /// http://www.cnblogs.com/YamatAmain/p/5016464.html /// /// 含有yield的函数说明它是一个生成器,而不是普通的函数。当程序运行到yield这一行时,该函数会返回值,并保存当前域的所有变量状态; /// 等到该函数下一次被调用时,会从上一次中断的地方开始执行,一直遇到下一个yield, 程序返回值, 并在此保存当前状态; 如此反复,直到函数正常执行完成。 /// /// 迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。 /// 简单来说,迭代器模式使得你能够获取到序列中的所有元素 而不用关心是其类型是array,list,linked list或者是其他什么序列结构。 /// 这一点使得能够非常高效的构建数据处理通道(data pipeline)--即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是LINQ的核心模式。 /// 在.NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接 口,那么就能够被迭代; /// 调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,他是 数据序列中的一个位置记录。 /// 迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。 /// </summary> public class YieldShow { /// <summary> /// /// </summary> public void Show() { IEnumerable<int> iterable = this.CreateEnumerable();//1 不会直接执行 //IEnumerator iterator = iterable.GetEnumerator(); IEnumerator<int> iterator = iterable.GetEnumerator(); Console.WriteLine("开始迭代"); while (true) { Console.WriteLine("调用MoveNext方法……"); Boolean result = iterator.MoveNext();//2 正式开启CreateEnumerable Console.WriteLine("MoveNext方法返回的{0}", result); if (!result) { break; } Console.WriteLine("获取当前值……"); Console.WriteLine("获取到的当前值为{0}", iterator.Current); } Console.ReadKey(); } /// <summary> /// 普通获取数据 /// </summary> /// <returns></returns> public IEnumerable<int> CommonMethod() { List<int> results = new List<int>(); int counter = 0; int result = 1; while (counter++ < 10) { Thread.Sleep(1000); Console.WriteLine($"获取{counter}次数据"); result = result * 2; results.Add(result); } return results; } /// <summary> /// yield获取数据 /// </summary> /// <returns></returns> public IEnumerable<int> YieldMethod() { int counter = 0; int result = 1; while (counter++ < 10) { Thread.Sleep(1000); Console.WriteLine($"获取{counter}次数据"); result = result * 2; yield return result; } } public IEnumerable<int> CreateEnumerable() { try { Console.WriteLine("{0} CreateEnumerable()方法开始", DateTime.Now); for (int i = 0; i < 5; i++) { Console.WriteLine("{0}开始 yield {1}", DateTime.Now, i); yield return i; Console.WriteLine("{0}yield 结束", DateTime.Now); if (i == 4) { yield break;//直接终结迭代 4会出现的,, } } Console.WriteLine("{0} Yielding最后一个值", DateTime.Now); yield return -1; Console.WriteLine("{0} CreateEnumerable()方法结束", DateTime.Now); } finally { Console.WriteLine("停止迭代!"); } } } }
付费内容,请联系本人QQ:1002453261
本文来自博客园,作者:明志德道,转载请注明原文链接:https://www.cnblogs.com/for-easy-fast/p/12340925.html