惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
博客园 - 司徒正美
WordPress大学
WordPress大学
爱范儿
爱范儿
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
博客园_首页
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
MyScale Blog
MyScale Blog
IT之家
IT之家
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
Microsoft Security Blog
Microsoft Security Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
人人都是产品经理
人人都是产品经理

Donghai's Blog

使用DBeaver连接Dynamics 365 Dataverse | Donghai C# 技术备忘 - LINQ | Donghai 为Astro站点添加Google Analytics | Donghai 20260819 说些什么 | Donghai Dynamics 365 interview questions | Donghai C# 技术备忘 (1) | Donghai Dynamics 365 事件框架、事件执行管道 | Donghai 马拉松配速表 | Donghai 马拉松赛事分级 | Donghai 抖音创作日志(2026年) | Donghai 2026年跑步日志(5月开始) | Donghai 2FA | Donghai 初尝Github Actions | Donghai AstroPaper主题添加Waline评论 | Donghai Dynamics 365集中视图/聚焦视图/Focused View | Donghai AstroPaper主题添加GitHub风格的Markdown警告框 | Donghai AstroPaper主题自定义记录 | Donghai 我日常收藏的优质网站资源导航(持续更新) | Donghai 如何从归档页批量获取URL并提交到Bing Webmaster Tools | Donghai PaperMod 使用 Zeoseven 自定义网页字体 | Donghai 我常用的Prompt | Donghai Hugo默认时区导致本地预览文章不显示 | Donghai 通过手动提交工单解决Bing不收录网站问题 | Donghai 如何访问 Power BI 管理门户(Power BI Admin Portal) | Donghai 还在手敲时间戳?Win11输入法一个技巧,秒输当前时间戳 | Donghai 工作术语备忘录(持续更新) | Donghai 使用XrmToolBox导出安全角色表级权限到Excel | Donghai 24年的年终总结 | Donghai World笔记 | Donghai Excel笔记 | Donghai
C# 技术备忘 - LINQ - Donghai’s Blog
Donghai · 2026-08-24 · via Donghai's Blog

准备示例数据源#

本地创建控制台应用程序,添加数据源。

using System;

using System.Collections.Generic;

using System.Linq;

namespace DaikinITC.D365.Sample

{

public class Program

{

static void Main(string[] args)

{

// [!code ++:12]

// 准备一个学生列表作为数据源

List<Student> students = new List<Student>

{

new Student { Id = 1, Name = "张三", Age = 20, Class = "软件工程1班", Score = 85, Balance = 50.5m },

new Student { Id = 2, Name = "李四", Age = 22, Class = "软件工程2班", Score = 92, Balance = 120.0m },

new Student { Id = 3, Name = "王五", Age = 21, Class = "软件工程1班", Score = 78, Balance = null }, // 没开通

new Student { Id = 4, Name = "赵六", Age = 23, Class = "软件工程3班", Score = 88, Balance = 30.0m },

new Student { Id = 5, Name = "孙七", Age = 20, Class = "软件工程2班", Score = 95, Balance = 200.0m },

new Student { Id = 6, Name = "周八", Age = 22, Class = "软件工程1班", Score = 60, Balance = null }, // 没开通

};

Console.ReadKey();

}

}

// [!code ++:8]

public class Student

{

public int Id { get; set; }

public string Name { get; set; }

public int Age { get; set; }

public string Class { get; set; }

public int Score { get; set; }

public decimal? Balance { get; set; } // 饭卡余额

}

}

#1 两种语法#

  1. 查询表达式(类似SQL)

var result = from s in students

where s.Age > 21

select s;

  1. Lambda表达式 *掌握,项目经常使用

var result = students.Where(s => s.Age > 21);

#2 查询#

where#

// 过滤(Where)—— 找出年龄大于21岁的学生

var temp1 = students.Where(x => x.Age > 21);

select#

// 选择(Select)—— 只取出姓名和分数,生成新对象

var temp2 = students.Select(s => new { s.Name, s.Score });

selectMany#

#3 排序#

方法排序方向说明
OrderBy升序对集合进行升序排序(从小到大)
OrderByDescending降序对集合进行降序排序(从大到小)
ThenBy升序在已有排序基础上,进行后续升序排序
ThenByDescending降序在已有排序基础上,进行后续降序排序

// 按分数升序

var sorted2 = students.OrderBy(x => x.Score);

// 按分数降序

var sorted1 = students.OrderByDescending(x => x.Score);

// 先按班级升序,再按分数降序

