一、为什么要使用 LINQ

要理解为什么使用 LINQ,先来看下下面的例子

例子:要统计字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次和其出现的的频率。如果用传统的 Sql 语句来写,一定是非常的繁杂,如果用 LINQ 语句来写,效果如下

string strs = "hello word, Hehehe";
var items = strs.Where(c => char.IsLetter(c)) //过滤非字符
.Select(c => char.IsLower(c)) //大小写都转换成小写
.GroupBy(c => c) //根据字母进行分组
.Where(g => g.Count() > 2) //过滤掉出现次数 <=2
.OrderByDescending(g => g.Count()) //按次数排序
.Select(g => new { Char = g.Key, Count = g.Count() });

使用 Linq 来实现,简单、清晰、明了。这里就体现了 LINQ 的一大好处:让数据处理变得简单

二、揭秘 LINQ 方法的背后

了解 LINQ 方法背后做了些什么,可以让我们更好的使用 LINQ。下面以 Wherr 方法为例,写一个我们自己的 MyWhere 方法

LINQ 提供了很多集合的扩展方法,配合 lambda 能简化数据处理

例子:有一个 [3, 15, 88, 4, 77, 42, 8] 的数组,要得到它 >10 的值

int[] num = new int[] { 3, 15, 88, 4, 77, 42, 8 };
//使用原本的Where
IEnumerable<int> result = num.Where(r => r > 10);
foreach (int i in result)
{
Console.WriteLine(i);
});
}
static void Main(string[] args)
{
int[] num = new int[] { 3, 15, 88, 4, 77, 42, 8 };
//使用我们自己写的MyWhere方法
var result2 = MyWhere2(num, r => r > 10);
foreach (int i in result2)
{
Console.WriteLine(i);
}
}
public static IEnumerable<int> MyWhere(IEnumerable<int> items, Func<int, bool> f)
{
List<int> result = new List<int>();
foreach (var i in items)
{
if (f(i) == true)
{
result.Add(i);
}
}
return result;
}

在上述代码中,MyWhere 方法是根据我们的需求来写的一个扩展方法,它要求传入一个IEnumerable 类型的参数和一个 Func<int,bool> 委托类型的参数,在方法中,新建一个 List 集合用于存值,遍历传入的数组,符合条件就存入集合中,最后返回集合。

三、LINQ 的扩展方法

LINQ 关键的功能是提供了集合类的扩展方法,所以实现了 IEnumerable 接口的类都可以使用这些方法,这是方法并不是 IEnumerable 中的方法,而是以扩展方法的存在于System.Linq 命名空间的静态类中

准备一些测试数据

class 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; }
public override string ToString()
{
return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";
}
}

Where(数据过滤)

Where 方法是根据条件对数据进行过滤

语法如下:

IEnumerable<Employee> items = list.Where(e => e.Age > 25);
foreach (var item in items)
{
Console.WriteLine(item);
}

Count(获取数据条数)

语法如下:

 //返回一个数字,表示指定序列中满足条件的元素个数。
Console.WriteLine(list.Count(e => e.Salary > 5000 || e.Age > 25));

Any(判断是否有一条数据满足条件)

语法如下:

//是否至少有一条数据
//性能比 Count 要高,Count 会遍历所有数据才返回结果,Any 在遍历数据时遇到满足条件的数据就直接返回结果
Console.WriteLine(list.Any(e => e.Salary == 8000));
Console.WriteLine(list.Where(e=>e.Salary>8000).Any());

Single、SingleOrDefault、First、FirstOrDefault(获取一条数据)

语法如下:

//有且只有一条数据,如果没有符合条件的数据或者存在大于一条符合条件的数据,都会报错
Console.WriteLine(list.Single(s => s.Name == "张德开"));
//有且只有一条数据,返回该条数据,否则返回默认值,如果匹配到多条数据,则会报错
Console.WriteLine(list.SingleOrDefault(s => s.Age == 18));
//匹配到多条数据时,返回第一条数据,没有匹配到数据时,则会报错
Console.WriteLine(list.First(e => e.Age > 30));
//匹配到多条数据时,返回第一条数据,否则返回默认值
Console.WriteLine(list.FirstOrDefault(e => e.Age > 30));

