当前位置: 首页 > news >正文

【C#】委托/Lambda/事件

目录

  • 0 参考文章
  • 1 委托(delegate)
    • 1.1 委托的用途
    • 1.2 委托的声明
      • 1.2.1 自定义委托
      • 1.2.2 System.Func和System.Action
    • 1.3 委托是一种特殊的类
  • 2 Lambda表达式
  • 3 事件(event)

0 参考文章

知乎:事件是以特殊方式声明的委托字段吗
书籍:《C# 7.0本质论》

1 委托(delegate)

为了方便理解,我们先从委托的用途讲起。

1.1 委托的用途

在C/C++中,“函数指针”将对方法的引用作为实参传给另一个方法,而委托在C#中承担着相似的功能。虽然这样说,但我们对委托用途的理解可能还是很抽象,这里用一个简单的例子帮助理解。

冒泡排序是最基础的排序算法,它的代码大致如下:

public static void BubbleSort(int[] items) {if (items == null) return;for(var i = items.Length - 1; i >= 0; i--){for(var j = 0; j+1 <= i; j++){if (items[j] > items[j + 1]){var temp = items[j];items[j] = items[j + 1];items[j + 1] = temp;}}}
}

该方法对整数数组执行升序排序。

但为了能够选择升序和降序,我们开始拓展这段代码。
第一个方案:拷贝以上代码,然后把大于操作符换成小于操作符。
第二个方案:增加一个参数,指出我们当前希望如何排序,然后在代码里进行判断。

但以上代码只照顾到了两种可能的排序方式,如果还想要按其他方式进行排序,代码就会变得很庞大。

为了增加灵活性,减少重复代码,我们可以将比较方法作为参数传入。而此时我们需要有一个数据类型可以表示方法,这就是委托。

委托加入后,代码会变成这样:

public static void BubbleSort(int[] items,Func<int,int,bool> compare) {if (items == null) return;if (compare == null) return;for(var i = items.Length - 1; i >= 0; i--){for(var j = 0; j+1 <= i; j++){if (compare(items[j], items[j+1])){var temp = items[j];items[j] = items[j + 1];items[j + 1] = temp;}}}
}

显然灵活多了。

PS:写到这里我突然理解了Sort((x,y)=>x>y)的含义,之前对升序到底对应x>y还是y>x总是一知半解。x>y那么x和y就交换,所以就是升序排列(虽然Sort底层是优化过的快排,不是冒泡排序,但也有两个数比对的步骤,所以代入一下就能得出结论)。

1.2 委托的声明

1.2.1 自定义委托

声明委托需要使用关键词delegate,并且指出委托的返回值和所需参数,只有方法的返回值和参数与委托一致,才可以将方法传递给委托。

