[C#学习笔记]LINQ

news2024/9/19 14:21:15

视频地址:LINQ入门示例及新手常犯的错误_哔哩哔哩_bilibili

强烈推荐学习C#和WPF的朋友关注此UP,知识点巨多,讲解透彻!

一、基本概念

语言集成查询(Language-Intergrated Query)

常见用途

  • .Net原生集合(List,Array,Dictionary,etc.)
  • SQL数据库(尤其搭配ORM)
  • XML文档
  • JSON文档(Newtonsoft.Json)

常见功能

  • 排序、筛选、选择
  • 分组、聚合、合并
  • 最大值,最小值,求和,求平均,求数量
  • ......

两种形式

  • 查询表达式 query expression
  • 链式表达式 chained expression

例如,现在有个List<int>,内容为0-9,无序排列,需要把其中大于等于4的元素取出并排序

普通写法

var lst = new List<int> {1,3,5,7,9,2,4,6,8,0};

var res = new List<int>();
foreach (var n in lst)
{
	if (n %2 == 0 && n >= 4) res.Add(n);
}

res.Sort();
res.Dump();

查询表达式:

var lst = new List<int> {1,3,5,7,9,2,4,6,8,0};

var res = from n in lst
	where n % 2== 0 && n >= 4
	orderby n
	select n;

res.Dump();

链式表达式:

var lst = new List<int> {1,3,5,7,9,2,4,6,8,0};

var res = lst
.Where(n=> n%2 == 0 && n>= 4)
.OrderBy(n=> n);

res.Dump();

二、例程

2.1 取两个数组的交集

普通写法

var arr1 = new int[]{1,2,3,4,5,6};
var arr2 = new int[]{4,5,6,7,8,9};

var res = new List<int>();
foreach (var n in arr1)
{
	if (arr2.Contains(n)) res.Add(n);
}

res.Dump();

查询表达式

var arr1 = new int[]{1,2,3,4,5,6};
var arr2 = new int[]{4,5,6,7,8,9};

var res = from n in arr1 
	where arr2.Contains(n)
	select n;

res.Dump();

链式表达式

var arr1 = new int[]{1,2,3,4,5,6};
var arr2 = new int[]{4,5,6,7,8,9};

var res = arr1
	.Intersect(arr2);

res.Dump();

2.2 统计数组中数字的频率

普通写法

var rnd = new Random(1334);
var arr = Enumerable.Range(0, 200).Select(_ => rnd.Next(20));

var dic = new Dictionary<int, int>();
foreach (var n in arr)
{
	if (dic.TryGetValue(n, out int value))
	{
		dic[n] = value +1;
	}
	else 
	{
		dic[n] = 1;
	}
}

dic.Dump();

查询表达式

var rnd = new Random(1334);
var arr = Enumerable.Range(0, 200).Select(_ => rnd.Next(20));

var dic = 
	from n in arr
	group n by n into g
	select new { g.Key, Count = g.Count() };

dic.Dump();

其中

new { Key = g.Key, Count = g.Count() }

为匿名类写法

链式表达式

var rnd = new Random(1334);
var arr = Enumerable.Range(0, 200).Select(_ => rnd.Next(20));

var dic = arr
	.GroupBy(x => x)
	.Select(g => new {Key = g.Key, Count = g.Count()});

dic.Dump();

三、重要概念

3.1 延迟执行

LINQ表达式在定义时不会真正的执行,只有在真正消耗时才会执行。

代码一:

var lst = new List<int> { 1, 2, 3, 4, 5 };
var query = lst.Select(x=>x*x);

lst.Add(6);
query.Dump();

运行结果为

代码二:

var lst = new List<int>{1,2,3,4,5};

Stopwatch watch = new Stopwatch();
watch.Start();
var query = lst.Select(x =>
{
	Thread.Sleep(500);
	return x*x;
});

watch.Dump();
query.ToList();
watch.Dump();

运行结果

 通过这两个代码可以看出,如果没有对查询表达式进行消耗的操作,表达式并不会真正执行

3.2 消耗

  • 遍历 foreach
  • ToList()、ToArray()、ToDictionary()、
  • Count()、Min()、Max()
  • Take()、First()、Last()

3.3 LINQ并不仅仅是可枚举类型的扩展方法

  • IEnumerable
  • IOrderedEnumerable
  • IQueryable
  • ParallelQuery

四、扩展例程

4.1 展平

将多维数组转为一维形式

查询表达式

var mat = new int[][]{
	new [] {1,2,3,4},
	new [] {5,6,7},
	new [] {8,9,10,11,12}
};

var res = 
	from row in mat
	from data in row
	select data;
	
res.Dump();	

链式表达式

var mat = new int[][]{
	new [] {1,2,3,4},
	new [] {5,6,7},
	new [] {8,9,10,11,12}
};

var res = mat
	.SelectMany(x=>x);
	
res.Dump();	

运行结果

4.2 笛卡尔积

普通写法

for (int i = 0; i < 5; i++)
{
	for	(int j = 0; j < 4; j++)
	{
		for (int k = 0; k < 3; k++)
		{
			$"{i},{j},{k}".Dump();
		}
	}
}

运行结果


查询表达式

var prods = 
	from i in Enumerable.Range(0, 5)
	from j in Enumerable.Range(0, 4)
	from k in Enumerable.Range(0, 3)
	select $"{i},{j},{k}";
	
prods.Dump();

链式表达式

var prods = Enumerable
	.Range(0, 5)
	.SelectMany(r => Enumerable.Range(0, 4), (l,r)=>(l,r))
	.SelectMany(r => Enumerable.Range(0, 3), (l,r)=>(l.l, l.r, r))
	.Select(x=>x.ToString());
	
prods.Dump();

4.3 字母频率

查询表达式

var words = new string[]{"tom", "jerry", "spike", "tyke", "butch", "quacker"};

var query = 
	from w in words
	from c in w
	group c by c into g
	select new {g, Count=g.Count()} into a
	orderby a.Count descending
	select a;

query.Dump();

结果

链式表达式

var words = new string[]{"tom", "jerry", "spike", "tyke", "butch", "quacker"};

var query = words
	.SelectMany(x=>x)
	.GroupBy(x=>x)
	.Select(x => new { x, Count = x.Count() })
	.OrderByDescending(x => x.Count);
	
query.Dump();

4.4 批量下载文件

普通写法

var urls = new string[]
{
	"http://www.example.com/pic1.png",
	"http://www.example.com/pic2.png",
	"http://www.example.com/pic3.png"
};

$"Tasks start at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();
foreach (var url in urls)
{
	await DownloadAsync(url, url.Split('/').Last());
}
$"Tasks end at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();


async Task DownloadAsync(string url, string filename)
{
	await Task.Delay(1000);
	$"{filename} downloaded at {DateTime.Now.ToString("HH:mm:ss ffff")}.".Dump();
}

输出结果,可以看到3个任务花了3秒

Tasks start at 16:48:19 7820
pic1.png downloaded at 16:48:20 7829.
pic2.png downloaded at 16:48:21 7841.
pic3.png downloaded at 16:48:22 7844.
Tasks end at 16:48:22 7845

改进写法

var urls = new string[]
{
	"http://www.example.com/pic1.png",
	"http://www.example.com/pic2.png",
	"http://www.example.com/pic3.png"
};

var tasks = new List<Task>();
foreach (var url in urls)
{
	tasks.Add(DownloadAsync(url, url.Split("/").Last()));
}

$"Tasks start at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();
await Task.WhenAll(tasks);
$"Tasks end at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();


async Task DownloadAsync(string url, string filename)
{
	await Task.Delay(1000);
	$"{filename} downloaded at {DateTime.Now.ToString("HH:mm:ss ffff")}.".Dump();
}

运行结果,可以看到3个任务花了1秒

Tasks start at 16:47:18 1255
pic1.png downloaded at 16:47:19 1259.
pic3.png downloaded at 16:47:19 1259.
pic2.png downloaded at 16:47:19 1259.
Tasks end at 16:47:19 1265

 查询表达式

var urls = new string[]
{
	"http://www.example.com/pic1.png",
	"http://www.example.com/pic2.png",
	"http://www.example.com/pic3.png"
};

var tasks = 
	from url in urls
	select DownloadAsync(url, url.Split("/").Last());

$"Tasks start at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();
await Task.WhenAll(tasks);
$"Tasks end at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();

async Task DownloadAsync(string url, string filename)
{
	await Task.Delay(1000);
	$"{filename} downloaded at {DateTime.Now.ToString("HH:mm:ss ffff")}.".Dump();
}

链式表达式

var urls = new string[]
{
	"http://www.example.com/pic1.png",
	"http://www.example.com/pic2.png",
	"http://www.example.com/pic3.png"
};

var tasks = urls 
	.Select(url => DownloadAsync(url, url.Split("/").Last()));
	
$"Tasks start at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();
await Task.WhenAll(tasks);
$"Tasks end at {DateTime.Now.ToString("HH:mm:ss ffff")}".Dump();

async Task DownloadAsync(string url, string filename)
{
	await Task.Delay(1000);
	$"{filename} downloaded at {DateTime.Now.ToString("HH:mm:ss ffff")}.".Dump();
}

五、避坑集合

5.1 尽量使用自带方法

5.1.1 First()、Last()

比如说一个集合处理完后,需要得到其第一个元素,使用以下写法

var arr = new List<int>{1,3,5,7,9,2,4,6,8,0};

arr.Select(x=>x).ToList()[0].Dump();

虽然也得到了结果,但是多占用了内存,而是应该尽量使用IEnumerable<T>自带的方法

var arr = new List<int>{1,3,5,7,9,2,4,6,8,0};

arr.Select(x=>x).First().Dump();

5.1.2 Average()

不要使用Sum+Count方法求平均数:

var arr = new List<int>{1,3,5,7,9,2,4,6,8,0};

(arr.Sum()/arr.Count).Dump();

使用自带方法Average

var arr = new List<int>{1,3,5,7,9,2,4,6,8,0};

arr.Average().Dump();

5.1.3 Count()、First()、Min()、Max()等方法可以传参

比如查找第一个偶数

使用Where+First

var arr = new List<int>{1,3,5,7,9,2,4,6,8,0};

arr.Where(x=>x%2==0).First().Dump();

可以把条件直接翻入First

var arr = new List<int> { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };

var res = 
	from x in arr
	select x;

5.1.4 Max()和MaxBy()的区别

比如说,查找年龄最大的对象

var people = new List<Person>
{
	new ("Tom", 18),
	new ("Jerry", 19),
	new ("Nason", 20),
};

var age = people.Max(x=> x.Age);
var p = people.First(x=>x.Age == age);
p.Dump();

record Person(string Name, int Age);

其实使用MaxBy就可简洁实现

var people = new List<Person>
{
	new ("Tom", 18),
	new ("Jerry", 19),
	new ("Nason", 20),
};

var p = people.MaxBy(x=>x.Age);
p.Dump();

record Person(string Name, int Age);

5.1.5 Default的使用

First和FirstOrDefault,Last和LastOrDefault,等等

比如说以下代码

var people = new List<Person>
{
	new ("Tom", 18),
	new ("Jerry", 19),
	new ("Nason", 20),
};

if (people.Any(x=>x.Age >= 21))
	people.First(x=>x.Age >= 21).Dump();

record Person(string Name, int Age);

使用FirstOrDefault后

var people = new List<Person>
{
	new ("Tom", 18),
	new ("Jerry", 19),
	new ("Nason", 20),
};

people.FirstOrDefault(x=>x.Age >= 21).Dump();

record Person(string Name, int Age);

5.2 注意开销

5.2.1 滥用ToList(),arr.Where().OrderBy().ToList()[0]

非必要不使用ToList,因为会多占用空间

5.2.2 滥用Count(),Count()>0

在判断集合中是否存在符合某个条件的元素时,应该使用Any(),

因为Count()是需要遍历所有元素,Any()是遇到第一个符合条件的就返回(最极端时才会遍历所有元素)

5.2.3 滥用OrderBy(),不适用Sort

OrderBy().ToList()需要重新开辟内存空间,Sort()是在原有集合上排序,不会多增加空间

而且OrderBy(x=>x)使用了lamda表达式,执行时间更长

5.2.4 不知道First()与Single()的区别

First是返回第一个符合条件的元素(可能有多个)

Single是有且只有一个

6. LINQ个人总结

6.1 命名

6.1.1 from

from后面是变量及变量的命名,作用域为当前LINQ语句。

var arr = new List<int> { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
var res = 
	from x in arr
	select x;
var res = 
	from x in Enumerable.Range(0, 5)
	from y in Enumerable.Range(10, 15)
	from z in Enumerable.Range(20, 25)
	select new {First = x, Second = y, Third = z} ;
	
res.Dump();

6.1.2 into

from前面是变量,后面是变量的命名,作用域为当前LINQ语句。

var query =
	from x in Enumerable.Range(0, 5)
	select new { x, Square = x * x} into y
	select y;
	
query.Dump();

6.1.3 let

let后面等号前为变量名,等号后为变量值,作用域为当前LINQ语句。

var query =
	from x in Enumerable.Range(0, 5)
	select new { x, Square = x * x} into y
	let result = y.Square
	select result;
	
query.Dump();

6.2 结尾

LINQ语句结尾必须要是select

6.3 Join

Join 个操作 - C# | Microsoft Learn

var students = new List<Student>()
{
	new (){ FirstName = "Bruce", LastName = "Cambell", ID = 10, Year = GradeLevel.FirstYear, DepartmentID = 223},
	new (){ FirstName = "Cindy", LastName = "Haneline", ID = 11, Year = GradeLevel.SecondYear, DepartmentID = 300},
	new (){ FirstName = "Andrea", LastName = "Deville", ID = 12, Year = GradeLevel.ThirdYear, DepartmentID = 400},
};

var teachers = new List<Teacher>()
{
	new (){ First = "Anita", Last = "Ryan", ID = 32, City = "NewYork" },
	new (){ First = "George", Last = "Bunkelman", ID = 44, City = "Washington"},
	new (){ First = "Andrew", Last = "Carter", ID = 50, City = "LosAngeles"}
};

var departments = new List<Department>()
{
	new (){ Name = "Secretariat", ID = 500, TeacherID = 32},
	new (){ Name = "General Office", ID = 300, TeacherID = 44},
	new (){ Name = "Law Committee", ID = 400, TeacherID = 50},
};

public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
	public string FirstName { get; init; }
	public string LastName { get; init; }
	public int ID { get; init; }
	public GradeLevel Year { get; init; }
	public int DepartmentID { get; init; }
}

public class Teacher
{
	public string First { get; init; }
	public string Last { get; init; }
	public int ID { get; init; }
	public string City { get; init; }
}
public class Department
{
	public string Name { get; init; }	
	public int ID { get; init; }
	public int TeacherID { get; init; }
}

6.3.1 Join ... in ... on ... equals ...

基于特定值联接两个序列


var query = from student in students
			join department in departments on student.DepartmentID equals department.ID
			select new { Name = $"{student.FirstName} {student.LastName}", DepartmentName = department.Name };
			
query.Dump();

6.3.2 join … in … on … equals … into …

基于特定值联接两个序列,并对每个元素的结果匹配项进行分组


IEnumerable<IEnumerable<Student>> studentGroups = from department in departments
												  join student in students on department.ID equals student.DepartmentID into studentGroup
												  select studentGroup;
studentGroups.Dump();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2132385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“勇者斗恶龙”即将上演,乐道L60剑指Model Y

文/王俣祺 导语&#xff1a;只要说到“二十多万”“大品牌”“纯电SUV”这一系列关键词&#xff0c;特斯拉Model Y一定是绕不开的车型&#xff0c;从近几年的销量来看&#xff0c;目前市面上很难有车型能“硬刚”。不过既然有“恶龙”就会有“勇者”&#xff0c;蔚来子品牌乐道…

“薅羊毛”时间到, 容声以旧换新“钜惠”升级

9月13日&#xff0c;由佛山市商务局、顺德区人民政府指导&#xff0c;海信家电集团主办的以旧换新佛山发布活动启幕。 海信家电&#xff08;SZ 000921&#xff0c;HK 00921&#xff09;旗下容声冰箱叠加国家以旧换新补贴&#xff0c;把“以旧换新”升级到“品质换新”&#xf…

汤臣倍健业绩大幅下滑:经销商减少131家,销售费用转化不达预期

《港湾商业观察》廖紫雯 在膳食营养补充剂&#xff08;VDS&#xff09;行业竞争加剧的情况下&#xff0c;日前&#xff0c;汤臣倍健股份有限公司&#xff08;以下简称&#xff1a;汤臣倍健&#xff0c;300146.SZ&#xff09;披露2024年半年报业绩情况。上半年&#xff0c;公司…

爱普生相机SD卡格式化后数据恢复指南

我借了朋友的‌爱普生相机&#xff0c;想查看一下内存&#xff0c;哎呀&#xff0c;一不小心按错了&#xff0c;竟然执行了格式化操作&#xff0c;这可真是太让人郁闷了&#xff0c;这还有机会挽救数据吗&#xff1f;心塞&#xff0c;求帮助&#xff01; 随着数码摄影的普及&am…

GUI编程10:窗口监听事件

视频链接&#xff1a;12、窗口监听事件_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p12&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.使用成员内部类方式实现窗口监听 ackage com.yundait.lesson03;import java.awt.*; import java.awt.event.Win…

学习之git的团队协作

git团队协作 一 团队内协作 生成SSH公钥私钥 一&#xff08;跨团队协作&#xff09;

java的ReentrantLock原理

示例代码如下&#xff0c;主要包括两个方法&#xff1a; public static void main(String[] args) throws InterruptedException {Lock lock new ReentrantLock();lock.lock();lock.unlock();}双向链表初始状态&#xff1a; 双向链表从尾节点添加阻塞的节点&#xff1a; 读源…

WebLogic 笔记汇总

WebLogic 笔记汇总 一、weblogic安装 1、创建用户和用户组 groupadd weblogicuseradd -g weblogic weblogic # 添加用户,并用-g参数来制定 web用户组passwd weblogic # passwd命令修改密码# 在文件末尾增加以下内容 cat >>/etc/security/limits.conf<<EOF web…

小程序开发设计-第一个小程序:注册小程序开发账号②

注册小程序开发账号整体流程&#xff1a; 1.点击注册按钮&#xff1a; 使用浏览器打开微信公众平台网址&#xff0c;点击右上角的“立即注册”即可进入到小程序开发账号的注册流程&#xff0c;主要流程截图如下&#xff1a; 2.选择注册账号的类型&#xff1a; 3.填好账号信息&…

天空卫士项目荣获“2024 IDC 中国20大杰出安全项目 ”奖项 ,实力见证安全守护

9月11日&#xff0c; IDC在上海圆满举办安全风险管控峰会&#xff0c;并现场官宣“2024 IDC中国20大杰出安全项目(CSO20) ”和“2024 IDC中国 CSO名人堂 (十大人物) ” 奖项名单。联通软研院申报的联通邮件系统安全合规建设项目被评为“2024 IDC中国20大杰出安全项目(CSO20) ”…

【Qt 编程入门】如何用 Qt 实现一个基本的计算器

前言 QT中实现一个简单的计算器是一个比较好的练手项目&#xff0c;下面是源码&#xff1a; Calculator计算器 设计界面 待实现的界面主要包含两个部分&#xff1a; 输入输出栏用户点击的按钮 输入输出栏通过QLabel类实现&#xff0c;而用户点击按钮通过QPushButton或QToo…

3分钟带你快速了解 Java 接口

我醉欲眠卿且去 明朝有意抱琴来 目录 接口的概念 定义接口 接口格式 接口的特性 &#xff08;1&#xff09;接口不能直接创建对象 &#xff08;2&#xff09;接口不能用 final 修饰 &#xff08;3&#xff09;接口中定义的变量 &#xff08;4&#xff09;接口中定义的方法 接…

Visual Studio Installer 2022 安装提示正在提取文件 进度条不动 0B每秒

Visual Studio Installer 稍等片刻...正在提取文件 进度条不动 0B每秒 一段时间后提示 循环下载安装文件 无法下载安装文件。请检查Internet 连接&#xff0c;然后重试 打开vs2017 或者vs2019或者vs2022的安装程序(visual studio installer)时&#xff0c;下载进度条不动&…

【C++】认识C++(前言)

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:C_小米里的大麦的博客-CSDN博客 &#x1f381;代码托管:C: 探索C编程精髓&#xff0c;打造高效代码仓库 (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、本节概述 二、什么是C 三、C发展史 四…

为了准确计算延迟退休时间,我做了一个退休年龄计算器

延迟退休计算方法 原本退休分为三种情况&#xff0c;男性&#xff0c;女工人&#xff0c;女干部 男性&#xff1a;退休年龄为60岁。女干部&#xff1a;退休年龄为55岁。女工人&#xff1a;退休年龄为50岁。 现在延迟以后&#xff08;根据2024年9月13日公布的规则&#xff09…

武汉墨家人俱乐部

这里主要是墨家人聊科技的俱乐部&#xff0c;想来的在评论区报名吧&#xff01; 这里有VR 各种AI软件 绘图 炼丹

半导体AI硬件基础设施发展洞察

2024 半导体分析洞察&#xff1a;AI 硬件基础设施篇 一、引言随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;AI 硬件基础设施在整个 AI 生态系统中的地位日益凸显。半导体作为 AI 硬件基础设施的核心组成部分&#xff0c;正面临着前所未有的机遇和挑战。2…

作图神器!AI 免抠图生成工具,告别素材烦恼

在设计的道路上&#xff0c;相信大家都遇到过这样令人头疼的问题&#xff1a;好不容易找到合适的素材&#xff0c;结果下载的时候提示需要付费&#xff0c;更悲催的是&#xff0c;即使花钱下载了还有可能面临侵权风险。曾经的我也深陷这样的困境&#xff0c;直到我发现了一个超…

java重点学习-集合(Map)

七 集合&#xff08;Map&#xff09; 7.8 什么是二叉树 1.什么是二叉树 每个节点最多有两个“叉”&#xff0c;分别是左子节点和右子节点。不要求每个节点都有两个子节点&#xff0c;有的节点只有左子节点&#xff0c;有的节点只有右子节点。二叉树每个节点的左子树和右子树也…

红帽7—tomcat的部署方法

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和 并发访问用户不是很多的场合下被普遍使用&#xff0c;Tomcat 具有处理HTML页面的功能&#xff0c;它还是一个Servlet和 JSP容器 一&#xff1a;安装 Tomcat …