一篇文章厘清C#中的lambda表达式

server/2024/9/20 13:36:40/ 标签: c#

一篇文章厘清C#中的lambda表达式

链接: 源码

说C#的匿名函数,就要先说一下匿名函数.

Lambda表达式

  • 1 lambda表达式演变史
    • 1. **C# 1.0 (2002)**
    • 2. **C# 2.0 (2005)**
    • 3. **C# 3.0 (2007)**
    • 4. **C# 4.0及以后**
  • 2 lambda表达式使用方法
    • 1 **基本语法**
    • 2 **使用场景和示例**
      • **作为参数**
      • **匿名委托**
      • **LINQ 查询表达式**
      • **事件处理**
      • **并行编程与任务调度**
      • **延迟执行与 Lazy<T>**
    • 3 **注意事项**
  • 3 系统自带的两种委托:Action和Func
    • **Action**
      • **用法示例**
      • **应用场景**
    • **Func**
      • **用法示例**
      • **应用场景**
  • 4 综合案列

1 lambda表达式演变史

C# 匿名函数的演变历史可以追溯到 C# 语言的不同版本,随着语言特性的不断丰富和发展,匿名函数经历了以下几个主要阶段:

1. C# 1.0 (2002)

在 C# 1.0 中,虽然还没有直接支持匿名函数的概念,但已经引入了委托(Delegate)这一关键概念。委托允许将方法作为参数传递或存储为变量,为后续匿名函数的引入奠定了基础。在这个版本中,若要创建委托实例,必须先定义一个具有匹配签名的方法,然后使用该方法的名称来初始化委托。

例如:

public delegate int MyDelegate(int x, int y);public static int AddNumbers(int a, int b)
{return a + b;
}MyDelegate add = new MyDelegate(AddNumbers);

2. C# 2.0 (2005)

C# 2.0 引入了匿名方法,这是对匿名函数功能的初步实现。匿名方法允许开发者在需要委托的地方直接编写一段代码块(内联),而无需事先定义一个命名方法。这种语法简化了在特定上下文中临时创建和使用简单功能的过程,特别是在事件处理和回调场景中。

匿名方法的语法如下:

MyDelegate add = delegate(int a, int b)
{return a + b;
};

3. C# 3.0 (2007)

C# 3.0 引入了更强大的匿名函数形式——Lambda 表达式。Lambda 表达式进一步简化了匿名方法的语法,使其更加简洁且易于阅读。Lambda 表达式可以直接表示输入参数、箭头符号(=>)以及要执行的表达式或语句块。它们在LINQ(Language Integrated Query)中扮演了核心角色,极大地增强了C#的函数式编程能力。

Lambda 表达式的语法示例:

// 单行表达式形式
MyDelegate add = (int a, int b) => a + b;// 多行语句块形式
MyDelegate process = (int a, int b) =>
{int result = a * b;Console.WriteLine("Processing numbers...");return result;
};

4. C# 4.0及以后

随着Lambda表达式的普及和广泛使用,匿名方法在新项目中的使用逐渐减少,Lambda表达式成为编写匿名函数的首选方式。后续版本的C#(如4.0、5.0、6.0、7.0、8.0、9.0、10.0等)继续强化和扩展了Lambda表达式的能力,包括:

  • 类型推断:Lambda表达式中的参数类型可以根据上下文自动推断,进一步减少了代码冗余。
  • 可变数量参数:Lambda表达式支持可变数量参数,方便处理不定长度的参数列表。
  • async/await支持:C# 支持异步Lambda表达式,便于编写非阻塞的异步代码。
  • Expression-bodied members:C# 6.0引入了表达式体成员语法,使得Lambda风格的简短表达式可以用于方法、属性、构造函数等更多场景。
  • Local functions(局部函数):虽然不是匿名函数,但C# 7.0引入的局部函数提供了另一种在方法内部定义私有、嵌套函数的方式,有时可以作为匿名函数的替代方案,尤其是在需要复用或避免闭包副作用的情况下。

综上所述,C# 匿名函数的演变历史始于C# 2.0的匿名方法,经由C# 3.0的Lambda表达式实现了重大飞跃,并在后续版本中持续得到增强和完善,成为现代C#编程中不可或缺的一部分。尽管匿名方法在早期版本中有其作用,但在当前实践中,Lambda表达式已成为编写匿名函数的标准方式。

