首页 > 编程语言 >c#中的 委托、匿名方法、lambda表达式、事件

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

时间:2023-05-02 19:56:47浏览次数:40  
标签:调用 委托 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

标签:调用,委托,c#,匿名,实例,事件,方法,lambda
From: https://www.cnblogs.com/idasheng/p/17368121.html

相关文章

  • 2023-05-02_National-Library-of-China
    2023年5月2日国家图书馆Datetime:2023-05-02T12:44+08:00Categories:Tour目录缘起反向导航与双层大巴进入国图与借阅藏书甲骨文特展与瞎逛诗集结尾缘起国家图书馆,简称国图。为什么要去国家图书馆呢,起因是看到《口是心非》专辑里,《神采》的文案:更漫长的永昼来临以前......
  • 闲聊 React hook,我们聊的是什么?
    Reacthook的由来Reacthook的由来,其实也可以看作是前端技术不断演进的结果。在worldwideweb刚刚诞生的洪荒时代,还没有js,Web页面也都是静态的,更没有所谓的前端工程师,页面的内容与更新完全由后端生成。这就使得页面的任意一点更新,都要刷新页面由后端重新生成,体验非常糟糕......
  • MFC-LineTo绘制直线
     HDChdc=::GetDC(m_hWnd);BOOLb=::MoveToEx(hdc,100,100,NULL);//移动绘制点BOOLb1=LineTo(hdc,200,50);//绘制直线/*参数1:HDChdc参数2:intX线段终点X坐标位置,采用逻辑坐标表示。这个点不会实际画出来;它不属于线段的一部份......
  • CTF-MISC-流量隐写(持续更新)
    1.认证方式了解题目来源:NSSCTF    可以看到有token值,这是jwt的数据结构 ......
  • 无法将“Autodesk.autocad.livepreview.previewruleprovider”的对象强制转换为类型“
    具体问题如下图所示:在vs2010中调试中,会调用CAD2014,这时就会出现上述的问题错误;经查找,具体的问题是因为:cad的dll引用中,“复制本地”设置为True,因更改为false;这里要注意的是,每个项目中的引用都要更改为false,如Hello项目和InitAndOpt项目中的引用,都要为false,否则这个错误一直会出......
  • spring security的.rememberMe
    springsecurity的.rememberMe springsecurity的.rememberMe的key干嘛用 Authorization:Basicuser是干嘛用  ......
  • 简单聊聊,使用Vue.js编写命令行界面,前端开发CLI的利器
    Temir介绍Temir,一个用Vue组件来编写命令行界面应用的工具.开发者只需要使用Vue就可以编写命令行应用,不需要任何额外的学习成本.<scriptlang="ts"setup>import{ref}from'@vue/runtime-core'import{TBox,TText}from'@temir/core'constcounter=ref(0)setIn......
  • 【C++】设立一组状态,在程序运行过程中设置对象的某个状态,检查对象是否满足所有的状态
    `#include//定义状态枚举enumState{STATE_A=1<<0,//0001STATE_B=1<<1,//0010STATE_C=1<<2//0100};classMyClass{private:intcurrentState;public:MyClass():currentState(0){}//设置状态voidsetState(Statestate){......
  • [PKUWC2018]猎人杀
    概率的分母在不断变化很麻烦,我们不妨令它可以打到已死的人。由于还活着的人概率之比没有变,显然是不会影响答案的。考虑容斥,设\(p(S)\)表示集合\(S\)中的人在\(1\)后被打的方案数,那么答案就是\(\sum_{S}(-1)^{|S|}p(S)\)。\(p(S)\)实际上就是无限开枪,每次不打\(S\cup\{1......
  • 认识Microbit的开发板
    #Microbit介绍##来自你的消息:Microbit简介##来自ChatGPT的消息:Micro:bit是一种微型电脑,由BBC(英国广播公司)等组织为英国中小学生设计的教育用硬件,于2015年发布。它有一个5x5LED矩阵屏幕、两个按键、一组引脚、一个加速度计、一个电子罗盘、一个温度传感器和一个蓝牙无线......