var multiSort = students.OrderBy(x => x.Class).ThenByDescending(x => x.Score);

#4 聚合方法 (Count, Sum, Average, Max, Min#

var stdCount = students.Count();

Console.WriteLine($"一共有 {stdCount} 位学生");

var totalBalance = students.Sum(x => x.Balance);

Console.WriteLine($"这些学生的饭卡余额一共是 {totalBalance}");

var avgScore = students.Average(s => s.Score);

var maxScore = students.Max(s => s.Score);

var minScore = students.Min(s => s.Score);

Console.WriteLine($"平均分: {avgScore}, 最高分: {maxScore}, 最低分: {minScore}");

#5 条件判断#

方法返回类型说明使用场景
Allbool判断所有元素是否都满足指定条件检查全员达标、
Anybool判断是否存在元素满足指定条件(或集合是否为空)
Containsbool判断集合中是否包含指定的特定元素检查某个对象是否在集合中

All#

// 1. 所有学生都及格了吗?(>= 60分)

bool allPassed = students.All(s => s.Score >= 60);

Console.WriteLine($"所有学生都及格: {allPassed}");

// 2. 所有学生都超过80分了吗?

bool allOver80 = students.All(s => s.Score >= 80);

// 3. 所有学生都满18岁了吗?

bool allAdult = students.All(s => s.Age >= 18);

Console.WriteLine($"所有学生都成年: {allAdult}");

Any#

// 1. 判断集合是否有元素(比 Count() > 0 高效)

bool hasStudents = students.Any();

Console.WriteLine($"集合有学生吗: {hasStudents}");

// 2. 判断是否有学生超过90分

bool hasHighScore = students.Any(s => s.Score > 90);

Console.WriteLine($"有超过90分的学生吗: {hasHighScore}");

// 3. 判断是否有不及格的学生

bool hasFailed = students.Any(s => s.Score < 60);

Console.WriteLine($"有不及格的学生吗: {hasFailed}");

Contains#

// 1. 基本类型 Contains

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

Console.WriteLine($"包含 3: {ids.Contains(3)}"); // True

Console.WriteLine($"包含 10: {ids.Contains(10)}"); // False

var names = new List<string> { "张三", "李四", "王五" };

Console.WriteLine($"包含 '张三': {names.Contains("张三")}"); // True

Console.WriteLine($"包含 '赵六': {names.Contains("赵六")}"); // False

// 2. 引用类型 Contains(需要实现 IEquatable 或重写 Equals)

var targetStudent = students.First(s => s.Id == 2);

bool containsTarget = students.Contains(targetStudent);

Console.WriteLine($"包含李四吗: {containsTarget}"); // True

// 3. Contains 用于字符串列表

var classNames = new List<string> { "软件工程1班", "软件工程2班", "软件工程3班" };

bool hasClass = classNames.Contains("软件工程1班");

Console.WriteLine($"有软件工程1班吗: {hasClass}"); // True

#6 分组和连接(Join)#

(2026-08-24 14:52<28> 补充)

方法类型说明
GroupBy分组根据指定的键对集合元素进行分组
Join连接(内连接)基于匹配键关联两个集合,类似 SQL INNER JOIN
GroupJoin连接(分组连接)关联两个集合并将结果分组,类似 SQL LEFT JOIN

GroupBy#

根据指定的键对集合元素进行分组

1. 基础分组:按班级分组#

var groupsByClass = students.GroupBy(s => s.Class);

foreach (var group in groupsByClass)

{

Console.WriteLine($"班级: {group.Key}");

foreach (var student in group)

{

Console.WriteLine($" {student.Name} - {student.Score}分");

}

}

2.分组 + 聚合统计#

var classStats = students.GroupBy(s => s.Class)

.Select(g => new {

ClassName = g.Key,

Count = g.Count(),

AvgScore = g.Average(s => s.Score),

MaxScore = g.Max(s => s.Score),

MinScore = g.Min(s => s.Score)

});

foreach (var stat in classStats)

{

Console.WriteLine($"{stat.ClassName}: {stat.Count}人, 平均分{stat.AvgScore:F1}, " +

$"最高{stat.MaxScore}, 最低{stat.MinScore}");

}

3.分组 + 筛选#

// (只显示平均分大于80的班级)

var highClass = students.GroupBy(s => s.Class)

.Where(g => g.Average(s => s.Score) > 80)

.Select(g => new { g.Key, AvgScore = g.Average(s => s.Score) });