2 lambda表达式使用方法

C# Lambda 表达式是一种简洁、灵活的匿名函数表示形式,广泛应用于各种编程场景,如 LINQ 查询、事件处理、委托实例化、高阶函数应用等。以下是使用 C# Lambda 表达式的一些基本方法和常见用例:

1 基本语法

(input-parameters) => { <sequence-of-statements> }

Lambda 表达式的语法由三部分组成:

  1. 参数列表:位于圆括号 () 内,可以为空(表示无参数)、包含一个或多个参数,参数类型可以显式声明或根据上下文推断。

    • 显式类型:(int x, string y)
    • 类型推断:(x, y) —— 当Lambda表达式赋值给已知委托类型或在编译器可以确定类型的情境下,可以省略参数类型。
  2. 箭头操作符 =>:将参数列表与表达式或语句块分隔开。

  3. 表达式或语句块:表示Lambda表达式的行为。

    • 单行表达式:直接返回表达式的计算结果,不需要使用 return 关键字。

      int[] numbers = { 1, 2, 3 };
      var evenNumbers = numbers.Where(n => n % 2 == 0);
      
    • 多行语句块:使用花括号 {} 包围,如果需要执行多条语句或需要显式 return 语句,则使用语句块形式。

      Func<int, int> squareAndLog = number =>
      {int squared = number * number;Console.WriteLine($"Squared: {squared}");return squared;
      };
      

2 使用场景和示例

作为参数

Lambda 表达式常被用作需要函数作为参数的方法或操作符的参数,如 LINQ 方法、事件处理器、ActionFunc 委托实例化等。

// LINQ 查询中的 Where 方法
var filteredItems = items.Where(item => item.IsAvailable && item.Price > 100);// 使用 Action 委托
Action<string> logMessage = message => Console.WriteLine($"Logging: {message}");
logMessage("Hello, Lambda!");// 使用 Func 委托
Func<int, int, int> add = (a, b) => a + b;
int sum = add(3, 5);

匿名委托

Lambda 表达式可以替代传统的匿名方法,创建不需显式定义的、临时使用的委托实例。

// 传统匿名方法
button.Click += delegate (object sender, EventArgs e)
{MessageBox.Show("Button clicked!");
};// Lambda 表达式形式
button.Click += (sender, e) => MessageBox.Show("Button clicked!");

LINQ 查询表达式

Lambda 表达式是 LINQ 查询语法的核心组成部分,用于定义筛选、投影、聚合等操作。

var customers = context.Customers.Where(c => c.Country == "USA").Select(c => new { Name = c.Name, TotalPurchases = c.Purchases.Sum(p => p.Amount) }).OrderByDescending(x => x.TotalPurchases);

事件处理

Lambda 表达式简化了事件处理器的注册,尤其在需要访问外部变量时,可以利用闭包特性。

string greeting = "Welcome!";
button.Click += (sender, e) => MessageBox.Show(greeting);

并行编程与任务调度

Lambda 表达式在 Task.RunParallel.ForEach 等并行编程场景中非常有用。

// 创建并启动一个后台任务
Task.Run(() =>
{DoSomeWork();UpdateUI();
});// 并行处理集合
Parallel.ForEach(numbers, number =>
{if (IsPrime(number))primes.Add(number);
});

延迟执行与 Lazy

Lambda 表达式用于初始化 Lazy<T> 对象,确保资源仅在首次访问时才进行计算或加载。

Lazy<int> expensiveComputation = new Lazy<int>(() => ComputeExpensiveValue());
int result = expensiveComputation.Value; // 计算仅在此处发生

3 注意事项

  • 类型推断:Lambda 表达式的参数类型和返回类型通常可以由编译器推断,无需显式声明。但在某些情况下,可能需要显式提供类型信息以消除歧义。

  • 闭包:Lambda 表达式可以捕获其封闭作用域内的变量,形成闭包。理解闭包行为对于避免潜在的并发问题和资源管理问题至关重要。

  • 性能:Lambda 表达式通常编译为高效代码,但在某些情况下(如大型循环中的复杂Lambda表达式),可能会导致编译器生成额外的类和方法,影响性能。适当优化或使用局部函数替代可能有助于提升效率。