OrderBy、OrderByDescending(排序)

语法如下:

 //升序排序
IEnumerable<Employee> e = list.OrderBy(s => s.Age);
//降序排序
e = list.OrderByDescending(s => s.Salary);
e = list.OrderByDescending(s => s.Name[s.Name.Length - 1]);
//可以在OrderBy、OrderByDescending后面继续使用ThenBy、ThenByDescending进行多规则排序
e = list.OrderBy(s => s.Age).ThenBy(s => s.Salary);
foreach (var item in e)
{
Console.WriteLine(item);
}

Sike()、Take()(限制结果集)

语法如下:

// Skip(n) 跳过 n 条数据,Take(n) 获取 n 条数据
var items2 = list.Skip(3).Take(2);
items2 = list.Where(e => e.Age >= 25).OrderBy(e => e.Age).Skip(2).Take(3);
foreach (var item in items2)
{
Console.WriteLine(item);
}

聚合函数

Max(最大值)、Min(最小值)、Average(平均值)、Sun(总和)、Count(总数)

语法如下:

//最大年龄
Console.WriteLine(list.Max(e => e.Age));
//最低工资
Console.WriteLine(list.Min(e => e.Salary));
//平均工资
Console.WriteLine(list.Average(e => e.Salary));
//所有人总工资
Console.WriteLine(list.Sum(e => e.Salary));
//女员工数量
Console.WriteLine(list.Count(e => e.Gender == false));

GroupBy(分组)

GroupBy 方法的参数 keySelector 是分组条件表达式,GroupBy 方法的返回值为IGrouping<TKey, TSource> 类型的泛型 IEnumerable。IGrouping 是继承自 IEnumerable的接口,IGrouping 中唯一的成员就是 Key 属性,表示这一组数据的数据项,由于IGrouping 是继承自 IEnumerable 接口的,因此我们依然可以使用 Count、Min、Average等方法对组内数据进行聚合运算

语法如下:

 //根据年龄进行分组
IEnumerable<IGrouping<int, Employee>> items3 = list.GroupBy(e => e.Age);
foreach (IGrouping<int, Employee> g in items3)
{
Console.WriteLine(g.Key);
foreach (Employee e in g)
{
Console.WriteLine(e);
}
}

综合案例:

//根据年龄分组,获取每组人数,最大工资,平均工资,用var简化编程
var item3 = list.GroupBy(e => e.Age);
foreach (var g in item3)
{
Console.WriteLine("每组人数:" + g.Count());
Console.WriteLine("最大工资:" + g.Max(e => e.Salary));
Console.WriteLine("平均工资:" + g.Average(e => e.Salary));
foreach (var e in g)
{
Console.WriteLine(e);
}
}

Select(投影)

可以对集合使用 Select 方法进行投影操作,通俗来讲就是把集合中的每一项逐渐转换为另外一种类型,Select 方法的参数是转换的表达式。

语法如下:

//把集合中的每一项转换成另一个类型
var items4 = list.Select(e => e.Age);
//Select 方法把 bool 的 Gender 转换为字符串类型,Select 方法的返回值为 IEnumerable<string> 类型
var items4 = list.Where(e => e.Salary > 5000).Select(e => e.Gender ? "男" : "女");
//根据年龄分组,然后统计各组人数、年龄、最高工资、最低工资
var items4 = list.GroupBy(e => e.Age).Select(s => new { NianLing = s.Key, MaxS = s.Max(e => e.Salary), MinS = s.Min(e => e.Salary), RenShu = s.Count() });
foreach (var e in items4)
{
Console.WriteLine(e.NianLing + ',' + e.MaxS + "," + e.MinS + ',' + e.RenShu);
}

