图解C#高级教程(一):委托
什么是委托
可以认为委托是持有一个或多个方法的对象。但它与对象不同,因为委托可以被执行。当执行委托时,委托会执行它所“持有”的方法。先看一个完整的使用示例。
// See https://aka.ms/new-console-template for more informationdelegate void MyDel(int value); // 声明委托类型class Program
{void PrintLow(int value){Console.WriteLine("{0} - Low value", value);}void PrintHigh(int value){Console.WriteLine("{0} - High value", value);}static void Main(string[] args){Program p = new Program();MyDel del; // 声明委托变量Random rand = new Random();int randomValue = rand.Next(1, 101);del = randomValue < 50 ? p.PrintLow : p.PrintHigh; // 选择调用哪个方法del(randomValue); // 调用委托方法}
}
如果生成的随机数 randomValue 小于50,则 del 引用的是 p.PrintLow;否则,del 引用的是 p.PrintHigh。
委托概述
下图是使用类和委托的对比:

可以把 delegate 看成一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。如下图所示:

委托的几点说明:
-
方法的列表称为调用列表;
-
委托保存的方法可以来自任何类或者结构,只要它们在委托的返回类型和委托的签名(包括ref和out修饰符)上保持一致;
-
调用列表中的方法可以是实例方法,和静态方法;
-
执行委托时,会按照方法的添加顺序执行调用列表。
声明委托类型

创建委托对象
委托是引用类型,因此初始化委托变量需要创建一个对象。有两种方式创建委托对象:
-
使用
new运算符:class Obj {void MyM1(int value) {}static void Other(int value) {}} delegate void MyDel(int value);MyDel del1, del2;Obj obj = new Obj(); del1 = new MyDel(obj.MyM1); // 使用一个对象的方法 del2 = new MyDel(Obj.Other); // 使用静态方法创建委托对象 -
省略
new运算符:MyDel del1, del2; del1 = obj.MyM1; // 使用一个对象的方法 del2 = Obj.Other; // 使用静态方法创建委托对象当为委托变量赋值时,除了为委托分配内存,创建委托对象还会把第一个方法放入委托的调用列表。
当然,我们还可以在声明委托变量时初始化委托变量。
MyDel del1 = obj.MyM1;当我们为同一个委托变量赋值另外一个委托对象时,之前的委托对象就会被垃圾回收器回收。
MyDel delVar; delVar = myInstObj.MyM1; ... delVar = SClass.OtherM2;

组合委托
可以将两个同类型的委托变量进行 + 操作,赋值给另外一个同类型的新变量,完成委托的组合:
MyDel delA = myInstObj.MyM1;
MyDel delB = SClass.OtherM2;MyDel delC = delA + delB;

为委托添加或者删除方法
委托可以持有多个方法, 通过使用运算符 += 和 -= 可以为委托添加或者删除方法。
MyDel del = inst.MyM1;
del += SCl.m3;
del += X.Act;

从委托移除方法:
del -= SCl.m3;

移除委托时需要注意以下事项:
- 如果委托的调用列表中存在多个实例,
-=运算符将从列表的最后开始搜索,并移除第一个与方法匹配的实例; - 当要删除的方法在调用列表不存在时,什么也不会发生;
- 试图调用空委托将会导致异常,因此执行委托之前有必要进行 null 判空。
调用委托
调用委托的方式与调用方法一样。用于调用委托的参数将会传递给调用列表当中的每一个方法(除非有输出参数)。
MyDel delVar = inst.MyM1;
delVar += SCl.m3;
delVar += X.Act;
...
delVar(55);

下面是一个完整的示例:
delegate void PrintFunction();class Test
{public void Print1(){Console.WriteLine("Print1 -- instance method");}public static void Print2(){Console.WriteLine("Print2 -- static method");}}class Program
{static void Main(){Test t = new Test();PrintFunction pf;
// 实例方法pf = t.Print1;// 给委托增加3个另外的方法pf += Test.Print2;pf += t.Print1;pf += Test.Print2; // 现在委托含有4个方法if (null != pf){pf();}else{Console.WriteLine("委托为空");}}
}
输出如下:
Print1 -- instance method
Print2 -- static method
Print1 -- instance method
Print2 -- static method
调用带引用参数的委托
如果委托有引用参数,在调用委托列表中的下一个方法时,参数的新值会传给下一个方法。
delegate void MyDel(ref int x);class MyClass
{public void Add2(ref int x) { x += 2; }public void Add3(ref int x) { x += 3; }static void Main(){MyClass mc = new MyClass();MyDel del = mc.Add2;del += mc.Add3;del += mc.Add2;int x = 5;del(ref x);Console.WriteLine("Value: {0}", x);}
}
输出如下:
Value: 12

Lambda 表达式
C# 当中的 Lambda 表达式是一种简洁的方式来表示匿名方法。通常用于简化代码,尤其是在与 LINQ、委托或事件等相关的场景中。(LINQ 和 事件会在后面相关文章中讲到)。
语法:
(parameters) => expression
parameters:代表输入参数。如果只有一个参数,可以省略()。=>:称为 lambda 操作符,表示从参数到表达式或代码块的映射。expression:返回的表达式。如果有多条语句,可以使用代码块{}。
Lambda 表达式的常见使用场景
- 委托与 Lambda 表达式
delegate void MyDel(int x);MyDel del = x => x *2;
用于 Func<T>委托:
using System;class Program
{static void Main(){// Func 委托,接受两个整数参数,返回它们的和Func<int, int, int> add = (a, b) => a + b;int result = add(3, 4);Console.WriteLine(result); // 输出 7}
}
Func 是 C# 中一个常用的泛型委托类型,在 C# 3.0 中引入,专门用于表示带有返回值的方法。在使用时,Func<T> 委托的最后一个泛型参数是返回值类型(必须有),前面的泛型参数是输入参数类型(可以没有输入参数)。
-
Lambda 表达式与 LINQ 查询
using System; using System.Linq; using System.Collections.Generic;class Program {static void Main(){List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };// 使用 Lambda 表达式筛选出大于 3 的数var result = numbers.Where(n => n > 3).ToList();foreach (var number in result){Console.WriteLine(number); // 输出 4, 5, 6}} } -
用于事件处理
using System;class Program {static void Main(){Action<string> messagePrinter = msg => Console.WriteLine(msg);messagePrinter("Hello, Lambda!"); // 输出 "Hello, Lambda!"} }
委托的使用场景
委托的主要使用场景如下:
- 事件处理。
- 回调函数。
以上两个使用场景的具体例子会在后面文章介绍到。
小结:本章主要介绍了 C# 当中委托和 Lambda 的概念、用法。
各位道友,码字不易,如有收获,记得一键三连啊。
相关文章:
图解C#高级教程(一):委托
什么是委托 可以认为委托是持有一个或多个方法的对象。但它与对象不同,因为委托可以被执行。当执行委托时,委托会执行它所“持有”的方法。先看一个完整的使用示例。 // See https://aka.ms/new-console-template for more informationdelegate void M…...
CMSIS-RTOS V2封装层专题视频,一期视频将常用配置和用法梳理清楚,适用于RTX5和FreeRTOS(2024-09-28)
【前言】 本期视频就一个任务,通过ARM官方的CMSIS RTOS文档,将常用配置和用法给大家梳理清楚。 对于初次使用CMSIS-RTOS的用户来说,通过梳理官方文档,可以系统的了解各种用法,方便大家再进一步的自学或者应用&#x…...
渗透测试入门学习——使用python脚本自动识别图片验证码,OCR技术初体验
写在前面 由于验证码在服务端生成后存储在服务器的session中,而标用于标识用户身份的sessionid存在于用户cookie中 所以本次识别验证码时需要用requests.session()创建会话对象,模拟真实的浏览器行为,保持与服务器的会话才能获取登录时服务…...
docker环境下配置cerbot获取免费ssl证书并自动续期
文章目录 实践场景了解certbot查看nginx的映射情况操作目标配置nginx配置的ssl证书设置自动续签 实践场景 本人使用docker部署了一个nginx容器,通过容器卷,实现本地html,ssl,conf和ngiinx容器映射的, 经常需要手动部署…...
Studying-多线程学习Part1-线程库的基本使用、线程函数中的数据未定义错误、互斥量解决多线程数据共享问题
来源:多线程编程 线程库的基本使用 两个概念: 进程是运行中的程序线程是进程中的进程 串行运行:一次只能取得一个任务并执行这一个任务 并行运行:可以同时通过多进程/多线程的方式取得多个任务,并以多进程或多线程…...
Flink 03 | 数据流基本操作
Flink数据流结构 DataStream 转换 通常我们需要分析的业务数据可能存在如下问题: 数据中包含一些我们不需要的数据 数据格式不方面分析 因此我们需要对原始数据流进行加工,比如过滤、转换等操作才可以进行数据分析。 “ Flink DataStream 转换主要作…...
在 TS 的 class 中,如何防止外部实例化
在 TypeScript(TS)中,如果你想要防止一个类被外部实例化,你可以采取以下几种策略: 将构造函数设为私有(Private Constructor): 通过将类的构造函数设为私有,你可以阻止外…...
HTML详解
HTML 基础HTML 标题HTML 段落HTML 链接HTML 图片HTML 元素HTML 注释HTML 属性HTML 文本格式化HTML 头部HTML cssHTML 表格HTML 列表HTML 自定义列表HTML 区块HTML 表单HTML 框架HTML 颜色HTML 脚本HTML 事件HTML 实体HTML urlHTML5 新元素 新元素 新元素 新元素 新元素 新元素 …...
记录|Modbus-TCP产品使用记录【德克威尔】
目录 前言一、德克威尔1.1 实验图1.2 DECOWELL IO Tester 软件1.3 读写设置1.4 C#进行Modbus-TCP读写 更新时间 前言 参考文章: 使用的第二款Modbus-TCP产品。 一、德克威尔 1.1 实验图 1.2 DECOWELL IO Tester 软件 这也是自带模块配置软件的。下图就是德克威尔的…...
基于深度学习的视频生成
基于深度学习的视频生成是一项极具前景的技术,旨在通过神经网络模型生成逼真的动态视频内容。随着生成对抗网络(GANs)、自回归模型、变分自编码器(VAEs)等深度学习模型的发展,视频生成技术已经取得了显著进…...
TB6612电机驱动模块(STM32)
目录 一、介绍 二、模块原理 1.原理图 2.电机驱动原理 三、程序设计 main.c文件 Motor.h文件 Motor.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 TB6612FNG 是东芝半导体公司生产的一款直流电机驱动器件,它具有大电流 MOSFET-H 桥结构ÿ…...
webpack信息泄露
先看看webpack中文网给出的解释 webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换、打包或包裹任何资源。 如果未正确配置,会生成一个.map文件,它包含了原始JavaScript代码的映…...
启动服务并登录MySQL9数据库
【图书推荐】《MySQL 9从入门到性能优化(视频教学版)》-CSDN博客 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) Windows平台下安装与配置MyS…...
微服务_3.微服务保护
文章目录 一、微服务雪崩及解决方法1.1、超时处理1.2、仓壁模式1.3、断路器1.4、限流 二、Sentinel2.1、流量控制2.1.1、普通限流2.1.2、热点参数限流 2.2、线程隔离2.3、熔断降级2.3.1、断路器状态机2.3.2、断路器熔断策略2.3.2.1、慢调用2.3.2.2、异常比例,异常数…...
【设计模式】软件设计原则——依赖倒置合成复用
依赖倒置引出 依赖倒置 定义:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。面向接口编程而不是面向实现编程。 通过抽象使用抽象类、接口让各个类or模块之间独立不影响,实现…...
vue中如何实现组件通信
1. 父子组件通信 1. props和emits 我们最常见的组件通信就是父子组件数据通信。父子组件实现数据通信需要使用props和emit两个api。 在父组件中我们通过props将数据绑定给子组件,在子组件中我们可以通过props对象来收集到父组件传递的数据。 在子组件想要修改的pr…...
C/C++:内存管理
文章目录 前言一、内存分区1. 内存划分情况2. 最大内存计算 二、malloc/calloc/realloc 与 free1. malloc2. calloc3. realloc4. free5. 差异对比6. 失败处理 三、内存分配题目1. 题目2. 内存区域划分 四、C内存管理方式1. new 与 delete2. new/delete操作内置类型3. new和dele…...
jmeter学习(4)提取器
同线程组https://blog.csdn.net/vikeyyyy/article/details/80437530 不同线程组 在JMeter中,正则表达式提取的参数可以跨线程组使用。 通过使用Beanshell后置处理器和属性设置函数,可以将提取的参数设置为全局变量,从而在多个线程组之间共享…...
移动端的每日任务,golang后端数据库应该怎么设计
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...
1、Spring Boot 3.x 集成 Eureka Server/Client
一、前言 基于 Spring Boot 3.x 版本开发,因为 Spring Boot 3.x 暂时没有正式发布,所以很少有 Spring Boot 3.x 开发的项目,自己也很想了踩踩坑,看看 Spring Boot 3.x 与 2.x 有什么区别。自己与记录一下在 Spring Boot 3.x 过程…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