总之,C# Lambda 表达式提供了简洁、直观的方式来编写匿名函数,极大地提高了代码的可读性和可维护性,尤其在处理函数式编程、事件处理、委托、LINQ 查询等方面发挥着重要作用。理解和熟练运用Lambda表达式是现代C#开发中的重要技能。

3 系统自带的两种委托:Action和Func

C# 中的 ActionFunc 是预定义的泛型委托类型,它们简化了委托的使用,避免了手动声明相似用途的自定义委托。今后我们使用时,没有必要自定义委托了,全部使用系统自带的委托就可以了,方便省事.

下面分别介绍 ActionFunc 的用法:

Action

Action 代表一个无返回值的方法,只用于封装需要执行的操作。根据需要传递的参数数量,C# 提供了一系列预定义的 Action 类型,从 Action(无参数)到 Action<typeparamref name="T1">, ..., T16</typeparamref></typeparamref></typeparamref>(最多16个参数)。

用法示例

  • 无参数

    Action noParamAction = () => Console.WriteLine("No parameter action called.");
    noParamAction(); // 输出 "No parameter action called."
    
  • 带参数

    Action<string, int> paramAction = (message, count) => Console.WriteLine($"{message}, count: {count}");
    paramAction("Action with parameters", 5); // 输出 "Action with parameters, count: 5"
    

应用场景

  • 事件处理:作为事件处理器,执行某种操作,无需返回值。

    button.Click += (sender, e) => Console.WriteLine("Button clicked!");
    
  • 回调:传递给异步操作或其他方法作为完成时的回调函数。

    Task.Run(() => LongRunningOperation()).ContinueWith(_ => Console.WriteLine("Long running operation completed."));
    

Func

Func 代表一个有返回值的方法,除了封装操作外,还返回一个指定类型的值。Func 类型同样有一系列预定义版本,格式为 Func<typeparamref name="T1">, ..., Tn</typeparamref>, TResult>,其中 T1Tn 代表输入参数类型,TResult 代表返回值类型。

用法示例

  • 无参数,返回整数

    Func<int> noParamFunc = () => DateTime.Now.Second;
    int currentSecond = noParamFunc(); // 获取当前秒数
    
  • 带参数,返回字符串

    Func<int, string, string> paramFunc = (num, text) => $"{text} {num}";
    string combined = paramFunc(42, "The answer is"); // 结果为 "The answer is 42"
    

应用场景

  • LINQ 查询:作为查询表达式中的选择器(Select)、谓词(Where)等方法的参数。

    List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
    var squares = numbers.Select(n => n * n); // 返回平方数的集合
    
  • 工厂方法:传递给需要动态创建对象的函数。

    Func<string, MyClass> factory = name => new MyClass { Name = name };
    MyClass instance = factory("Instance created by Func");
    
  • 计算或转换:在需要简单计算或数据转换的场景中作为参数传递。

    double average = CalculateAverage(scores, score => score / 10.0); // 转换分数为小数
    

总结来说,Action 用于封装无需返回值的操作,常用于事件处理、回调等场景;而 Func 用于封装带有返回值的方法,适用于需要计算、转换或作为函数参数返回结果的情况。两者均通过泛型参数来适应不同数量和类型的参数需求,大大简化了委托的使用。

4 综合案列