集合转换

语法如下:

// ToList() 将其他类型转换为 List<T> 类型
List<Employee> empList = list.Where(e => e.Age > 25).ToList();
// ToArray() 将其他类型转换为数组
Employee[] empArr = list.Where(e => e.Salary > 5000).ToArray();

链式调用

上述介绍的这些方法,Where、Count、OrderBy、GroupBy 等方法的返回值都是 IEnumerable 类型,因此它们是可以链式调用的

//获取 id>2 的数据,然后按照 Age 分组,并且把分组按照 Age 排序,然后取出前三条,最后再投影取得年龄、人数、平均工资
var items5 = list.Where(e => e.Id > 2).GroupBy(g => g.Age).OrderBy(g => g.Key).Take(3)
.Select(e => new { LianLin = e.Key, renShu = e.Count(), pingJun = e.Average(e => e.Salary) });
foreach (var i in items5)
{
Console.WriteLine(i.pingJun + "," + i.renShu + "," + i.pingJun);
}

四、LINQ 的另一种写法

LINQ 有两种语法,分别为方法语法和查询语法

//方法语法:使用Where、OrderBy、Selsect等扩展方法来查询数据的写法叫做Linq方法语法
var items6 = list.Where(e => e.Salary > 3000).OrderBy(e => e.Age)
.Select(e => new { e.Name, e.Age, XB = e.Gender ? "男" : "女" });
//查询语法:使用 LINQ 声明性查询语法编写的查询语句,在编译代码时,查询语法必须转换为针对.Net公共语言的的调用,这些方法的调用会调用标准查询运算符
var items7 = from e in list
where e.Salary > 3000
orderby e.Age
select new
{
e.Name,
e.Age,
XB = e.Gender ? "男" : "女"
};

方法语法

“方法语法” 并没有发明新的语法,用的都是扩展方法、Lambda 表达式等 C# 中已经存在的语法,而 “查询语法” 则是新的 C# 语法 。编译器会把 “查询语法” 编译成 “方法语法” 形式,因此它们在运行时没有区别。所有的 “查询语法” 都能改写成 “方法语法”,所有的 “方法语法”也能改写成 “查询语法” 。

查询语法

“查询语法” 看起来更加新颖,而且比 “方法语法” 需要写的代码会少一点,但在编写复杂的查询条件的时候,用 “方法语法” 编写的代码会更清晰

五、小练习

练习一

"85,34,23,45,12,67,60" 计算这些数的平均值

 string s = "85,34,23,45,12,67,60";
var avg = s.Split(',').Select(e => Convert.ToInt32(e)).Average();
Console.WriteLine(avg);

练习二

统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次的单词和出现的频率

string s = "Hello word Hehehe Hahaha";
var items7 = s.Where(e => char.IsLetter(e)).Select(e => char.ToLower(e)).GroupBy(c => c)
.Select(g => new { g.Key, Count = g.Count() }).OrderBy(g => g.Count).Where(g => g.Count > 2);
foreach (var item in items7)
{
Console.WriteLine(item);
}

