c#中的 委托、匿名方法、lambda表达式、事件

综述:委托、匿名方法、lambda表达式、事件

委托的意义在于:通过委托把函数当成方法参数来传递,以便方法内部调用额外传过来的处理逻辑。

(定义委托类型→声明委托变量→实例化委托变量(附加方法)→作为参数传递给目标方法→目标方法内调用委托)

匿名方法的意义在于:快速方便的实例化委托,不用定义具体的方法来关联委托,就是临时定义个方法(处理逻辑)与委托相关联。

lambda表达式的意义在于:简化匿名方法的定义,更方便的实例化委托。

事件的意义在于:??

匿名方法允许我们避免使用独立的具名方法。总之匿名方法是为了节省代码,避免正式的声明只用一次的方法;还有Lambda表达式,也是为了简化匿名方法;

认识委托

官网文档:委托 - C# 编程指南 | Microsoft Learn

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法。

委托的定义类似方法,用关键字delegate

如:public delegate string TestDelegate(string inputStr);

c#中的委托可以理解为函数的一个包装,它使得c#中的函数可以作为参数来被传递。可以把委托看做一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。

方法签名包括方法名、方法参数的个数、类型和顺序

委托使得c#中的函数可以作为另一个函数的参数来被传递,这样可以实现比如说回调的功能。

委托的本质

你定义一个委托类型,编译器会把它编译成一个继承自System.MulticastDelegate的子类。父类拥有一个构造函数和3个虚方法。因为有构造函数,故委托变量可以用new来实例化。

运算符重载:表面上扩展了运算符实际上是方法的扩展。

使用委托的步骤

  1. 定义委托类型
  2. 声明委托变量
  3. 实例化委托变量(附加方法)
  4. 作为参数传递给目标方法
  5. 目标方法内调用委托

也可以在实例化委托变量即附加相关方法后,直接调用委托来回调关联的方法;

实例化委托的方式

委托是引用类型,创建委托对象,或者说实例化委托,有三种方法:

eg:Delegate void MyDel (int x) ;

  • MyDel md1=new MyDel (new ClassA().M1);
  • MyDel md2 = new ClassA().M1; //之间存在隐式转换,注意,用来实例化委托的具名方法是不带括号的;
  • MyDel md3= x=>{ x=100+x; } ; //使用lamada表达式实例化委托变量

委托链

它是委托类型,把链接了多个方法的委托称为委托链或多路广播委托(它封装了对对象方法引用的数组)。在调用委托链时,被绑定到委托链中的每个方法都会被调用,且调用顺序是先绑定先调用

使用如:MyDel theChain=null; theChain+=md; theChain+=mc;

调用:theChain(); //先执行md,再执行mc

委托还可以组合,即; MyDel md3=md1+md2;

.net 类库中常用的委托

Action<T1,T2,……>

无返回值的处理逻辑,用它。

Func<T1,T2,……,TResult>

有返回值的处理逻辑,用它。

匿名方法

匿名方法就是没有名字的方法,因为没有名字,所以只能在函数定义的时候被调用,在其他任何情况下都不能被调用,所以不具有复用性;编译器在编译匿名方法时会为其生成一个方法名。

匿名方法与委托结合使用:

在实例化委托变量时,不直接赋予它一个定义好了的方法,而是直接“现做现卖”。

格式:委托类型 变量名 = delegate( 形参 ) { 逻辑处理语句 };

比如:td1 += delegate (int a, string n) { $"Hi {n},你一顿吃{a}个馒头??";};

调用时直接用:变量名(实参);这个调用是隐式调用方式。

如果形参是无参的,delegate后面的括号不要写,直接跟大括号,里面写实现代码就行了;如果形参有参数,那括号就别省;

如果委托有返回值,那么匿名方法的方法体中的return后面一定要跟与委托返回值类型兼容的值(如其子类、实现类的相应对象)

闭包延长外部变量的生命周期:

