05-C#.Net-Lambda与LINQ-学习笔记

C# Lambda表达式与委托深度解析:从语法糖到表达式树 Lambda表达式是C#中连接函数式编程与面向对象范式的桥梁,其本质并非简单语法糖,而是编译器根据上下文(Func<T> 或 Expression<Func<T>>)动态生成委托或可遍历表达式树的类型安全机制。理解其底层原理——包括MulticastDelegate内存结构、泛型委托避免装箱的性能优势、以及Expression树如何被EF Core翻译为SQL——对构建高性能、可维护的.NET应用至关重要。它支撑着ASP.NET Core中间件、Blazor事件绑定、LINQ查询优化及动态规则引擎等核心场景 阅读详情

一、Lambda 表达式的演变

Lambda 表达式是委托的语法糖,本质上是匿名方法的简化写法,编译器最终会将其编译为委托实例。

.NET 1.0/1.1 — 命名方法委托

public delegate void NoReturnWithPara(int x, string y);

NoReturnWithPara method = new NoReturnWithPara(Study);
method.Invoke(123, "Richard");

private void Study(int id, string name)
{
    Console.WriteLine($"{id} {name} 学习.Net高级班");
}

缺点:方法必须单独定义,代码分散,不够内聚。

.NET 2.0 — 匿名方法

引入 delegate 关键字,方法体可以直接内联,并且能访问外部局部变量(闭包):

int i = 0;
NoReturnWithPara method = new NoReturnWithPara(delegate (int x, string y)
{
    Console.WriteLine(x);
    Console.WriteLine(y);
    Console.WriteLine(i); // 可以访问外部变量
});

.NET 3.0 — Lambda 表达式

用 => 操作符(读作 "goes to")替代 delegate 关键字,语法更简洁:

// 完整形式
NoReturnWithPara method = (int x, string y) =>
{
    Console.WriteLine(x);
    Console.WriteLine(y);
};

// 省略参数类型(编译器自动推导)
NoReturnWithPara method = (x, y) =>
{
    Console.WriteLine(x);
};

Lambda 简化规则

规则从上到下依次叠加:

// 方法体只有一行,省略大括号
NoReturnWithPara method1 = (x, y) => Console.WriteLine(x);

// 只有一个参数,省略小括号
Action<string> method2 = s => Console.WriteLine(s);

// 有返回值且只有一行,省略 return
Func<string> func1 = () => "hello";
Func<int, string> func2 = i => i.ToString();

二、匿名类型与 var

匿名类型的三种接收方式

// object:无法访问属性,编译报错
object model = new { Id = 1, Name = "张三" };
// model.Id  // 编译错误

// dynamic:运行时检查,可以访问,但没有编译期类型安全
dynamic dModel = new { Id = 1, Name = "张三" };
Console.WriteLine(dModel.Id); // 运行时才检查

// var:编译器推导类型,推荐用法
var model = new { Id = 1, Name = "张三" };
Console.WriteLine(model.Id); // 编译期就能检查

var 的特点

  • 必须在声明时初始化,编译器根据右侧值推导类型
  • 不能赋值为 null(无法推导类型)
  • 不能作为方法参数或返回值类型
  • 匿名类型的属性是只读的,不能修改
var i = 13;        // 推导为 int
var s = "Richard"; // 推导为 string
// var x = null;   // 编译错误

三、扩展方法

扩展方法允许在不修改原类型源码的情况下为其添加新方法,是 LINQ 的实现基础,也是遵循开闭原则的常用手段。

三要素

  1. 定义在静态类中
  2. 方法本身是静态方法
  3. 第一个参数前加 this 关键字,表示被扩展的类型
public static class MethodExtension
{
    // 为 Student 扩展方法
    public static void StudyFramework(this Student student)
    {
        Console.WriteLine($"{student.Id} {student.Name} 学习架构课程");
    }

    // 为 int 扩展方法
    public static string IntToString(this int i)
    {
        return i.ToString();
    }

