C#lambda表达式和匿名方法
一、lambda表达式介绍
无参数 () =>{return "1";}; 等同于 string getnum(){ return "1"; } 有两个参数 (p1, p2) =>{ return p1*p2;}; 等同于 int mul(p1, p2) { return p1*p2;};
lambda表达式可以捕获外部变量,并在其主体中使用它们。这些变量被称为捕获变量,它们会在lambda表达式创建时被复制,并在lambda表达式执行时被更新。例如:
int factor = 2; Func<int, int> multiplier = n => n * factor; //==》 当结构体只有一句的时候可以省略 return ,多条语句不可省略 Console.WriteLine(multiplier(3)); //输出6 factor = 3; Console.WriteLine(multiplier(3)); //输出9
二、lambda演进过程 //声明委托类型 public delegate void SayHandler(string name, int age); //原始形态 SayHandler handler1=new SayHandler( delegate(string name, int age){ Console.WriteLine("你好"); } ); //进化1 直接去掉:new SayHandler( ) SayHandler handler2=delegate(string name, int age) { Console.WriteLine("你好"); } //进化2 delegate去掉后,换上了一个符号“=>”,读作:goes to SayHandler handler3=(string name, int age)=>{ Console.WriteLine("你好"); } //进化3 因为与委托类型是一一对应关系,所以直接去掉参数类型名称 SayHandler handler4=(name, age)=>{ Console.WriteLine("你好"); } //进化4 匿名方法只有一行代码,则花括号{} 可省略 //注:若参数列表中只有一个参数,则圆括号() 也可省略 SayHandler handler5=(name, age)=> Console.WriteLine("你好");
三、lambda表达式的5中高频使用方法
第一种 使用lambda表达式作为委托或事件处理程序
1.1 委托是一种引用类型,它可以封装一个具有特定参数和返回值类型的方法。委托可以用来实现回调函数、事件处理程序、匿名方法等功能。lambda表达式可以直接表示为委托的代码,而不需要单独定义一个方法或一个委托类型。这样可以简化委托的创建和使用,提高代码的可读性和灵活性。
//定义一个委托类型
delegate int CalculateHandler(int x, int y); //创建一个lambda表达式,并赋值给一个委托变量 CalculateHandler sumHandler = (x, y) => x + y; //调用委托变量,执行lambda表达式的主体 Console.WriteLine(sumHandler(1, 2)); //输出3//使用系统预定义的泛型委托类型 Func<int, int, int> multiplyHandler = (x, y) => x * y; //调用委托变量,执行lambda表达式的主体 Console.WriteLine(multiplyHandler(2, 3)); //输出6//直接将lambda表达式作为参数传递给其他方法 List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 }; numbers.ForEach(n => Console.WriteLine(n * n)); //输出1 4 9 16 25
1.2 事件是一种特殊的委托,它可以在某个对象发生特定行为时通知其他对象。事件处理程序是一种特殊的方法,它可以在事件被触发时执行一些操作。lambda表达式可以直接表示为事件处理程序的代码,而不需要单独定义一个方法。这样可以简化事件处理程序的创建和使用,提高代码的可读性和灵活性。
//定义一个按钮类
class Button { //定义一个点击事件
public event EventHandler Click; //定义一个触发点击事件的方法
public void OnClick() { //如果有订阅了该事件的处理程序,则调用它们 Click?.Invoke(this, EventArgs.Empty); } } //创建一个按钮对象 Button button = new Button(); //创建一个lambda表达式,并添加到点击事件的订阅列表中 button.Click += (sender, e) => { Console.WriteLine("Button clicked"); }; //触发点击事件,执行lambda表达式的主体 button.OnClick(); //输出Button clicked
第二种:使用lambda表达式作为LINQ查询的谓词或选择器
LINQ是一种语言集成查询,它可以用来对各种数据源进行查询、筛选、排序、分组等操作。LINQ查询可以使用查询表达式或方法语法来编写,其中方法语法可以使用lambda表达式来指定查询的条件或投影。lambda表达式可以直接表示为谓词或选择器的代码,而不需要单独定义一个方法。这样可以简化LINQ查询的创建和使用,提高代码的可读性和灵活性。
//定义一个数据源 List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 }; //创建一个lambda表达式,并作为参数传递给Where方法,筛选出偶数
var evenNumbers = numbers.Where(n => n % 2 == 0); //遍历LINQ查询结果,执行lambda表达式的主体
foreach (var n in evenNumbers) { Console.WriteLine(n); //输出2 4 } //创建一个lambda表达式,并作为参数传递给Select方法,将每个数乘以10
var multipliedNumbers = numbers.Select(n => n * 10); //遍历LINQ查询结果,执行lambda表达式的主体
foreach (var n in multipliedNumbers) { Console.WriteLine(n); //输出10 20 30 40 50 } //创建一个lambda表达式,并作为参数传递给OrderBy方法,按照数值大小排序
var sortedNumbers = numbers.OrderBy(n => n); //遍历LINQ查询结果,执行lambda表达式的主体foreach (var n in sortedNumbers) { Console.WriteLine(n); //输出1 2 3 4 5 }
第三种:使用lambda表达式作为Task.Run或Task.Factory.StartNew的参数
Task是一种表示异步操作的对象,它可以用来实现并行编程、异步编程、多线程编程等功能。Task可以使用Task.Run或Task.Factory.StartNew方法来创建和启动,这些方法可以接受一个委托作为参数,表示要在任务中执行的代码。lambda表达式可以直接表示为委托的代码,而不需要单独定义一个方法。这样可以简化Task的创建和使用,提高代码的可读性和灵活性。
//创建一个lambda表达式,并作为参数传递给Task.Run方法,创建并启动一个任务
var task = Task.Run(() => { //在任务中执行一些耗时的操作 Thread.Sleep(1000); //返回一个结果 return 42; }); //等待任务完成,获取任务的结果 int result = task.Result; Console.WriteLine(result); //输出42 //创建一个lambda表达式,并作为参数传递给Task.Factory.StartNew方法,创建并启动一个任务 var task2 = Task.Factory.StartNew(() => { //在任务中执行一些耗时的操作 Thread.Sleep(1000); //抛出一个异常 throw new Exception("Something went wrong"); }); //等待任务完成,获取任务的异常 try { task2.Wait(); } catch (AggregateException ae) { foreach (var e in ae.InnerExceptions) { Console.WriteLine(e.Message); //输出Something went wrong } }
第四种 使用lambda表达式作为表达式树
表达式树是一种表示代码结构的数据结构,它可以用来实现动态编译、元编程、LINQ提供程序等功能。表达式树可以使用Expression<TDelegate>类型来创建和表示,这是一种特殊的委托类型,它可以接受一个lambda表达式作为参数,表示要在表达式树中表示的代码。lambda表达式可以直接表示为表达式树的代码,而不需要单独定义一个方法。这样可以简化表达式树的创建和使用,提高代码的可读性和灵活性。
//创建一个lambda表达式,并作为参数传递给Expression<Func<int, int, int>>类型的构造函数,创建一个表达式树对象 Expression<Func<int, int, int>> expression = (x, y) => x + y; //遍历表达式树对象,获取表达式树的结构 Console.WriteLine(expression); //输出(x, y) => (x + y) Console.WriteLine(expression.Body); //输出(x + y) Console.WriteLine(expression.Parameters[0]); //输出x Console.WriteLine(expression.Parameters[1]); //输出y//转换表达式树对象,编译为委托 Func<int, int, int> func = expression.Compile(); //调用委托,执行lambda表达式的主体 Console.WriteLine(func(1, 2)); //输出3
第五种 使用lambda表达式作为异步方法
异步方法是一种使用async和await关键字来实现异步编程的方法,它可以用来实现非阻塞的UI、并发的网络请求、异步的IO操作等功能。异步方法可以使用Task或Task<T>类型来表示异步操作的结果,它们可以接受一个lambda表达式作为参数,表示要在异步方法中执行的代码。lambda表达式可以直接表示为异步方法的代码,而不需要单独定义一个方法。这样可以简化异步方法的创建和使用,提高代码的可读性和灵活性。
关键字来标记异步处理的代码,并使用await关键字来等待异步操作的完成。 - 将lambda表达式作为参数传递给Task或Task<T>类型的方法,如Task.Run或Task.FromResult,创建并启动一个异步方法。 - 等待异步方法完成,获取异步方法的结果或异常。 例如,下面的代码演示了如何使用lambda表达式作为异步方法: //创建一个lambda表达式,并作为参数传递给Task.Run方法,创建并启动一个异步方法
var task = Task.Run(async () => { //在lambda表达式中使用async和await关键字来标记异步处理的代码await Task.Delay(1000); //等待1秒return42; //返回一个结果 }); //等待异步方法完成,获取异步方法的结果
int result = await task; Console.WriteLine(result); //输出42//创建一个lambda表达式,并作为参数传递给Task.FromResult方法,创建并启动一个异步方法
var task2 = Task.FromResult(async () => { //在lambda表达式中使用async和await关键字来标记异步处理的代码await Task.Delay(1000); //等待1秒thrownew Exception("Something went wrong"); //抛出一个异常 }); //等待异步方法完成,获取异步方法的异常try { await task2; } catch (Exception e) { Console.WriteLine(e.Message); //输出Something went wrong }
二、匿名方法 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法。 而 C# 2.0 -- 引进了匿名方法,在 ≥ C# 3.0 的版本中,我们会用 Lambda 表达式进行取代匿名方法,并且用 Lambda 表达式作为编写内联代码的首选方式,因为它更简洁。 匿名方法是,顾名思义,匿名方法就是没有名称的方法。匿名方法最明显的好处就是:可以降低额外另写一个方法的工作量;另外一个好处就是可以直接访问调用者的变量,从而降低传参的复杂度。 匿名方法,它不是一个事先定义的方法,而是使用一个委托的代码块,在使用时,和普通方法并没有什么区别,但是匿名方法可以在一定程度上减少系统开销。 匿名方法,关键字:delegate。 匿名方法,使用场景:通常在,①需要一个临时的方法,并且该方法使用的次数极少;②该方法的代码很短,不长。
匿名方法的参数使用范围:匿名方法块。 如果目标在块外部,你在匿名块内部使用 -- 跳转的关键字(类似 goto/break/continue),就是错误的;如果目标在块内部,你在匿名块外部使用 -- 跳转的关键字(类似 goto/break/continue),也是错误的。 如果局部变量和参数的范围已经包含匿名方法声明,则该局部变量和参数将被称为该匿名方法的“外部”(外界)变量。你看,这个代码段中的 num 就是一个外部(外界)变量: int num = 250; MyDel del = delegate() { WriteLine("#:{0}", --num); }; 这个外部变量的所引用的 num,将会被认为是在创建委托时进行捕获的,它与我们常用的本地变量不同,这个外部变量的生存期 -- 引用该匿名方法的委托对象被 .NET 的 CLR 的垃圾回收机制进行回收。 【注意】匿名方法无法访问外部(外界)范围内,带 ref 与 out 关键字的参数。 【注意】在匿名块中不能访问 unsafe 的代码。 【注意】在 is 运算符的左边,是用不了匿名的方法的。
三、匿名方法与Lambda表达式的区别
Lambda表达式和匿名方法的比较
第一、Lambda表达式本身就是匿名方法。
第二、Lambda表达式允许不指明参数类型,但是匿名方法必须要明确。
第三、Lambda表达式允许单一的表达式或多条语句组成,而匿名方法不允许单一表达式。
CalculatorDelegate cal2 = delegate (int a,int b) { return a-b; } public delegate int CalculatorDelegate(int a,int b);
CalculatorDelegate cal3 = (int a, int b) => { return a + b; }; CalculatorDelegate cal4 =(a,b)=>a-b;
来源:https://blog.csdn.net/polsnet/article/details/129711365
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析