//先定义委托
delegate void Feedback();
//然后使用new操作符构造委托实例并传入方法
class Program{static void Main(string[] args){//向委托的构造函数传递静态方法Feedback fbStatic = new Feedback(Program.FeedbackToConsole);//传递实例方法Feedback fbInstance = new Feedback(new Program().FeedbackToFile);//调用委托的两种不同方式fbStatic.Invoke();fbInstance();}
}

1.2.2 System.Func和System.Action

为了减少定义自己的委托类型的必要,C#推出了一组常规用途的委托。

Func
代表有返回值的方法。

delegate TResult Func<参数,参数...,out TResult>

Action
代表无返回值的方法。

delegate void Action<参数,参数...>

PS:虽然C#开始提供Func和Action委托,减去了自定义委托类型的必要(要写声明还要自定义名字),但有时候出于可读性,还是可以考虑声明自己的委托类型。如Comparer委托就能使人对其用途一目了然。

public delegate bool Comparer(int first,int second);
class Program{public static void BubbleSort(int[] items,Comparer comparer){}
}

1.3 委托是一种特殊的类

委托实际上是特殊的类,它派生自System.MulticastDelegate,而后者又派生自System.Delegate,其实把它理解成一种C#里的类型就可以了,但它和一般的类型又有些不同。如果说int,string等是对数据类型的定义,那么委托就类似于对“方法类型”的定义(看着下面的代码仔细琢磨)。

string str;
delegate void Method(int num);  //理解:Method是变量名,delegate/返回值/参数共同构成了一种“方法类型”

2 Lambda表达式

上文提到的冒泡排序,如果我们想去调用它,代码大致如下:

public static void BubbleSort(int[] items,Func<int,int,bool> comparer){}//声明方法
public static void GreaterThan(int first,int second){return first>second;
}//Main函数里调用
static void Main(string[] arg){int[] items = new int[5];//...初始化BubbleSort(items,GreaterThan);
}

你会发现整个过程下来要进行的准备有点太过复杂了,其实我们只需要主体的return first>second。于是C#提供了匿名函数(C# 3.0叫Lambda表达式),简化了这个过程。

BubbleSort(items,(first,second)=>{return first>second});

3 事件(event)

事件就是对委托的封装,相对于委托,它只提供了“+=”和“-=”两个方法,保证了在外部操作时的安全性。

对订阅的封装
只提供“+=”和“-=”,避免程序员在编写代码时错误地使用“=”代替“+=”。

对发布的封装
不再提供Invoke方法,保证只有指定字段发生变更时,委托方法才会被调用,避免外部主动调用。

private delegate void Method();
public event Method EventName;   //提供给外部

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Python | Leetcode Python题解之第337题打家劫舍III
  • 单细胞课程01-课程简介
  • Qt题目知多少-4
  • 基于python 开发调试rabbitmq - 2
  • 鸿蒙前端开发——工具安装与项目创建
  • Vue2中watch与Vue3中watch对比
  • “论软件开发过程RUP及其应用”写作框架,软考高级,系统架构设计师
  • 使用 GPT-4 Vision 的 CLIP 嵌入来改进多模态 RAG
  • 【运维】JetBrains Gateway (Pycharm) SSH免密连接,改为免密连接
  • 【Material-UI】Floating Action Button (FAB) 详解:基础用法
  • ubuntu22.04不生成core文件
  • 结构体structure、共用体union
  • Elasticsearch中的自动补全功能详解与实践
  • 苹果Mac电脑——装macOS和Windows双系统的方法
  • html+css+js网页制作 京东首页官网 ui还原度100%
  • AWS实战 - 利用IAM对S3做访问控制
  • gf框架之分页模块(五) - 自定义分页
  • Java到底能干嘛?
  • Js基础知识(四) - js运行原理与机制
  • Material Design
  • nginx 负载服务器优化
  • Rancher如何对接Ceph-RBD块存储
  • SpingCloudBus整合RabbitMQ
  • SQLServer之创建显式事务
  • tweak 支持第三方库
  • Webpack 4x 之路 ( 四 )
  • 二维平面内的碰撞检测【一】
  • 工作中总结前端开发流程--vue项目
  • 王永庆:技术创新改变教育未来
  • 一个项目push到多个远程Git仓库
  • 树莓派用上kodexplorer也能玩成私有网盘
  • 组复制官方翻译九、Group Replication Technical Details
  • ​iOS实时查看App运行日志
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #vue3 实现前端下载excel文件模板功能
  • #Z0458. 树的中心2
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (+4)2.2UML建模图
  • (windows2012共享文件夹和防火墙设置
  • (搬运以学习)flask 上下文的实现
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (四)linux文件内容查看
  • (算法)硬币问题
  • (转)c++ std::pair 与 std::make
  • .net dataexcel winform控件 更新 日志
  • .NET IoC 容器(三)Autofac
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
  • .net 逐行读取大文本文件_如何使用 Java 灵活读取 Excel 内容 ?
  • .Net转Java自学之路—基础巩固篇十三(集合)
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • :=
  • :如何用SQL脚本保存存储过程返回的结果集
  • @Documented注解的作用
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)