c#">{    //.netframework 1.0MyDelgate myDelgate = new MyDelgate(MyFunc);myDelgate("keson");void MyFunc(string s){Console.WriteLine($"hello,{s}");}
}{    //.netframework 2.0MyDelgate myDelgate = new MyDelgate(delegate (string s){Console.WriteLine($"hello,{s}");});myDelgate("keson");
}{    //.netframework 3.0MyDelgate myDelgate = new MyDelgate((string s) =>{Console.WriteLine($"hello,{s}");});myDelgate("keson");
}{//省略参数关键字MyDelgate2 myDelgate = new MyDelgate2((s, n) =>{Console.WriteLine($"hello,{s},I am {n} years old");});myDelgate("keson", 18);
}{ //省略方法体的大括号MyDelgate2 myDelgate = new MyDelgate2((s, n) =>Console.WriteLine($"hello,{s},I am {n} years old"));myDelgate("keson", 18);
}{//匿名类object ImplicitClass = new{Id = 1,Name = "jack"};Console.WriteLine(ImplicitClass.GetType().Name);//C#是强类型语言,编译时会确定类型,ImplicitClass编译时是object类型,//object类本身没有Id属性,所以无法访问//Console.WriteLine(ImplicitClass.Id);//dynamic避开编译器检查dynamic ImplicitClass2 = new{Id = 99,Name = "keson"};Console.WriteLine(ImplicitClass2.Id);Console.WriteLine(ImplicitClass2.GetType().Name);// ImplicitClass2.瞎写();//编译时不检查,但是运行时会抛出异常.var ImplicitClass3 = new{Id = 89,Name = "keson",Age = 18};Console.WriteLine(ImplicitClass3.Id);Console.WriteLine(ImplicitClass3.GetType().Name);//ImplicitClass3.Id = 1; //匿名类是只读类型,只能初始化是设置
}{//使用系统自带的委托action和funcAction action = () => { Console.WriteLine("无参"); }; //无入参Action<string> action2 = s => { Console.WriteLine($"有参: {s}"); };//有入参Func<string> func = () => { return "keson"; };//无入参,有返回值Func<int,int> func2 = i => { return i * i; };//有入参,有返回值Func<int, int> func3 = i =>  i + i;//当方法体只有一行,关键字return可以省略,大括号可以省略Console.WriteLine(func3(3));
}public delegate void MyDelgate(string s);
public delegate void MyDelgate2(string s, int num);

http://www.ppmy.cn/server/2796.html

相关文章

计算机网络——应用层(3)电子邮件

电子邮件 1、概述&#xff1a; 电子邮件是使用电子设备交换的邮件及其方法。 优点&#xff1a;使用方便&#xff0c;传递迅速&#xff0c;费用低廉&#xff0c;可传送多种信息 重要标准&#xff1a; 简单邮件发送协议&#xff1a;SMTP互联网文本报文格式通用互联网邮件扩充…

【python实战】-- 按指定字符生成密码字典并测试打开word

系列文章目录 文章目录 系列文章目录前言一、生成密码字典1.python程序 二、测试打开Word1.python程序 总结 前言 一、生成密码字典 1.python程序 代码如下&#xff08;示例&#xff09;&#xff1a; import string# 定义密码字符集&#xff0c;这里以小写字母为例 #all_char…

pytorch环境配置踩坑记录

一、问题1 1.执行命令 conda create -n pytorch python3.62.报错如下 Solving environment: failedCondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/msys2/noarch/repodata.json.bz2> Elapsed: -An HTTP error occurred when tr…

可视化ETL解决方案:Apache NiFi、DataX(加上DataX-Web)、Kettle这3个解决方案对比

1.Apache NiFi&#xff1a; Apache NiFi是一个易于使用、功能强大的可视化ETL工具&#xff0c;它提供了一套直观的图形界面&#xff0c;让用户可以轻松地设计、管理和监控数据流。NiFi支持多种数据源和目标系统&#xff0c;具有强大的数据处理能力&#xff0c;如数据过滤、转换…

Idea与Maven版本不一致问题

Idea拉取Jar包&#xff0c;报Unable to import maven project: See logs for details 查看日志信息No implementation for org.apache.maven.model.path.PathTranslator was bound 具体错误详情&#xff1a; IDEA执行Maven报错 Unable to import maven project: See logs f…

使用Docker搭建一主二从的redis集群

文章目录 一、根据基础镜像构建三个docker容器二、构建master机三、配置slave机四、测试 本文使用 主机指代 物理机、 master机指代“一主二从”中的 一主&#xff0c; slave机指代“一主二从”中的 二从 一、根据基础镜像构建三个docker容器 根据本文第一章&#xff08…

MIMO(多天线)通信的四种译码算法

目录 一. 介绍 二. 极大似然译码 三. 破零译码算法 四. 最小均方误差算法 五. 球形译码 一. 介绍 发射天线数记为Mt&#xff0c;接收天线数记为Mr。由此发射信号x为向量&#xff1a; 接受信号y为向量&#xff1a; 信道H为矩阵&#xff1a; 利用n代表噪声向量&#xff0c;…

【Web】2022DASCTF Apr X FATE 防疫挑战赛 题解(全)

目录 warmup-php soeasy_php warmup-java warmup-php spl_autoload_register函数实现了当程序遇到调用没有定义过的函数时&#xff0c;会去找./class/函数名.php路径下的php文件&#xff0c;并把它包含在程序中。 拿到附件拖进Seay里自动审计一下 显然利用终点为evaluateExp…

