Linq声明式语法
1.找出C盘中5个最大的文件。
var query = from file in new DirectoryInfo(path).GetFiles()
orderby file.Length descending
select file;
foreach (var file in query.Take(5))
{
Console.WriteLine($"{file.Name,-20}:{file.Length,10:N}");
}
query白话:from
表示重哪里来,from file
就表示file
从哪里来,in
表示在…里面。in
集合,表示在这个集合里面。from file in new DirectoryInfo(path).GetFiles()
连起来就是file
从DirectoryInfo(path).GetFiles()
里面拿出来,然后对每个file
做排序操作。
Linq命令式语法
var query = new DirectoryInfo(path).GetFiles().OrderByDescending(f=>f.Length).Take(5);
foreach (var file in query)
{
Console.WriteLine($"{file.Name,-20}:{file.Length,10:N}");
}
lambda演算
假如我们要筛选名字为A开通的客户,原始写法。
这种写法每次都需要定义方法,很痛苦。其中Where
为Linq
的命令式语法,会遍历集合中的所有数据,True
保留在集合False
则不保留。
于是有了匿名方法。
这种写法看起来还是很复杂,而且delegate
这种关键字并不参与代码的逻辑,类型string
本身就是集合类型也可以省略,只有一行代码也没必要要返回值。于是就有了lambda
。
去掉上面该去掉的就是Lambda写法。
把上面的 题目改一下,找出C盘中5个最大的文件,并且返回以小写b开头的文件。
var query = new DirectoryInfo(path).GetFiles()
.OrderByDescending(f=>f.Length)
.Where(f=>f.Name.StartsWith("b"))
.Take(5);
foreach (var file in query)
{
Console.WriteLine($"{file.Name,-20}:{file.Length,10:N}");
}
结构化查询
找出C盘中5个最大的文件,并且返回以小写b开头的文件,并且文件大小大于4000
var query = from file in new DirectoryInfo(path).GetFiles()
where file.Name.StartsWith("b") && file.Length > 4000
orderby file.Name
select file;
foreach (var file in query)
{
Console.WriteLine($"{file.Name,-20}:{file.Length,10:N}");
}
select
为最后需要返回的结果导向。比如sql
中的 select *
就返回所有字段这里的 file
也表示返回所有。
这里的query
返回类型,完全有select
决定,如果返回file.Name
则是string
类型。
命令式写法:
var query = new DirectoryInfo(path).GetFiles()
.Where(f => f.Name.StartsWith("b") && f.Length > 4000)
.OrderBy(f => f.Name);
foreach (var file in query)
{
Console.WriteLine($"{file.Name,-20}:{file.Length,10:N}");
}
Linq原理解析
为where
语句实现一个扩展方法。
新建一个类MyLinq
实现MyWhere
方法。
using System;
using System.Collections;
using System.Collections.Generic;
namespace Linq
{
public static class MyLinq
{
public static IEnumerable<T> MyWhere<T>(
this IEnumerable<T> source , Func<T,bool> predicate
)
{
var rusult = new List<T>();
foreach (var item in source)
{
if (predicate(item))
{
rusult.Add(item);
}
}
return rusult;
}
}
}
this IEnumerable<T> source
表示当前集合元数据。
该方法实现了和where一样的效率。
我们可以运行一下我们自己的扩展方法MyWhere
var query = new DirectoryInfo(path).GetFiles()
.MyWhere(f => f.Name.StartsWith("b") && f.Length > 4000)
.OrderBy(f => f.Name);
效果也可以出来,但是原生的where语句支持懒加载。懒加载的底层就是用了 yield return
通过 yield return
返回一个集合迭代,最终实现懒加载,不懂yield return
的请查看我的 yield return
文章。
对MyWhere
改动后如下:
using System;
using System.Collections;
using System.Collections.Generic;
namespace Linq
{
public static class MyLinq
{
public static IEnumerable<T> MyWhere<T>(
this IEnumerable<T> source , Func<T,bool> predicate
)
{
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
}
}
案例:读取文本中的汽车数据并打印出来。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace LinqTest
{
class Program
{
static void Main(string[] args)
{
List<Car> cars = ProcessCars("Cars.txt");
foreach (var car in cars)
{
Console.WriteLine($"型号:{car.CarNumber ,-20}品牌:{car.CarName,-20}最大马力:{car.MaxML,-10}油耗:{car.FCPHK,-10}");
}
Console.Read();
}
private static List<Car> ProcessCars(string path)
{
//Skip 跳过第一行
var result = File.ReadAllLines(path)
.Skip(1)
.Where(r => r.Length > 1)
.Select(line => {
var columns = line.Split(",");
Car car = new Car
{
CarNumber = columns[0],
CarName = columns[1],
MaxML = columns[2],
FCPHK = columns[3]
};
return car;
});
return result.ToList();
}
}
}
排序与过滤
查找油耗小于15的宝马汽车,并根据百公里油耗和最大马力排序。并取出符合要求的一辆汽车。
命令式:
var query = cars
.Where(c=>c.CarName.Equals("宝马")&& int.Parse(c.FCPHK) <= 15)
.OrderByDescending(c => c.FCPHK)
.ThenByDescending(c => c.MaxML)
.FirstOrDefault();
其中ThenByDescending
为二级排序。
声明式:
var query = (from car in cars
where car.CarName == "宝马" && int.Parse(car.FCPHK) <= 15
orderby car.FCPHK descending, car.MaxML descending
select car).FirstOrDefault();
查询操作
//查询 是否有大众汽车
var query1 = cars.Any(c => c.CarName == "大众");
if (query1)
{
Console.WriteLine("包含!");
}
//判断集合是否为空
var isCarsEmpty = cars.Any();
if (!isCarsEmpty)
{
Console.WriteLine("不是空集合");
}
//查询集合中是否包含有该对象
cars.Contains(query);
//查询集合是否都是大众汽车
cars.All(c => c.CarName == "大众");
数据投影与SelectMany
ToCar
是迭代器的扩展方法,通过扩展方法可以直接把字符串集合返回为对象集合。
public static class CarExtension
{
public static IEnumerable<Car> ToCar(this IEnumerable<string> source)
{
foreach (var line in source)
{
var columns = line.Split(",");
yield return new Car
{
CarNumber = columns[0],
CarName = columns[1],
MaxML = columns[2],
FCPHK = columns[3]
};
}
}
}
}
调用ToCar
方法。
var result = File.ReadAllLines(path)
.Skip(1)
.Where(r => r.Length > 1)
.ToCar();
如果不想返回整个对象,想进行字段筛选过滤。可以采用new{}
(匿名对象的形式,过滤数据。)
var query = (from car in cars
where car.CarName == "宝马" && int.Parse(car.FCPHK) <= 15
orderby car.FCPHK descending, car.MaxML descending
select new {
CarName = car.CarName,
CarNumber =car.CarNumber,
MaxML=car.MaxML,
FCPHK=car.FCPHK
}).FirstOrDefault();
Console.WriteLine($"型号:{query.CarNumber,-20}品牌:{query.CarName,-20}最大马力:{query.MaxML,-10}油耗:{query.FCPHK,-10}");
数据连接Join
声明式
var query = (from car in cars
join manufacturer in manufacturers on car.CarName equals manufacturer.CarName
orderby car.FCPHK descending, car.MaxML descending
select new
{
CarName = car.CarName,
CarNumber = car.CarNumber,
MaxML = car.MaxML,
FCPHK = car.FCPHK,
Addrse= manufacturer.CarAddres
}).FirstOrDefault();
命令式
var query2 = cars.Join(manufacturers, (c) => c.CarName, (m) => m.CarName, (c, m) => new
{
Car = c,
Manufacturer = m
}).OrderByDescending(JoinDate => JoinDate.Car.FCPHK)
.Select(JoinDate=> new {
CarName = JoinDate.Car.CarName,
CarNumber = JoinDate.Car.CarNumber,
MaxML = JoinDate.Car.MaxML,
FCPHK = JoinDate.Car.FCPHK,
Addrse = JoinDate.Manufacturer.CarAddres
}).FirstOrDefault();
Join
参数1是关联的对象,参数2,3是分别可以关联上的字段,参数4是关联后返回的最终结果。
数据分组Group
声明式
var query = from car in cars
group car by car.FCPHK into manufactureGroup
orderby manufactureGroup.Key descending
select manufactureGroup;
如果需要对声明式的分组进行排序,需要用到into
关键字,最后返回select
分完组并排序的结果。
命令式:
var query2 = cars.GroupBy(c=>c.FCPHK).OrderByDescending(g=>g.Key);
数据分组连接groupjoin
声明式
var query = from manufacture in manufacturers
join car in cars on manufacture.CarName equals car.CarName
into CarGroup
orderby manufacture.CarName descending
select new
{
Manufacture= manufacture,
Cars= CarGroup
};
命令式
var quer2 = manufacturers.GroupJoin(cars, m => m.CarName, c => c.CarName, (m, CarGroup) => new
{
Manufacture = m,
Cars = CarGroup
}).OrderByDescending(m => m.Manufacture.CarName);
数据聚合
声明式
var quer2 = from car in cars
group car by car.FCPHK into GroupCar
select new
{
FCPHK = GroupCar.Key,
Avg = GroupCar.Average(c => c.FCPHK),
Max = GroupCar.Max(c => c.FCPHK),
Min = GroupCar.Min(c => c.FCPHK)
} into AverageFCPHK
orderby AverageFCPHK.Avg descending
select AverageFCPHK;