foreach (var hc in highClass)

{

Console.WriteLine($"{hc.Key}: 平均分{hc.AvgScore:F1}");

}

4.多键分组(按班级和年龄分组)#

var multiGroup = students.GroupBy(s => new { s.Class, s.Age });

foreach (var group in multiGroup)

{

Console.WriteLine($"班级: {group.Key.Class}, " +

$"年龄: {group.Key.Age}, 人数: {group.Count()}");

}

Join#

基于匹配键关联两个集合,类似 SQL INNER JOIN。

调整关联数据:

var classes = new List<Class>

{

new Class { Id = 1, ClassName = "软件工程1班", Teacher = "王老师" },

new Class { Id = 2, ClassName = "软件工程2班", Teacher = "李老师" },

new Class { Id = 3, ClassName = "软件工程3班", Teacher = "张老师" },

};

public class Class

{

public int Id { get; set; }

public string ClassName { get; set; }

public string Teacher { get; set; }

}

// 修改 Student 类,添加 ClassId

public class Student

{

public int Id { get; set; }

public string Name { get; set; }

public int Age { get; set; }

public int ClassId { get; set; } // 外键

public string Class { get; set; } // 保留原字段

public int Score { get; set; }

public decimal? Balance { get; set; }

}

Join - 内连接(查询语法)#

var querySyntax = from s in students

join c in classes on s.ClassId equals c.Id

select new

{

s.Name,

s.Score,

c.ClassName,

c.Teacher

};

foreach (var item in querySyntax)

{

Console.WriteLine($"{item.Name} - {item.ClassName} - 班主任: {item.Teacher}");

//张三 - 软件工程1班 - 班主任: 王老师

//李四 - 软件工程2班 - 班主任: 李老师

//王五 - 软件工程1班 - 班主任: 王老师

//赵六 - 软件工程3班 - 班主任: 张老师

//孙七 - 软件工程2班 - 班主任: 李老师

//周八 - 软件工程1班 - 班主任: 王老师

}

Join - 内连接#

var methodSyntax = students.Join(classes,

s => s.ClassId, // 左表外键

c => c.Id, // 右表主键

(s, c) => new { // 结果投影

s.Name,

s.Score,

c.ClassName,

c.Teacher

});

foreach (var item in methodSyntax)

{

Console.WriteLine($"{item.Name} - {item.ClassName} - {item.Score}分");

//张三 - 软件工程1班 - 85分

//李四 - 软件工程2班 - 92分

//王五 - 软件工程1班 - 78分

//赵六 - 软件工程3班 - 88分

//孙七 - 软件工程2班 - 95分

//周八 - 软件工程1班 - 60分

}

Join + 条件过滤#

var filteredJoin = students.Join(classes,

s => s.ClassId,

c => c.Id,

(s, c) => new { s, c })

.Where(x => x.s.Score > 80)

.Select(x => new {

x.s.Name,

x.s.Score,

x.c.ClassName,

x.c.Teacher

});

GroupJoin(分组连接)#

GroupJoin - 基本用法(查询语法)#

var groupQuery = from c in classes

join s in students on c.Id equals s.ClassId into studentGroup

select new

{

ClassName = c.ClassName,

Teacher = c.Teacher,

Students = studentGroup,

StudentCount = studentGroup.Count()

};

foreach (var item in groupQuery)

{

Console.WriteLine($"班级: {item.ClassName} ({item.Teacher}) - " +

$"学生数: {item.StudentCount}");

foreach (var student in item.Students)

{

Console.WriteLine($" {student.Name} - {student.Score}分");

}

}

GroupJoin - 方法语法#

var groupMethod = classes.GroupJoin(

students,

c => c.Id, // 左表主键

s => s.ClassId, // 右表外键

(c, studentGroup) => new {

ClassName = c.ClassName,

Teacher = c.Teacher,

Students = studentGroup,

AvgScore = studentGroup.Average(s => s.Score)

});

foreach (var item in groupMethod)

{

Console.WriteLine($"{item.ClassName} - 平均分: {item.AvgScore:F1}");

}

GroupJoin + 处理空组#

var groupWithEmpty = classes.GroupJoin(

students,

c => c.Id,

s => s.ClassId,

(c, studentGroup) => new {

ClassName = c.ClassName,

Students = studentGroup.DefaultIfEmpty(), // 无学生时返回默认值

HasStudents = studentGroup.Any()

});

foreach (var item in groupWithEmpty)