匿名方法的缺点:不能在其他地方被调用,即不具有复用性。 而且,匿名方法会将自动形成闭包。当一个函数(这里称为外部函数)包含对另一个函数(内部函数)的调用时,或内部函数使用了外部函数的变量时都会形成闭包。对于一个被捕获的变量(匿名方法内使用匿名方法外的变量)而言,只要还有任何委托实例在引用它,它就一直存在,就不会在部分委托实例调用结束后被垃圾回收释放掉。

对于匿名方法捕捉到的变量,编译器会额外创建一个类来容纳他们,可通过IL来查看。

匿名方法不能访问外部范围的 ref out 参数。

Lambda表达式

由来:是为了简化匿名方法,减少代码;

简介:Lambda表达式可以理解为一个匿名方法,使用 => 操作符(读作goes to),它在一行代码中声明方法的参数,以及方法的逻辑。用于创建委托实例或转换为表达式树。

写法上, => 的左边是匿名方法的输入参数(形参),右边是表达式或语句块。

左边:形参无论多少个,不需要声明类型,c#编译器会推断出类型,若只有一个参数,不用写括号,0个或1个以上的参数需要括号。

右边:表达式或语句块,如果只有一句,不用写 {},如果有好几句,那就得需要了。另外如果只有一句,则不需要写 return 关键词,其结果会自动作为方法返回值。

Lambda 表达式是作为对象处理的代码块(表达式或语句块)。 它可作为参数传递给方法,也可通过方法调用返回。

演变过程

》c#1.0中创建委托实例,是用一个另外定义的具体方法

》c#2.0中可以用匿名方法来创建委托实例,此时就不需要额外定义回调方法即委托关联的方法了。

》c#3.0用Lambda表达式来创建委托实例。

使用:在实际开发过程中,委托的用途莫过于订阅事件了,lambda表达式的作用可以是订阅事件。总之,他就是结合委托使用的。

表达式也有树结构----表达式树。

作用:为学习Linq to SQL做铺垫。

解释:可以理解为一种数据结构,即类似与数据结构课程中的栈和队列,只不过表达式树用于表示Lambda表达式的逻辑罢了。

事件

定义格式: 访问修饰符(最好public) event 委托类型 事件名 ;

事件是建立在委托的基础之上的。它涉及两个类(不太准确),事件发布者和事件订阅者。

事件订阅者需要订阅(实例化)事件发布者发布的事件,以便在事件被触发时接受消息并作出处理。用+=订阅,-=取消订阅。

使用步骤:

  1. 自定义委托,也可以用系统的EventHandler
  2. 用该委托定义事件;
  3. 发出事件;实际就是一个函数,返回类型和签名同委托(不太确定),里 面触发事件的语句(函数调用)是:事件名(实参); 这样会调用与委托关联的函数。
  4. 以上三点可以写在发布者类里。事件处理写在订阅者类里,它就是一个可以与委托关联(实例化委托变量)的函数,该函数里面对发布者进行回应;
  5. 事件的订阅:事件名 +=与委托关联的方法(即实例化委托变量时 等号的右半部分);
  6. 发出通知:调用之前“发出事件”时定义的函数。然后各个订阅者就可以有所反应了。

事件相关的常见委托类型

EventHandler是.NET类库中预定义的委托类型,它只用于处理不包含事件数据的事件。如果我们想在这种方式定义的事件中包含事件数据,则可以通过派生EventArgs类来实现。

事件的本质

特殊的多路广播委托(委托链)。c#事件提供了对私有委托字段进行访问的有效方法。 验证可以查看IL代码。c#中的事件被编译成包含一字段(私有委托变量)和两个公共方法的代码段。两个公共方法中,一个带有add_前缀,一个带有remove_前缀,前缀后面是事件名称。Add前缀方法是通过调用Delegate.Combine()方法来实现的,该方法将多个委托组合成了一个多路广播委托(委托链),相应的,remove前缀的方法是通过间接调用Delegate.Remove()方法,该方法用于将一个委托从委托链中字段移除。这个私有的委托类型变量,用于保存对事件处理方法的引用,注意该委托类型的变量为私有,只能从定义该事件的类中进行访问。


更新于:2023-05-02

posted @   AI大胜  阅读(234)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示