.NET-LINQ
.NET-LINQ
enumerable中文释义 :可数的,可枚举的
委托
.NET框架提供了两个委托Action
和 Func
它们分别用于表示没有返回值的方法和有返回值的方法
Action
是一个没有返回值的委托类型,它可以接受零个到十六个参数。常见的形式如下:
Action
:无参数,无返回值。Action<T>
:一个参数,无返回值。Action<T1, T2>
:两个参数,无返回值。Action<T1, T2, T3>
:三个参数,无返回值。- ...以此类推,最多支持十六个参数。
Func
是一个有返回值的委托类型,它可以接受零个到十六个参数,并且必须返回一个值。常见的形式如下:
Func<TResult>
:无参数,有返回值。Func<T, TResult>
:一个参数,有返回值。Func<T1, T2, TResult>
:两个参数,有返回值。Func<T1, T2, T3, TResult>
:三个参数,有返回值。- ...以此类推,最多支持十六个参数。
Lambda
委托指向的方法不止可以是普通方法,还可以是匿名方法
// 有返回值的匿名方法委托
Func<int, int, int> fun1 = delegate(int x, int y)
{
return x + y;
};
// 无返回值的匿名方法委托
Action<int, string> action = delegate(int age, string name)
{
Console.WriteLine($"age:{age},name:{name}");
};
使用Lambda表达式改写之后为
Func<int, int, int> fun1 = (int x, int y) => x + y;
Action<int, string> action = (int age, string name) => { Console.WriteLine($"age:{age},name:{name}"); };
LINQ的原理
让我们来手搓一个LINQ的WHERE方法
namespace LINQ
{
internal class Program
{
static void Main(string[] args)
{
// 建立一个数组
int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 调用MyWhere方法,传入数组,通过委托传入一个匿名方法(lambda)
var enumerable = MyWhere(nums, (num) => num > 5);
// 遍历结果集
foreach (var i in enumerable)
{
Console.WriteLine(i);
}
}
// 在这里 我们自定义了一个类Where的功能
// 第一个参数为items ,为被传入的集合
// 第二个参数为.NET提供的委托,Func f;其中一个参数为int,返回值为布尔类型
static IEnumerable<int> MyWhere(IEnumerable<int> items, Func<int, bool> f)
{
//实例化一个List用来保存查询结果,注意,List集合也是IEnumerable接口的实现类
List<int> result = new List<int>();
;
// 遍历items
foreach (var item in items)
{
// 将items传入委托方法做判断,如果委托方法返回为true,则将此项压入结果集
if (f(item))
{
result.Add(item);
}
}
// 返回结果集
return result;
}
}
}
小小的改写
static IEnumerable<int> MyWhere(IEnumerable<int> items, Func<int, bool> f)
{
// 遍历items
foreach (var item in items)
{
if (f(item))
{
// 在这里我们使用yield的方式来返回结果集,这也就无需实例化一个list集合作为结果集了
yield return item;
}
}
}
LINQ的方法
LINQ是IEnumerable的扩展方法,其目的是为了简化数据处理,大部分都在System.Linq命名空间中
也正因为这一特点,所以LINQ可应用于IEnumerable的子类:数组、List、Dictionary、Set……
LINQ很多方法的参数是一个委托方法,即传入一个返回值为布尔类型的匿名函数,作为操作的条件
LINQ的返回值多有变化,所以可以使用C#的类型推断特性(var)来简化类型接收
实体类和数据
record Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salary { get; set; }
}
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 35, Gender = false, Salary = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salary = 8000 });
}
Where方法
Where方法的返回值是IEnumerable<T>
// 检索年龄大于30并且工资大于8000的所有员工信息
var enumerable = list.Where(e => e.Age > 30 && e.Salary > 8000);
// 遍历结果集
foreach (var employee in enumerable)
{
Console.WriteLine(employee);
}
Count方法
Count方法的返回值是一个int值
统计满足条件结果集的个数
// 统计list集合中所有条数
Console.WriteLine(list.Count()); // 8
// 统计list集合中所有满足条件的员工信息的个数
Console.WriteLine(list.Count(e => e.Age > 30)); // 5
Any方法
Any方法的返回值是一个布尔值
判断结果中是否至少存在一条满足条件的员工信息
Console.WriteLine(list.Any(e => e.Salary > 9000)); // false
Console.WriteLine(list.Any(e => e.Salary >= 9000)); // true
Single方法
有且只有一条满足要求的数据(其他情况都报错)
var single = list.Single(e=> e .Name == "jerry");
SingleOrDefault方法
最多只有一条满足要求的数据(1条或者0条,如果大于1条则报错)
First方法
至少有一条,返回第一条(大于1条,如果0条则报错)
FirstOrDefault方法
返回第一条或默认值
Order方法
对简单类型数据正序排序
OrderByDescending方法
对数据按照一定规则进行倒序排序
OrderBy方法
对数据按照一定规则进行正序排序
需要注意的是,所有的排序方法的返回值都是IOrderedEnumerable对象,而它也是IEnumerable的接口
// 对工资进行升序排序
var orderedEnumerable = list.OrderBy(e => e.Salary);
foreach (var employee in orderedEnumerable)
{
Console.WriteLine(employee);
}
// 对年龄进行降序排序
var orderedEnumerable = list.OrderByDescending(e => e.Age);
foreach (var employee in orderedEnumerable)
{
Console.WriteLine(employee);
}
多规则排序
使用链式调用.ThenBy()和.ThenByDescending()方法来对数据进行多规则排序
// 对工资进行正序排序,如有相同,再对年龄进行倒序排序
var orderedEnumerable = list.OrderBy(e => e.Salary)
.ThenByDescending(e => e.Age);
Skin(n)&Take(n)方法
使用skin来跳过n条数据,使用take来获取n条数据(可以链式调用)
一般使用这两个方法来实现分页查询
聚合函数
LINQ中所有的扩展方法几乎都是针对IEnumerable接口的,而几乎所有能返回集合的都返回IEnumerable,所以是可以把几乎所有方法都进行链式调用的
函数名 | 说明 |
---|---|
Max() | 取最大值 |
Min() | 取最小值 |
Average() | 取平均值 |
Sum() | 取和 |
Count() | 取数量 |
分组
使用GroupBy对数据进行指定项的分组
不难看出,最后的分组结果是以键值对的方式存在一个集合中,key为分组的指定项,value为被分组的数据
List<Map<int,List<Employee>>>
IEnumerable<IGrouping<int, Employee>> groupBy = list.GroupBy(e => e.Age);
// 遍历大集合
foreach (var employeese in groupBy)
{
// 打印key
Console.WriteLine(employeese.Key);
// 输出每一组的最大工资
Console.WriteLine($"最大工资:{employeese.Max(e => e.Salary)}");
// 循环遍历value中内容
foreach (var employee in employeese)
{
Console.WriteLine(employee);
}
Console.WriteLine("****************");
}
Select方法
// 将投影的列额外的封装成对象
IEnumerable<User> enumerable = list.Select(e => new User()
{
name = e.Name,
age = e.Age,
});
foreach (var user in enumerable)
{
Console.WriteLine($"姓名:{user.name},年龄:{user.age}");
}
class User
{
public string name;
public int age;
}
匿名类型
C# 中的匿名类型是一种没有名称的类,可以在程序中临时创建对象而无需显式定义类
匿名类型主要用于简化代码,特别是在需要快速创建一个对象并立即使用该对象的情况下
var people = new { Name = "xiaobai", age = "18" };
在Java中,我们连接数据库查询到的数据除了使用DTO封装好的类型进行包装之外,还可以使用Map集合来临时包装这些数据然后将其返回
在C#中,我们可以使用匿名类型和类型推断来完成对数据临时的封装
匿名类型与投影
var enumerable = list.Select(e => new
{
Name = e.Name,
Age = e.Age,
});
集合转换
LINQ语句的返回值大多都是IEnumerable接口的数据类型
但实际开发中,我们需要结果为List<T>或者数组类型
可以通过ToArray()
方法和ToList()
方法来进行集合转换
查询语法
查询表达式语法是一种更接近 SQL 语法的形式
它使用 from
、where
、select
等关键字来构建查询。这种语法通常更易于阅读和理解,特别是对于熟悉 SQL 的开发者来说
// 方法语法
var employees = list.Where(e => e.Salary > 3000);
foreach (var employee in employees)
{
Console.WriteLine(employee);
}
//查询表达式语法
var enumerable = from e in list
where e.Salary > 3000
select e;
foreach (var employee in enumerable)
{
Console.WriteLine(employee);
}