探索 IntelliJ IDEA 2024.1最新变化:全面升级助力编码效率

探索 IntelliJ IDEA 2024.1最新变化&#xff1a;全面升级助力编码效率 文章目录 探索 IntelliJ IDEA 2024.1最新变化&#xff1a;全面升级助力编码效率摘要引言 IntelliJ IDEA 2024.1 最新变化关键亮点全行代码补全 Ultimate对 Java 22 功能的支持新终端 Beta编辑器中的粘性行 …

用html写文本变形动画

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>文本变形动画</title><link rel"stylesheet" href"./style.css"> </head> <body> <!-- 两个文本部分…

oracle 19c数据库W00n进程使用很多PGA内存资源的分析

今天&#xff0c;客户反馈测试环境的数据库PGA资源不足&#xff0c;报错ORA-04036: 实例使用的 PGA 内存超出 PGA_AGGREGATE_LIMIT&#xff1b;分析是多个W00n进程使用大量PGA-触发了BUG&#xff0c;对应解决办法就是打补丁。&#xff08;民间办法就是KILL进程、重启数据库&…

微服务架构中的业务解耦设计

目录 业务解耦的原则 单一责任原则 松耦合原则 业务解耦的实现方法 业务解耦的优势 微服务架构中的业务解耦设计案例 背景 业务解耦设计 服务拆分 商品服务 订单服务 用户服务 支付服务 物流服务 通信机制 数据一致性 具体实现 商品服务 订单服务 用户服务…

锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测 Matlab基于GRU的锂电池剩余寿命预测 基于GRU的锂电池剩余寿命预测&#xff08;单变量&#xff09; 运行环境Matlab2020及以上 锂电池的剩余寿命预测是…

SVN修改已提交版本的注释

目录 一、需求分析 二、问题分析 三、解决办法 一、需求分析 ​开发过程中&#xff0c;在SVN提交文件后&#xff0c;发现注释写的不完整或不够明确&#xff0c;想再修改之前的注释文字​。 使用环境&#xff1a; SVN服务器操作系统&#xff1a;Ubuntu 20.04.6 LTS SVN版本&…

MongoDB的go SDK使用集锦

在上一章解读MongoDB官方文档获取mongo7.0版本的安装步骤与基本使用介绍了如何使用mongo shell操作mongo数据库&#xff0c;接下来介绍如何使用sdk来操作数据库&#xff0c;这里以go语言为例&#xff0c;其他语言请查看源文档mongo docs Quick Start 内置数据结构 MongoDB是存…

Java 变得越来越像 Rust?

随着编程技术的增强和复杂性的提升&#xff0c;许多编程语言也纷纷效仿&#xff0c;Java 也不例外。 另一边&#xff0c;尽管社区内部问题重重&#xff0c;但 Rust 仍逐年获得开发人员的喜爱。这背后都是有原因的&#xff1a;Rust 的编译器让开发人员避免了各种问题。编译器对…

【MIT6.824】lab2C-persistence, lab2D-log compaction 实现笔记

引言 lab2C的实验要求如下 Complete the functions persist() and readPersist() in raft.go by adding code to save and restore persistent state. You will need to encode (or “serialize”) the state as an array of bytes in order to pass it to the Persister. Us…

设计模式系列:适配器模式

简介 适配器模式&#xff08;Adapter Pattern&#xff09;又称为变压器模式&#xff0c;它是一种结构型设计模式。适配器模式的目的是将一个类的接口转换成客户端所期望的另一种接口&#xff0c;从而使原本因接口不匹配而不能一起工作的两个类能够一起工作。 适配器模式有两种…

论软件系统的架构风格,使用三段论 写一篇系统架构师论文

软件系统的架构风格是指在软件系统设计与开发过程中&#xff0c;采用的一组相互协调的设计原则、模式和实践。这些风格不仅影响着系统的技术实现&#xff0c;还关乎到系统的可维护性、可扩展性和可靠性等关键质量属性。通过三段论的结构&#xff0c;本文旨在探讨软件系统架构风…

数据结构之单链表的相关知识点及应用

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构 目录 链表的概念及结构 链表与顺序表的区别与优劣势 链表的分类 单链表的实现 单链表中增加节点 单链表中尾插数据 打印单链…