    // 为 string 扩展方法,带默认参数
    public static string FormatString(this string oldString, int length = 5)
    {
        if (string.IsNullOrWhiteSpace(oldString))
            return string.Empty;
        else if (oldString.Length <= length)
            return oldString;
        else
            return $"{oldString.Substring(0, length)}...";
    }
}

调用时像实例方法一样使用:

Student student = new Student { Id = 1, Name = "张三" };
student.StudyFramework();

int num = 100;
string s = num.IntToString();

string text = "这是一段很长的文本内容";
string result = text.FormatString(10); // "这是一段很长的文..."(取前10个字符加省略号)

注意事项

  • 可以扩展任何类型,包括密封类和值类型
  • 如果类型本身有同名方法,优先调用类型自身的方法,扩展方法不会覆盖它
  • 扩展泛型类型(如 this T t)会影响所有类型,侵入性强,慎用
  • 扩展 object 会影响所有类型,不推荐
  • 调用方需要引入扩展方法所在的命名空间

四、LINQ 基础用法

LINQ 有两种等价语法,可以混用:

// 扩展方法语法(Lambda 风格)
var list1 = studentList.Where(s => s.Age < 30);

// 查询表达式语法(类 SQL 风格)
var list2 = from s in studentList
            where s.Age < 30
            select s;

Where — 条件过滤

var result = studentList.Where(s => s.Age < 30);

Select — 投影

将数据转换为新的形状,可以组合字段、计算属性:

var result = studentList
    .Where(s => s.Age < 30)
    .Select(s => new
    {
        IdName = s.Id + s.Name,
        ClassName = s.ClassId == 2 ? "高级班" : "其他班"
    });

OrderBy / ThenBy / OrderByDescending — 排序

var result = studentList
    .OrderBy(s => s.Id)              // 升序
    .ThenBy(s => s.Name)             // 多字段排序(第二优先级)
    .OrderByDescending(s => s.Age);  // 降序

Skip / Take — 分页

必须先排序再分页,否则结果不稳定:

var result = studentList
    .OrderBy(s => s.Id)
    .Skip(10)   // 跳过前 10 条
    .Take(5);   // 取 5 条

GroupBy — 分组

// 查询表达式
var result = from s in studentList
             where s.Age < 30
             group s by s.ClassId into sg
             select new
             {
                 Key = sg.Key,
                 MaxAge = sg.Max(t => t.Age)
             };

// 扩展方法等价写法
var result = studentList
    .Where(s => s.Age < 30)
    .GroupBy(s => s.ClassId)
    .Select(sg => new
    {
        Key = sg.Key,
        MaxAge = sg.Max(t => t.Age)
    });

GroupBy 也支持多字段分组:group s by new { s.ClassId, s.Age }

Join — 连接查询

内连接(只返回两边都匹配的数据):

// 查询表达式,连接条件必须用 equals,不能用 ==
var result = from s in studentList
             join c in classList on s.ClassId equals c.Id
             select new { s.Name, c.ClassName };

// 扩展方法等价写法
var result = studentList.Join(
    classList,
    s => s.ClassId,
    c => c.Id,
    (s, c) => new { s.Name, c.ClassName }
);

左连接(左边全部保留,右边没有匹配时为 null):

var result = from s in studentList
             join c in classList on s.ClassId equals c.Id
             into scList
             from sc in scList.DefaultIfEmpty()  // 关键:DefaultIfEmpty 保留左边无匹配的行
             select new
             {
                 s.Name,
                 ClassName = sc == null ? "无班级" : sc.ClassName
             };

右连接只需把左右两个数据源交换位置即可。


五、LINQ 实现原理

从重复代码到 LINQ 的演变思路

假设要对集合做多种条件过滤,最原始的写法是每种条件写一个方法:

// 过滤年龄 < 30
public static List<Student> FilterByAge(List<Student> list)
{
    var result = new List<Student>();
    foreach (var item in list)
        if (item.Age < 30) result.Add(item);
    return result;
}