{

Console.WriteLine($"{item.ClassName}: {(item.HasStudents ? "有学生" : "无学生")}");

}

#7 集合转换#

(2026-08-24 16:12<06> 补充)

这些方法把查询结果立即执行并转换成不同数据类型。

方法描述
ToList将实现了IEnumerable<T>接口的集合转换为一个List<T>类型的对象,属于将集合转换为特定类型列表的方法
ToArray将一个实现了IEnumerable<T>接口的集合转换为一个数组,属于将集合转换为数组类型的方法
ToDictionary将一个IEnumerable<T>集合转换为一个Dictionary<TKey,TValue>键值对集合(字典)的方法,注意 ToDictionary 要求键唯一,否则抛出异常
ToLookup将一个IEnumerable<T>集合转换为一个泛型Lookup<TKey,TElement>Lookup<TKey,TElement>一个一对多字典,用于将键映射到值的集合

ToList() - 转成 List#

var students = new List<Student>

{

new Student { Id = 1, Name = "张三", Age = 20, Score = 85, Balance = 50.5m },

new Student { Id = 2, Name = "李四", Age = 22, Score = 92, Balance = 120.0m },

new Student { Id = 3, Name = "王五", Age = 21, Score = 78, Balance = null },

};

// ToList - 立即执行,生成新的 List

var highScoreList = students.Where(s => s.Score >= 80).ToList();

Console.WriteLine($"高分学生数: {highScoreList.Count}"); // 输出:2

// 关键:ToList 会立即执行并固化结果

var filtered = students.Where(s => s.Age > 20);

students.Add(new Student { Id = 4, Name = "赵六", Age = 25, Score = 88 }); // 后添加

// 延迟执行:filtered 会包含赵六

Console.WriteLine($"延迟执行数量: {filtered.Count()}"); // 输出:3(李四、王五、赵六)

var filteredToList = students.Where(s => s.Age > 20).ToList();

students.Add(new Student { Id = 5, Name = "孙七", Age = 24, Score = 90 }); // 再添加

// ToList 已固化:不包含孙七

Console.WriteLine($"ToList固化数量: {filteredToList.Count}"); // 输出:3(李四、王五、赵六)

ToArray() - 转成数组#

// ToArray - 转成数组

var highScoreArray = students.Where(s => s.Score >= 80).ToArray();

Console.WriteLine($"数组类型: {highScoreArray.GetType()}"); // 输出:Student[]

Console.WriteLine($"数组长度: {highScoreArray.Length}"); // 输出:2

// 使用场景:需要数组的索引访问

for (int i = 0; i < highScoreArray.Length; i++)

{

Console.WriteLine($"第{i}个高分学生: {highScoreArray[i].Name}");

}

ToDictionary() - 转成字典(常用)#

// ToDictionary - 把集合转成字典,必须指定唯一的 Key

try

{

// 用 Id 作为 Key(唯一)

var dictById = students.ToDictionary(s => s.Id);

Console.WriteLine($"字典数量: {dictById.Count}");

// 通过 Key 快速查找(O(1) 时间复杂度)

var student = dictById[2];

Console.WriteLine($"ID=2的学生: {student.Name}"); // 输出:李四

// 遍历字典

foreach (var kvp in dictById)

{

Console.WriteLine($"Key: {kvp.Key}, Name: {kvp.Value.Name}");

}

}

catch (ArgumentException ex)

{

Console.WriteLine($"转字典报错: {ex.Message}"); // 如果 Key 重复会报错

}

// 带值选择器的 ToDictionary(可以只取部分字段)

var dictNameAndScore = students.ToDictionary(

s => s.Id, // Key 选择器

s => new { s.Name, s.Score } // Value 选择器

);

Console.WriteLine($"学生2: {dictNameAndScore[2].Name} - {dictNameAndScore[2].Score}分");

ToLookup() - 一对多字典(类似分组)#

// ToLookup - 一个 Key 对应多个值(类似分组,但可以索引)

var lookupByClass = students.ToLookup(s => s.Class);

// 获取某个班级的所有学生

var class1Students = lookupByClass["软件工程1班"];

Console.WriteLine($"软件工程1班学生数: {class1Students.Count()}");

foreach (var s in class1Students)

{

Console.WriteLine($" {s.Name}");

}

// 检查某个 Key 是否存在

Console.WriteLine($"是否有软件工程1班: {lookupByClass.Contains("软件工程1班")}");

Console.WriteLine($"是否有软件工程5班: {lookupByClass.Contains("软件工程5班")}");