本文主要演示Linq常用拓展方法。包括Where(),Single(),SingleOrDefault(),First(),
FirstOrDefault(),Count(),Any(),OrderBy(),Skip(),Take(),Max(),
Min(),Sum(),Average(),GroupBy(),Select(),ToList(),ToArray()。学会并掌握常用的Linq常用拓展方法,可以更快的处理数据源,其语法逻辑与SQL类似。
Linq语法常用于IEnumerable<泛型>这种可遍历的数据,如List<泛型> Array等。其底层逻辑就是遍历每一个元素,用一定的规则来判定元素是否符合标准。这个规则通常就是扩展方法中传入的委托参数。比如IEnumerable
委托变量通常指向一个用Lamda表达式描述的匿名方法,所以学习Linq之前还需对委托及Lamda表达式有一定的了解!
如果又不太了解的读友,可以参考我的前一篇文章 委托、委托与lamda表达式的关系
public class Employee
{
public long Id;
public string Name;
public int Age;
public int Salary;
public int Gender;
public Employee(long id, string name, int age, int salary, int gender)
{
Id = id;
Name = name;
Age = age;
Salary = salary;
Gender = gender;
}
public override string ToString()
{
return $"Id:{Id},Name:{Name},Age:{Age},Salary:{Salary},Gender:{Gender}";
}
}
List<Employee> list = new List<Employee>();
list.Add(new Employee(1,"llk",28,4000,1));
list.Add(new Employee(2, "lom", 28, 7000, 1));
list.Add(new Employee(3, "lombok", 35, 9000, 0));
list.Add(new Employee(4, "lld", 28, 8200, 0));
list.Add(new Employee(5, "ssf", 25, 6000, 1));
list.Add(new Employee(6, "ghj", 45, 12000, 0));
list.Add(new Employee(7, "xxk", 28, 8200, 0));
list.Add(new Employee(8, "qm", 25, 6000, 1));
list.Add(new Employee(9, "fmh", 38, 9000, 0));
IEnumerable Where(this IEnumerable source, Func
predicate);
对source中的每个元素进行bool条件判断,为true时加入需要返回的集合中
IEnumerable<Employee> items1 = list.Where(a=>a.Age>=35);
Console.OutputEncoding = Encoding.Unicode;
foreach (Employee employee in items1) {
Console.WriteLine(employee.ToString());
}
当前筛选条件为元素的Age必须不小于35
Id:3,Name:lombok,Age:35,Salary:9000,Gender:0
Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
1 TSource Single(this IEnumerable source, Func
predicate);
当TSource满足bool条件判断时,返回一条数据。 0或多笔数据符合条件时,会报错
System.InvalidOperationException: 'Sequence contains more than one matching element'
System.InvalidOperationException: 'Sequence contains no matching element'
2 TSource SingleOrDefault(this IEnumerable source, Func
predicate);
当TSource满足bool条件判断时,返回一条数据。 多笔数据符合条件时,会报错。不存在时会根据TSource的数据类型赋默认的值 比如 TSource为int时,不满足条件时SingleOrDefault查询的值将会是int的默认值 0
3 TSource First(this IEnumerable source, Funcpredicate);
当TSource满足bool条件判断时,返回一条数据。 0笔数据符合条件时,会报System.InvalidOperationException: 'Sequence contains no matching element'异常 多笔数据符合时,会选择最先符合条件的的一笔数据
4 TSource FirstOrDefault(this IEnumerable source, Funcpredicate);
当TSource满足bool条件判断时,返回一条数据。 0笔数据符合条件时,会根据TSource的数据类型赋默认的值 ,多笔数据符合时,会选择最先符合条件的一笔数据
Employee emp = list.Single(i => i.Id == 1);
Console.WriteLine(emp.ToString());
emp = list.SingleOrDefault(i => i.Id == 0);
Console.WriteLine(emp == null ? "Null" : emp.ToString());
emp = list.First(i => i.Id>6);
Console.WriteLine(emp.ToString());
emp = list.FirstOrDefault(i => i.Gender == 0);
Console.WriteLine(emp == null ? "Null" : emp.ToString());
Id:1,Name:llk,Age:28,Salary:4000,Gender:1
Null
Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
Id:3,Name:lombok,Age:35,Salary:9000,Gender:0
Count(this IEnumerable source, Func
predicate);
对source中的每个元素进行bool条件判断,为true时计数
int allCount = list.Count();
int Fcount = list.Count(e => e.Gender == 1);
int Mcount = list.Count(e => e.Gender == 0);
Console.WriteLine($"员工总数为:{allCount} 男员工共{Mcount} 女员工共{Fcount}");
员工总数为:9 男员工共5 女员工共4
bool All(this IEnumerable source, Func
predicate);
判断source中的元素是否至少有一个满足bool条件判断
bool flag = list.Any(a => a.Salary > 15000);
Console.WriteLine($"是否有员工的工资大于15000?{flag}"); //False
flag = list.Any(a => a.Id==1);
Console.WriteLine($"是否有员工的工号为1?{flag}"); //True
IOrderedEnumerable OrderBy
(this IEnumerable source, Func keySelector);
source元素根据给定的TKey来进行升序排
IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector);
source元素根据给定的TKey来进行降序排
IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector);
source元素根据给定的TKey来进行多规则升序排
IOrderedEnumerable ThenByDescending(this IOrderedEnumerable source, Func keySelector);
source元素根据给定的TKey来进行多规则降序排
IEnumerable<Employee> newEmps = list.OrderBy(i => i.Age);
Console.WriteLine("年龄升序排");
foreach (Employee employee in newEmps)
{
Console.WriteLine(employee.ToString());
}
//source元素根据给定的TKey来进行降序排
newEmps = list.OrderByDescending(i => i.Salary);
Console.WriteLine("工资降序排");
foreach (Employee employee in newEmps)
{
Console.WriteLine(employee.ToString());
}
//ThenBy 多规则排序
newEmps = list.OrderByDescending(i => i.Salary).ThenBy(i => i.Age).ThenByDescending(i => i.Gender);
Console.WriteLine("工资降序排再年龄升序排,性别降序排");
foreach (Employee employee in newEmps)
{
Console.WriteLine(employee.ToString());
}
年龄升序排
Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
Id:8,Name:qm,Age:25,Salary:6000,Gender:1
Id:1,Name:llk,Age:28,Salary:4000,Gender:1
Id:2,Name:lom,Age:28,Salary:7000,Gender:1
Id:4,Name:lld,Age:28,Salary:8200,Gender:0
Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
Id:3,Name:lombok,Age:35,Salary:9000,Gender:
Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
工资降序排
Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
Id:3,Name:lombok,Age:35,Salary:9000,Gender:
Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
Id:4,Name:lld,Age:28,Salary:8200,Gender:0
Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
Id:2,Name:lom,Age:28,Salary:7000,Gender:1
Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
Id:8,Name:qm,Age:25,Salary:6000,Gender:1
Id:1,Name:llk,Age:28,Salary:4000,Gender:1
工资降序排再年龄升序排,性别降序排
Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
Id:3,Name:lombok,Age:35,Salary:9000,Gender:
Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
Id:4,Name:lld,Age:28,Salary:8200,Gender:0
Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
Id:2,Name:lom,Age:28,Salary:7000,Gender:1
Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
Id:8,Name:qm,Age:25,Salary:6000,Gender:1
Id:1,Name:llk,Age:28,Salary:4000,Gender:1
Skip(n1) 跳过n1条数据,Take(n2) 获得n2条数据
通常可以用作数据的分页处理,Skip(3).Take(3); 每三条数据为一页,获得第二页的数据
newEmps = list.OrderByDescending(i => i.Salary).Skip(3).Take(3);
Console.WriteLine("分页显示");
foreach (Employee employee in newEmps)
{
Console.WriteLine(employee.ToString());
}
分页显示
Id:4,Name:lld,Age:28,Salary:8200,Gender:0
Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
Id:2,Name:lom,Age:28,Salary:7000,Gender:1
//TResult Max(this IEnumerable source, Func selector);
//Func selector 对输入元素TSource判断最大值,返回泛型TResult
int maxAge= list.Max(i=>i.Age); // 获得最大年龄,Int类型
string maxName = list.Max(i => i.Name); //获得最大名字,对String 进行比较
Console.WriteLine("员工最大年龄" + maxAge);
Console.WriteLine("员工最大名字" + maxName);
//TResult Min(this IEnumerable source, Func selector);
// //Func selector 对输入元素TSource判断最小值,返回泛型TResult
int minAge = list.Min(i => i.Age); // 获得最小年龄,Int类型
Console.WriteLine("员工最小年龄" + minAge);
//double Average(this IEnumerable source, Func selector);
//System.Linq命名空间下基本上都是对IEnumerable接口的扩展方法,因此可以链式调用
//查询Gender=0 女员工的平均工资
double agvSalay = list.Where(i => i.Gender == 0).Average(i=>i.Salary);
Console.WriteLine("女员工的平均工资" + agvSalay);
//Gender=1 男性员工的工资大于6000的人数
int qty = list.Where(i => i.Gender == 1 && i.Salary > 6000).Count();
Console.WriteLine("男性员工的工资大于6000的人数" + qty);
员工最大年龄45
员工最大名字xxk
员工最小年龄25
女员工的平均工资9280
男性员工的工资大于6000的人数1
// IEnumerable> GroupBy(this IEnumerable source, Func keySelector);
// Func keySelector 按照TKey 进行分组
// IGrouping 返回以Tkey为组的TSource集合 TSource的类型与source的类型一致
IEnumerable<IGrouping<int, Employee>> sources= list.GroupBy(i => i.Age);
foreach (IGrouping<int, Employee> group in sources) {
Console.WriteLine("年龄组为:" + group.Key);
foreach (Employee employee in group)
{
Console.WriteLine(employee.ToString());
}
Console.WriteLine("最大工资"+group.Max(i=>i.Salary));
}
年龄组为:28
Id:1,Name:llk,Age:28,Salary:4000,Gender:1
Id:2,Name:lom,Age:28,Salary:7000,Gender:1
Id:4,Name:lld,Age:28,Salary:8200,Gender:0
Id:7,Name:xxk,Age:28,Salary:8200,Gender:0
最大工资8200
年龄组为:35
Id:3,Name:lombok,Age:35,Salary:9000,Gender:0
最大工资9000
年龄组为:25
Id:5,Name:ssf,Age:25,Salary:6000,Gender:1
Id:8,Name:qm,Age:25,Salary:6000,Gender:1
最大工资6000
年龄组为:45
Id:6,Name:ghj,Age:45,Salary:12000,Gender:0
最大工资12000
年龄组为:38
Id:9,Name:fmh,Age:38,Salary:9000,Gender:0
最大工资9000
Enumerable Select
(this IEnumerable source, Func selector);
Funcselector 將TSource转换成另一个类型
var ages = list.Select(i=>i.Age);
Console.WriteLine("遍历所有的年龄");
foreach (int age in ages)
{
Console.WriteLine(age);
}
//Employee to String
var genders = list.Select(i=>i.Gender==1?"男":"女");
Console.WriteLine("遍历所有的性別");
foreach (string gender in genders)
{
Console.WriteLine(gender);
}
遍历所有的年龄
28
28
35
28
25
45
28
25
38
遍历所有的性別
男
男
女
女
男
女
女
男
女
在一定的情形下,需要将IEnumerable 通过ToList转换为List类型 ToArray转换为数组类型
List <Employee> li = list.ToList();
Employee[] arr= list.ToArray();
foreach (Employee e in li)
{
Console.WriteLine(e.ToString());
}
foreach (Employee e in arr)
{
Console.WriteLine(e.ToString());
}
System.Linq命名空间下的扩展方法基本上都是对IEnumerable<泛型>的处理,且Where、Select、OrderBy、GroupBy、Take、Skip的返回值都是IEnumerable <泛型>,所有可以进行链式调用去更快速的处理数据
//分组 投影 聚合函数
var obj= list.GroupBy(i=>i.Gender).Select(g=>new {Gender=g.Key==1?"男":"女",Count=g.Count(),MaxSalary=g.Max(item=>item.Salary),AgvSalay=g.Average(item=>item.Salary)});
foreach(var o in obj)
{
Console.WriteLine($"Gender:{o.Gender},MaxSalary:{o.MaxSalary} AgvSalay:{o.AgvSalay}");
}
Gender:男,MaxSalary:7000 AgvSalay:5750
Gender:女,MaxSalary:12000 AgvSalay:9280