// 过滤名称长度 > 2
public static List<Student> FilterByName(List<Student> list)
{
    var result = new List<Student>();
    foreach (var item in list)
        if (item.Name.Length > 2) result.Add(item);
    return result;
}

这两个方法的结构完全一样,唯一不同的是判断条件。把"不变的逻辑"(循环、添加)留在方法里,把"可变的逻辑"(判断条件)通过委托传入,就得到了 LINQ 的雏形:

public static List<T> CustomWhere<T>(this List<T> source, Func<T, bool> predicate)
{
    var result = new List<T>();
    foreach (var item in source)
    {
        if (predicate.Invoke(item))
            result.Add(item);
    }
    return result;
}

// 使用
var result = studentList.CustomWhere(s => s.Age < 30);

这就是 LINQ 的设计本质:不变的逻辑封装在方法内,可变的逻辑通过委托传递,再用扩展方法提供链式调用,用泛型支持任意类型。

yield 与延迟执行

上面的 CustomWhere 返回 List<T>,意味着调用时就立即执行了所有判断,并把结果全部存入内存。

改用 yield return 配合 IEnumerable<T> 返回值,可以实现延迟执行:

public static IEnumerable<T> CustomWhereIEnumerable<T>(
    this IEnumerable<T> source,
    Func<T, bool> predicate)
{
    foreach (var item in source)
    {
        if (predicate.Invoke(item))
            yield return item; // 符合条件就立即返回,不符合继续往后判断
    }
}

yield return 的效果:

  • 调用方法时不会立即执行方法体
  • 只有在外部遍历(foreach)时,才会一条一条地执行
  • 每次 yield return 后,方法暂停,等待下一次迭代再继续
  • 这种机制叫做状态机,由编译器自动生成
IEnumerable<Student> query = studentList.CustomWhereIEnumerable(s => s.Age < 30);
// 此时方法体尚未执行

foreach (var item in query)
{
    Console.WriteLine(item.Name); // 遍历时才真正执行
}

延迟执行的实际影响

var query = numbers.Where(n => n > 2); // 未执行

numbers.Add(10); // 修改原始数据

foreach (var item in query) // 此时执行,会包含新加的 10
{
    Console.WriteLine(item);
}

立即执行的方法(调用后查询立刻完成,结果固化):

  • ToList(), ToArray(), ToDictionary()
  • Count(), Sum(), Average(), Min(), Max()
  • First(), FirstOrDefault(), Single(), Any()

六、IEnumerable vs IQueryable

对比项IEnumerable<T>IQueryable<T>
命名空间System.Collections.GenericSystem.Linq
执行位置内存(客户端)数据源端(如数据库)
条件参数类型Func<T, bool>Expression<Func<T, bool>>
典型场景LINQ to ObjectsLINQ to SQL / EF
性能特点先把数据全部加载到内存再过滤把条件翻译成 SQL,在数据库端过滤
// IEnumerable:全表加载到内存,再在内存中过滤
IEnumerable<Student> q1 = dbContext.Students
    .AsEnumerable()
    .Where(s => s.Age > 20);

// IQueryable:生成 SQL "WHERE Age > 20",数据库端过滤
IQueryable<Student> q2 = dbContext.Students
    .Where(s => s.Age > 20);

七、LINQ to Everything

LINQ 的设计思想不局限于内存集合,可以扩展到任何数据源:

  • LINQ to Objects:查询内存中的对象集合(IEnumerable<T>)
  • LINQ to SQL:查询数据库,条件被翻译为 SQL 语句
  • LINQ to XML:查询和操作 XML 文档
  • LINQ to Entities:Entity Framework 的查询接口
  • LINQ to Redis / Cache:查询缓存数据源

核心思想都一样:把固定的处理逻辑封装在内部,把可变的查询条件通过委托(或表达式树)传入,整合成统一的查询接口。

