C#中linq

207 阅读1分钟

Linq是结合了声明式的命令式结构

语法

引入using System.Linq; linq的核心就是一段查询数据

//声明式结构
var query = from file in new DirectoryInfo(path).GetFiles()
            orderby file.Length descending
            select file;
//命令式结构,使用对象的链式结构完成
var query = new DirectoryInfo(path).GetFiles()
    .OrderByDescending(f => f.Length)
    .Take(5);

foreach (var f in query)
{
    Console.WriteLine($"{f.Name,-20} : {f.Length,10:N}");
}

lambda 表达式

匿名方法

delegate (参数)=>{逻辑处理;}
//
()=>{}

Linq查询

引入Linq后,就可以在各种集合中以对象的形式使用linq的方法 .orderby .when .count(Linq的方法查询) Linq的结构化查询(类sql)

var query = from c in customers
where c.Address == "广州"
orderby c.Name
select c.Name;

//对象结构
var query = customers
.Where(c => c.Address == "广州")
.OrderBy(c => c.Name);
//对象结构中,如果最后的c不需要做数据塑形,select可以忽略
//.Select(c => c);

where的实现原理

//不需要实例化就可使用,故用static
public static MyLinq{
    //集合需要进行迭代操作,故使用IEumerable
    //                             因为要用到链式结构,所以第一个对象(数据源)就是它本身, 第二个参数要接受一个委托方法
    public static IEnumerable<T> MyWhere<T>(this IEumerable<T> source, Func<T,bool> predicate){
        var result = new List<T>();
        foreach(var item in source){
        //遍历所有数据,符合条件的返回
        if(predicate(item)){
            result.Add(item);
            }
        }
        return result
    }
}

//实际上where是延迟执行(yield return)所以更像这样
 foreach(var item in source){
    //遍历所有数据,符合条件的返回
    if(predicate(item)){
        yield return item;
        }
    }

从CSV中读取数据

首先根据csv中数据创建数据模型

static void Main(string[] args)
{
    List<Car> cars = ProcessCars("fuel.csv"); 

    Console.Read();
}

private static List<Car> ProcessCars(string v)
{
    var result = File.ReadAllLines(v)
        .Skip(1)
        .Where(l => l.Length > 1)
        .Select(line =>
        {
            var columns = line.Split(",");
            return new Car
            {
                Year = columns[0],
                Manufacturer = columns[1],
                Model = columns[2],
                Displacement = double.Parse(columns[3]),
                CylindersCount = int.Parse(columns[4]),
                City = int.Parse(columns[5]),
                Highway = int.Parse(columns[6]),
                Combined = int.Parse(columns[7])
            };
        });
    //这里需要调用ToList让它变成IEumerable
    return result.ToList();
}

排序

对次一级排序时,不能连续使用orderby,连续使用orderby会给列表重新排序

var query = cars
//按照降序排序
.OrderByDescending(c => c.Combined)
//对次一级排序
.ThenByDescending(c => c.Model);

//声明式语法
var query = (from car in cars
where car.Manufacturer == "dddddd" && car.Year == "2016"
//只需要将两个排序条件逗号分隔,编译器会自动识别一级和二级
orderby car.Combined descending, car.Model descending
select car)
//.First()
.FirstOrDefault();
//使用take(1),返回仍然是集合,但是集合只有一个元素,.First() .FirstOrDefault(); 返回的是对象 这两个方法可以在提取第一项数据的同时展开集合

数据量化 Any、All、Contains

Any(lambda表达式) 查询数据中是否含有某一类,返回一个bool值,一般与if搭配,any也可以判断一个集合是否为空,此时不需要传参 Contains(实例对象) 查询数据中是否有某某对象 All(lambda) 与Any相反,查询数据中是否全部满足条件,返回一个bool值

//Any()
var query2 = cars.Any(c => c.Manufacturer == "Volkswagen");
Console.WriteLine(query2);
if(query2)
{
    Console.WriteLine("有大众");
} 
else
{
    Console.WriteLine("没有大众");
}
var isCarsEmpty = cars.Any();

// contains
var isReal = cars.Contains(query);

//All
var query2 = cars.All(c => c.Manufacturer == "Volkswagen");

数据投影(select)

数据投影 将有用的信息投影出来,避免冗余信息

select new {
     Model = car.Model,
     Combined = car.Combined
 })

selectMany展开嵌套集合,并且提取子集合中所有数据

//此时query类型是字符(从字符串即字符的集合中取出数据)
var query3 = cars.SelectMany(c => c.Model);
foreach(var c in query3)
{
    Console.WriteLine(c);
}

数据连接 join

join与from作用类似,告诉linq从哪读取数据 join on 语法中不能使用==,用equals,只能判断是否相等,不能比大小

var query = (from car in cars
             join manufacturer in manufacturers on car.Manufacturer equals manufacturer.Name
             orderby car.Combined descending, car.Model descending
             select new
             {
                 Manufacturer = car.Manufacturer,
                 Model = car.Model,
                 Combined = car.Combined,
                 Headquarters = manufacturer.Headquarters,
                 Phone = manufacturer.Phone
             })
             .Take(10);
             
//对象的链式调用
//                              两个lambda表达式 用于表示链接的内容,第三个表示链接后内容最终输出形式
  var query2 = cars.Join(manufacturers, (c) => c.Manufacturer, (m) => m.Name, (c, m) => new
            {
                Car = c,
                Manufacturer = m
            }).OrderByDescending(joinData => joinData.Car.Combined)
            .ThenBy(joinData => joinData.Car.Model)
            .Select(joinData => new
            {
                Manufacturer = joinData.Car.Manufacturer,
                Model = joinData.Car.Model,
                Combined = joinData.Car.Combined,
                Headquarters = joinData.Manufacturer.Headquarters,
                Phone = joinData.Manufacturer.Phone
            }).Take(10);

join对应于sql中的内链接:任何数据只要在任意数据源中缺失,将会忽略整个数据的输出

数据分组 group

group和select一样都是进行数据投影的过程,故可以直接用于结尾

var query = from car in cars
            group car by car.Manufacturer into manufacturerGroup
            orderby manufacturerGroup.Key descending
            select manufacturerGroup;
            
var query = from car in cars
            group car by car.Manufacturer into manufacturerGroup
            orderby manufacturerGroup.Key descending
            select manufacturerGroup;

数据分组连接 group join

在分组过程中使用第二个数据源

var query = from manufacturer in manufacturers
            join car in cars on manufacturer.Name equals car.Manufacturer into carGroup
            orderby manufacturer.Name descending
            select new
            {
                Manufacturer = manufacturer,
                Cars = carGroup
            };

var query2 = manufacturers.GroupJoin(cars, m => m.Name, c => c.Manufacturer, (m, carGroup) => new
{
    Manufacturer = m,
    Cars = carGroup
}).OrderByDescending(m => m.Manufacturer.Name);

数据聚合

var query = from car in cars
            group car by car.Manufacturer into carGroup
            select new
            {
                Manufacturer = carGroup.Key,
                Avg = carGroup.Average(c => c.Combined),
                Max = carGroup.Max(c => c.Combined),
                Min = carGroup.Min(c => c.Combined)
            } into tempGroup
            //根据Avg排序
            orderby tempGroup.Avg descending
            select tempGroup;

foreach (var group in query)
{
    Console.WriteLine($"{group.Manufacturer} 油耗情况 ");
    Console.WriteLine($"\t 最高: {group.Max}");
    Console.WriteLine($"\t 最低: {group.Min}");
    Console.WriteLine($"\t 平均: {group.Avg}");
}