LINQ 学习之路的更多相关文章

  1. [EntLib]微软企业库5.0 学习之路——第一步、基本入门

    话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白). 直到去年在 ...

  2. 微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—下篇

    在上一篇文章中, 我介绍了企业库Cryptographer模块的一些重要类,同时介绍了企业库Cryptographer模块为我们提供的扩展接口,今天我就要根据这些 接口来进行扩展开发,实现2个加密解密 ...

  3. 微软企业库5.0 学习之路——第四步、使用缓存提高网站的性能(EntLib Caching)

    首先先补习下企业库的Caching Application Block的相关知识: 1.四大缓存方式,在Caching Application Block中,主要提供以下四种保存缓存数据的途径,分别是 ...

  4. 微软企业库5.0 学习之路——第二步、使用VS2010+Data Access模块建立多数据库项目

    现在我就开始进入学习之路的第二步——Data Access模块,这个模块是企业库中被使用频率最高的模块,它很好的封装了数据库操作应用,为我们进行多数据库系统开发提供了便利,只需更改配置文件就 可以很快 ...

  5. jQuery学习之路(1)-选择器

    ▓▓▓▓▓▓ 大致介绍 终于开始了我的jQuery学习之路!感觉不能再拖了,要边学习原生JavaScript边学习jQuery jQuery是什么? jQuery是一个快速.简洁的JavaScript ...

  6. Android开发学习之路-RecyclerView滑动删除和拖动排序

    Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...

  7. RPC远程过程调用学习之路(一):用最原始代码还原PRC框架

    RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用. rpc的实现方式有很多,可以通 ...

  8. webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口

    webService学习之路一:讲解了通过传统方式怎么发布及调用webservice webService学习之路二:讲解了SpringMVC和CXF的集成及快速发布webservice 本篇文章将讲 ...

  9. [精品书单] C#/.NET 学习之路——从入门到放弃

    C#/.NET 学习之路--从入门到放弃 此系列只包含 C#/CLR 学习,不包含应用框架(ASP.NET , WPF , WCF 等)及架构设计学习书籍和资料. C# 入门 <C# 本质论&g ...

  10. Redis——学习之路四(初识主从配置)

    首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务 ...

随机推荐

  1. 手撕Udp套接字|实现群聊通信|实现Windows & Linux通信交互

    ​ 专栏和Git地址 操作系统https://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482UdpSocke ...

  2. 使用私有gitlab搭建gitbook持续集成

    目录 环境搭建 1. 安装 Node.js 2. 安装 gitbook 3. 安装 Gitlab Runner 4. 注册Runner gitbook 配置 1. 目录结构 2. 命令行 3. 插件 ...

  3. CentOS7.5上Oracle19c离线rpm安装

    CentOS7.5上Oracle19c离线rpm安装 一.基础环境配置及依赖安装(1)关闭系统防火墙(如外网环境不能关闭防火墙自行开放相关需要使用的端口即可) systemctl stop firew ...

  4. JS Leetcode 155. 最小栈 题解分析

    壹 ❀ 引 本题来自LeetCode155. 最小栈,难度简单,题目描述如下: 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x ...

  5. GaussDB(for MySQL) Serverless全面商用:无感弹性,极致性价比

    本文分享自华为云社区<GaussDB(for MySQL) Serverless全面商用:无感弹性,极致性价比>,作者: GaussDB 数据库. 技术背景 对于现代企业级IT系统,数据库 ...

  6. 【OpenGL ES】绘制魔方

    1 前言 ​ 在立方体贴图(6张图)中,绘制了一个立方体,贴了 6 张图,本文的魔方案例,将实现绘制 27个立方体,贴 162 张图.贴图图片如下: ​ 说明:inside.png 为魔方内部色块,用 ...

  7. CompletableFuture使用自定义线程池实现多任务结果聚合返回

    为什么要使用自定义线程池? 默认线程池缺点 1.CompletableFuture默认使用的线程池是 ForkJoinPool.commonPool(),commonPool是当前 JVM(进程) 上 ...

  8. vue+jsPlumb制成的流程图项目

    本项目参考:https://github.com/wangduanduan/visual-ivr 主页面如下:

  9. 以二进制文件安装K8S之创建CA根证书

    为etcd和Kubernetes服务启用基于CA认证的安全机制,需要CA证书进行配置. 如果组织能够提供统一的CA认证中心,则直接使用组织颁发的CA证书即可.如果没有统一的CA认证中心,则可以通过颁发 ...

  10. django学习第八天--多表操作删除和修改,子查询连表查询,双下划线跨表查询,聚合查询,分组查询,F查询,Q查询

    orm多条操作 删除和修改 修改 在一对一和一对多关系时,和单表操作是一样的 一对一 一个作者对应一个信息 ad_obj = models.AuthorDetail.objects.get(id=1) ...