2021-05-25 .NET高级班 07-Lambda public class LambdaShow { public delegate void NoReturnNoPara();//无参数无返回值委托 public delegate void NoReturnWithPara(string x, string y);//有参数无返回值委托 public delegate int WithReturnNoPara(); public delegate string WithReturnWithPara(out int x, r 阅读详情

相关推荐

.NET 9 中 LINQ:查询数据的魔法之旅

LINQ是.NET 9中不可或缺的一部分,它将查询的魔力深植于编程语言之中,让数据处理变得既简单又强大。通过掌握LINQ,你就能像魔法师一样,以优雅的方式操控数据,让代码充满魔法与智慧。现在,拿起你的魔杖(键盘),开始在.NET 9的舞台上施展你的LINQ魔法吧!🔮✨。

java专栏 621

.net高级技术——lambda表达式

自C#3.0开始,就可以使用一种新语法把实现代码赋予委托:lambda表达式。只要有委托参数类型的部分,就可以使用Lambda表达式。 下面先来看几个例子 static void Main(string[] args) { Action<int> a1 = delegate (int n) { n = n + 1; };//最原始的写法 ...

qq_33407246的博客 2657

从零开始:YOLOv8车牌检测模型训练全流程实战

本文详细介绍了从零开始训练YOLOv8车牌检测模型的全流程实战指南。内容涵盖环境搭建、数据集收集与标注、模型配置与训练、评估测试及高级优化技巧。通过使用预训练模型和迁移学习,开发者可以高效构建精准的车牌检测系统,并掌握模型导出与部署的关键步骤。

weixin_29061041的博客 335

[.NET/C#] 委托与 Lambda 表达式, 语句详解

在 C# 中, 委托与 Lambda 是最常用的功能之一, 诸如多线程, 可迭代类型的 LINQ 操作, 回调, 都是借助委托完成的. 它在 C 语言对应的是函数指针, 在 C++ 中对应的是 `std::function`

诺尔的博客 1358

Spring AI 实战:5 分钟搭建你的第一个 AI 应用(含完整代码)

能力关键 API适用场景基础对话简单问答、文本生成流式输出实时打字机效果@Bean模型需要调用外部工具结构化输出需要返回 Java 对象多轮对话聊天机器人场景Spring AI 让 Java 开发者可以用最熟悉的方式(Spring Boot + Maven)进入 AI 应用开发领域,不用学新语言,不用换框架,这是它最大的优势。但目前为止,我们的 AI 应用只能回答"它脑子里已有的知识"。如果让它读懂你自己的技术文档、公司内部知识库呢?这就是下一篇文章的主题——RAG(检索增强生成)实战。

qq_17204557的博客 642

OpenCV实战:5分钟搞定图像拼接(附Python代码)

本文通过OpenCV实战教程,详细讲解如何使用Python在5分钟内完成图像拼接。重点介绍`findHomography`函数和单应性矩阵的应用,提供完整的代码示例和常见问题解决方案,帮助开发者快速掌握图像拼接技术。

weixin_26833503的博客 120

(十).netLambda和Linq表达式

Lambda和Linq表达式

lwy 865

若依RuoYi文件上传漏洞实战:如何安全配置/profile/upload路径(附避坑指南)

本文深入解析若依RuoYi框架中/profile/upload路径的文件上传漏洞,提供详细的安全配置方案和实战指南。通过权限修正、输入验证强化和存储隔离等多层防御策略,有效防范未授权访问和恶意文件上传风险,保障系统安全。

pink的博客 776

.NET笔记系列:LAMBDA表达式常用写…

这里主要是将数据库中的常用操作用LAMBDA表达式重新表示了下,用法不多,但相对较常用,等有时间了还会扩展,并将查询语句及LINQ到时也一并重新整理下: 1.select语句:books.Select(p=>new { p.Title, p.UnitPrice, p.Author});//需用匿名方式 2.where语句:books.Where(p=>p.UnitPrice==100&&p.Tit

Mr.Vincent with .NET 954

阿里云大模型ACP认证模拟考试(二)

摘要:在阿里云大模型高级工程师 ACP 认证暂无真题题库的现状下,模拟题具有重要意义。其依照真实考试设计,涵盖题型、题量与分值分布,助力考生熟悉考试框架,合理分配答题时间精力,提升效率。同时,模拟题紧扣官方课程,能帮助考生精准定位知识盲点,如大模型架构原理和通义千问应用实践等重点内容,以便针对性完善知识体系。此外,通过练习模拟题,考生可掌握不同题型的解题思维与技巧,快速洞察复杂题目本质,提高答题准确率。

davidkorhenn的博客 2069

【经验&技巧】如何在ADS中导入示波器的时域波形?

从示波器中导出的时域波形数据,需要按照固定的格式进行修改,并通过Display dataset对话框导入ADS。

一只豌豆象的博客 1571

.NET学习笔记(五)----Lambda、Linq、匿名类(var)、扩展方法

一、Lambda前世今生 Lambda的表达式:()=>{} Lambda的本质:一个匿名函数,在底层,会在一个尖括号(<>)类中,生成带有名称的方法。 二、匿名类

qq_37739112的博客 761

Python 爬虫实战:医学临床指南爬取(含 PDF 解析与结构化存储)

在当今数字化时代,医学临床指南作为医生进行科学诊疗的重要参考资料,包含大量经过临床验证的诊疗路径和建议。这些指南通常发布在权威医学网站上,数量庞大且更新频繁。如何自动化地抓取并存储这些指南,成为医疗数据分析、医学研究以及医疗 AI 模型训练等工作的关键第一步。本文将系统讲解如何利用 Python 爬虫技术从权威医学网站爬取临床指南内容,并实现 PDF 解析与结构化存储。

u014481728的博客 449

第2章 LINQ C#之LINQ .Net CoreLINQ C#Lambda表达式

本文记录了C#中的LINQ Lambda 匿名方法等相关知识及实际编码应用。

weixin_46540714的博客 1044

《派出你的AI同事:WorkBuddy案例实战》055:客服工单自动分类与话术生成

客服工单自动分类与话术生成 —— 《派出你的AI同事:WorkBuddy案例实战》系列文章,配图已统一为规范示意图。

LuckyHanMo的博客 281

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

20251208_初识LINQ_Study05

定义了一个匿名类型,包括BianHao,XinMing,GongZi三个属性并且将employee的三个属性一一映射到这个匿名类型上。通过使用这四种方法,进行防御性编程,可以将数据中存在的问题暴露出来,从而尽早地修复问题。由此可见,使用Lambda表达式可以减少代码体量,但是代码可读性也会变差。LINQ提供了很多集合的扩展方法,配合Lambda表达式可以简化数据处理。关键字实现类型自动推导。

m0_74473517的博客 1142

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

.NET进阶篇05-Linq、Lambda表达式

知识需要不断积累、总结和沉淀,思考和写作是成长的催化剂 文章目录一、Lambda表达式1、匿名方法2、Lambda表达式二、Linq概述三、查询操作符1、linq初见排序分组连接合并分页聚合转换四、并行Linq五、表达式树1、认识表达式目录树2、拼装表达式树3、应用六、小结 一、Lambda表达式 1、匿名方法 使用delegate的时候很多时候没必要使用一个普通方法,因为这个方法只有**de...

u012818292的博客 421

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

C#语言基础速成Day05

C#(发音为“C Sharp”)是一种现代的、面向对象的编程语言,由微软开发,作为其 .NET 框架的一部分。面向对象编程(OOP)是一种编程范式,基于“对象”和“类”的概念,旨在提高代码的可重用性、可扩展性和可维护性。C#的面向对象是及其重要的部分,只有掌握了面向对象的思想才算掌握了精髓部分。同时,C#提供了丰富的特性和功能,使开发者能够编写高效、可维护的代码。通过理解和应用这些特性,我们可以大大提高自己的编程能力和项目的质量。

2301_79175212的博客 1094

C#知识的扩展(EF、Linq、lambda、MVC、jquery)

一.EF相关技术文档:/ http://www.entityframeworktutorial.net/ 点击打开链接 https://msdn.microsoft.com/en-us/library/aa937723(v=vs.113).aspx MVC经典: 张子阳mvc:http://www.tracefact.net ASP.NETMVC:https://www.asp...

代码改变生活 849

05-C#

这两个方法的结构完全一样,唯一不同的是判断条件。核心思想都一样:把固定的处理逻辑封装在内部,把可变的查询条件通过委托(或表达式树)传入,整合成统一的查询接口。扩展方法允许在不修改原类型源码的情况下为其添加新方法,是 LINQ 的实现基础,也是遵循开闭原则的常用手段。Lambda 表达式是委托的语法糖,本质上是匿名方法的简化写法,编译器最终会将其编译为委托实例。,意味着调用时就立即执行了所有判断,并把结果全部存入内存。缺点:方法必须单独定义,代码分散,不够内聚。操作符(读作 "goes to")替代。

噢~ 236

【C#基础 /.net core 高频面试题】

前阵子人事安排了好几个人过来技术面,每次都要花费不少时间结合面试者简历筛选面试题,特此博主花了点时间整理了.net C# 比较基础且重要的技术栈面试题。非常适合应届毕业生或者想转行.net的同学,想拿高薪的行业大牛可以直接跳过。ps:以下部分问题的回答可能相对概述,是结合我公司实际想考察的重点来写的,如果觉得不够详细可以自行百度。

素弦的博客 1071

(转)Linq学习笔记

写在前面 最近在看Linq,在博客园看到这篇文章,写的通俗易懂,转来和大家一起做个分享。原文地址http://www.cnblogs.com/goscan/archive/2011/05/05/Linq_study_log.html 什么是Linq LINQ是Language Integrated Query的简称,它是集成在.NET编程语言中的一种特性。已成为编程语言的一个组成部...

diaoshanwai2368的博客 108

C# 中Linq探讨 Or条件拼接

然而,如果你需要根据不同的条件动态地构建查询(例如,用户可能只输入了名字,或者只输入了年龄范围,或者两者都输入了),那么 PredicateBuilder 就变得非常有用。PredicateBuilder 的主要用途是构建复杂的逻辑表达式,特别是当你需要动态地添加条件时(例如,在运行时根据用户的输入构建查询)。虽然没有一个特定的NuGet包专门用于拼接LINQ的OR条件,但你可以使用PredicateBuilder这样的模式或直接在查询中编写条件来达成目的。方法一:使用PredicateBuilder。

weixin_43542114的博客 1147

C#版本与.NET版本对应关系

C#版本 .NET版本 发布日期 特性 C# 1.0 .NET Framework 1.0 2002-02-13 委托、事件 C# 1.1 .NET Framework 1.1 2003-04-24 APM(异步编程模型) C# 2.0 .NET Framework 2.0 2005-11-07 泛型、匿名方法、迭代器、可空类型 C# 3.0 .NET Framework 3.0 2007-11-06

songjuntao8的专栏 514

C#版本与.NET版本对应关系以及各版本的特性

C#版本 .NET版本 发布日期 特性 C# 1.0 .NET Framework 1.0 2002-02-13 委托、事件 C# 1.1 .NET Framework 1.1 2003-04-24 APM(异步编程模型) C# 2.0 .NET Framework 2.0 2005-11-07 泛型、匿名方法、迭代器、可空类型 C# 3.0 .NET Framework 3.0 2007-11-06

icesker 900

C#.NET的Linq查询、lambda、委托:Func<>和Action

  这两天在家休息,就研究了下现在比较流行的Linq查询,以及lambda表达式和Func委托与Action委托,做了一些基础的测试,下面贴出代码,如有错误疏忽之处,请指出。 代码 using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System....

weixin_30409849的博客 212
上一篇: 04-C#.Net-委托和事件-面试题
下一篇: 06-C#.Net-表达式目录树-学习笔记
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值