【从零开始入门unity游戏开发之——C#篇33】C#委托(`Delegate`)和事件(`event` )、事件与委托的区别、Invoke()的解释
文章目录
- 一、委托(`Delegate`)
- 1、什么是委托?
- 2、委托的基本语法
- 3、定义自定义委托
- 4、如何使用自定义委托
- 5、多播委托
- 6、C# 中的系统委托
- 7、`GetInvocationList` 获取多个函数返回值
- 8、总结
- 二、事件(`event` )
- 1、事件是什么?
- 1.1. **事件是基于委托的存在**:
- 1.2. **事件是委托的安全包裹**:
- 1.3. **事件是一种特殊的变量类型**:
- 2、事件的使用
- 2.1 **声明事件语法**:
- 2.2 **事件的使用**:
- 2.3 **事件的触发**:
- 3、**委托与事件的区别**:
- 4、为什么要使用事件?
- 4.1. **防止外部随意置空委托**:
- 4.2. **防止外部随意调用委托**:
- 4.3. **事件封装了委托,增强了安全性**:
- 5、事件与委托的区别总结
- 5.1. **赋值操作**:
- 5.2. **调用方式**:
- 5.3. **临时变量**:
- 5.4. **可见性和封装**:
- 6、总结
- 三、补充`Invoke()`的解释说明
- 专栏推荐
- 完结
一、委托(Delegate
)
1、什么是委托?
委托(Delegate
)是 C# 中的一个重要概念,它可以被理解为“函数(方法)的容器
”。更具体地说,委托是用来存储、传递函数(方法)的类型,允许我们将方法作为参数传递给其他方法或存储在变量中。委托的本质是一个类,用来定义方法的类型(即方法的返回值和参数类型)。
- 委托本质上是一个类型,它定义了方法的类型:返回值类型和参数类型。
- 委托用于
存储方法
,它帮助你把方法传递给其他方法,或者在程序中动态调用。 - 委托是一个
引用类型
2、委托的基本语法
-
关键字:
delegate
-
语法:
[访问修饰符] delegate 返回值类型 委托名(参数列表);
示例:
delegate void MyDelegate(); // 无返回值,无参数的委托 delegate int MyDelegateWithParam(int x); // 返回 int,带一个参数的委托
- 委托可以声明在类(
class
)或命名空间(namespace
)中,一般情况下会声明在命名空间中。 - 委托声明的访问修饰符可以是
public
(默认是public
),也可以是private
,protected
,internal
等。
3、定义自定义委托
-
无参无返回值的委托
public delegate void MyFun();
-
带参数和返回值的委托
public delegate int MyFun2(int a);
-
泛型委托:
委托是支持泛型的,可以让返回值和参数的类型灵活变更。delegate T MyFun3<T, K>(T v, K k);
4、如何使用自定义委托
委托变量相当于一个函数的容器,可以存储函数,并通过委托来调用它。
-
基本用法
- 通过
new
关键字创建委托实例,并调用它。
public delegate void MyFun();static void Fun() {Console.WriteLine("Hello from Fun!"); }static void Main(string[] args) {MyFun f = new MyFun(Fun); // 创建委托实例f.Invoke(); // 调用委托,输出: Hello from Fun! }
- 通过
-
简化方式(委托隐式转换)
委托可以直接引用方法,而不需要new
操作符。MyFun f2 = Fun; f2(); // 调用 Fun() 方法
-
带参数的委托
如果委托方法需要参数,可以在调用时传递参数。public delegate int MyFun2(int a);static int Fun2(int x) {return x * 2; }static void Main(string[] args) {MyFun2 f3 = Fun2;Console.WriteLine(f3(3)); // 输出: 6 }
-
委托作为参数
委托经常作为方法的参数,用于传递行为。public delegate void MyFun(); // 定义委托类型class Test(){public void TestFun(MyFun fun){// 执行其他逻辑fun(); // 调用传入的委托方法}}class Program{static void FunLog(){Console.WriteLine("Hello from Fun!");}public static void Main(){MyFun Fun = FunLog; // 创建委托实例Test t = new Test();t.TestFun(Fun); // 传递委托}}
结果打印
5、多播委托
委托可以存储多个方法(多播委托)。通过多播委托,可以在一个委托实例中注册多个方法,调用时按顺序执行这些方法。
-
添加多个方法到委托
MyFun f = Fun; f += Fun2; f(); // 调用 Fun 和 Fun2 方法
-
移除方法
f -= Fun; f(); // 只调用 Fun2 方法
-
清空所有注册的方法
f = null;
6、C# 中的系统委托
C# 提供了多个预定义的委托类型,主要有 Action
和 Func
。这些委托可以大大简化我们的编程工作。
-
Action 委托
Action
委托用于没有返回值
的情况。它可以有0到16个参数。Action委托的语法结构
Action<参数类型1, 参数类型2, ..., 参数类型N>
-
无参数:
Action action = Fun; action += Fun2; action();// 调用 Fun 和 Fun2 方法
-
带参数:
Action<int, string> action2 = Fun2; action2(1, "Hello");
-
-
Func 委托
Func
委托用于有返回值
的情况。它也支持0到16个参数。
Func
委托的最后一个类型参数
总是方法的返回值类型
,而其他的参数类型则是方法的输入参数类型。Func 委托的语法结构
Func<参数类型1, 参数类型2, ..., 参数类型N, 返回值类型>
-
无参数,有返回值:
static int FunLog() {return 1; }public static void Main() {Func<int> func = FunLog;int result = func();Console.WriteLine(result);// 打印1 }
-
带参数,有返回值:
static int FunLog(string str){return int.Parse(str);}public static void Main(){Func<string, int> func = FunLog;int result = func("111");Console.WriteLine(result);// 打印111}
-
7、GetInvocationList
获取多个函数返回值
当用有返回值的委托容器存储多个函数时
using System;class Program
{static void Main(){// 创建一个 Func 委托,返回 string 类型Func<string> funTest = () =>{Console.WriteLine("第一个函数");return "1";};// 将更多函数添加到委托链funTest += () =>{Console.WriteLine("第二个函数");return "2";};funTest += () =>{Console.WriteLine("第三个函数");return "3";};// 如果直接调用委托,会执行所有函数逻辑,但是只能获取到最后一个返回值Console.WriteLine("直接调用返回值:");Console.WriteLine(funTest()); // 只会输出 "3"}
}
输出结果:
直接调用返回值:
第一个函数
第二个函数
第三个函数
3
如果想要获取每个函数的返回值,可以通过 GetInvocationList 方法
foreach (Func<string> del in funTest.GetInvocationList())
{Console.WriteLine(del()); // 输出每个函数的返回值
}
输出结果:
第一个函数
1
第二个函数
2
第三个函数
3
- funTest.GetInvocationList() 返回一个委托数组,数组中的每个元素都是一个 Func 委托实例,表示容器中每个方法。
- 我们通过 foreach 遍历这些委托实例,并单独执行每个方法,获取它们的返回值。
8、总结
- 委托 是 C# 中用来装载方法的类型,允许将方法作为参数传递或者存储。
- 委托的定义语法包括
delegate
关键字,后接返回类型、委托名称以及参数列表。 - 委托支持泛型,可以创建灵活的、可重用的函数容器。
- 委托可以作为方法的参数,或作为类的成员变量,用于实现事件、回调等功能。
- C# 提供了 没有返回值
Action
和 有返回值Func
等预定义的委托类型,可以减少开发者手动定义委托的工作量。
二、事件(event
)
在 C# 中,事件是基于委托的,它对委托进行了封装,提供了一些安全性和约束。事件是 C# 中一种特殊的成员,通常用于发布-订阅模式,允许一个对象(发布者)通知其他对象(订阅者)某些事情的发生。
1、事件是什么?
1.1. 事件是基于委托的存在:
- 委托可以理解为对方法的引用,而事件是基于委托的,委托在事件中扮演着核心角色。
1.2. 事件是委托的安全包裹:
- 事件通过封装委托,限制了委托的使用,使其更具安全性。特别是防止外部代码随意更改委托(赋值和调用)。
1.3. 事件是一种特殊的变量类型:
- 在 C# 中,事件是一种特殊的变量,它用于存储委托类型的引用,但它具有一些额外的限制。
2、事件的使用
2.1 声明事件语法:
事件的声明语法如下:
[访问修饰符] event 委托类型 事件名;
例如:
public event Action MyEvent;
2.2 事件的使用:
- 事件作为类的成员变量,类似于委托。
- 事件和委托的使用方法非常相似,主要的区别在于事件增加了访问限制。
示例:
public class Test
{public Action MyFun; // 委托类型成员变量public event Action MyEvent; // 事件类型成员变量
}
2.3 事件的触发:
- 事件的触发只能在事件的拥有者(类)内部进行,通过调用事件处理方法。
- 一般来说,可以通过一个方法来触发事件,例如:
public void DoEvent()
{if (MyEvent != null){MyEvent.Invoke(); // 安全地触发事件}
}
3、委托与事件的区别:
- 事件不能在类外部赋值:委托可以在类外部进行赋值(例如:
myFun = TestFun
),但事件只能通过+=
和-=
操作符来添加和移除事件处理器。 - 事件不能在类外部调用:委托可以在类外部直接调用(例如:
myFun()
),而事件只能在类内部触发(通常使用事件发布方法)。
示例:
public void TestFun()
{Console.WriteLine("123");
}// 委托使用
myFun += TestFun;
myFun(); // 调用委托// 事件使用
myEvent += TestFun; // 添加事件处理器
myEvent(); // 错误,无法在类外部调用事件
4、为什么要使用事件?
4.1. 防止外部随意置空委托:
- 事件的最大优势之一是它防止了外部代码直接修改委托的值(置空)。这保证了事件的安全性,防止委托被外部代码随意设置为
null
或重新赋值,避免了潜在的错误。
4.2. 防止外部随意调用委托:
- 事件提供了对委托的封装,确保事件只能通过
+=
或-=
操作符来添加和移除事件处理器,不能通过外部代码直接调用。
4.3. 事件封装了委托,增强了安全性:
- 事件是一种委托的封装,提供了更高的安全性和可控性,使得事件处理过程更加规范。外部代码无法直接修改事件的内容,只能通过规范的方式来订阅和退订事件。
5、事件与委托的区别总结
5.1. 赋值操作:
- 委托:可以在外部赋值(
=
)。 - 事件:不能直接赋值,只能通过
+=
和-=
操作符来添加和移除事件处理器。
5.2. 调用方式:
- 委托:可以在外部直接调用(
myFun()
)。 - 事件:只能在类内部通过触发方法调用(
MyEvent.Invoke()
)。
5.3. 临时变量:
- 委托:可以作为局部变量在方法中使用。
- 事件:不能作为临时变量,只能作为类的成员存在。
5.4. 可见性和封装:
- 委托:没有访问控制的封装机制,外部可以自由操作。
- 事件:有封装,提供了更严格的访问控制,确保安全性。
6、总结
- 事件本质上是一个委托的封装,提供了额外的安全性。它限制了委托在外部的赋值和调用,使得事件的触发只能在拥有事件的类内部进行。
- 事件的主要用途是实现发布-订阅模式,当某个对象发生变化时,能够通知其他对象,避免外部代码直接修改和调用事件。
- 事件与委托的主要区别在于访问权限的控制,事件通过严格的封装和访问控制来增强安全性,避免外部随意操作。
三、补充Invoke()
的解释说明
Invoke() 是委托
和事件
中常用的方法,它的作用是调用委托所指向的方法。当你在委托或者事件上调用 Invoke() 时,它会执行该委托或事件注册的所有方法。
示例:
public delegate void MyDelegate(string message);public class Program
{static void Main(){MyDelegate myDelegate = new MyDelegate(PrintMessage);myDelegate.Invoke("Hello, World!"); // 调用委托的 Invoke 方法}static void PrintMessage(string message){Console.WriteLine(message);}
}
Invoke()
是隐式调用的,因此你也可以直接使用 myDelegate()
来调用它。这两者的作用是相同的。实际上,Invoke()
是自动由 C# 编译器生成的,如果你使用 myDelegate()
,编译器会在幕后调用 Invoke()
。
对于事件同理,Invoke()
方法用于触发事件并调用所有注册的事件处理程序。事件本质上是封装了委托,因此通过 Invoke()
可以调用所有订阅该事件的方法。
专栏推荐
地址 |
---|
【从零开始入门unity游戏开发之——C#篇】 |
【从零开始入门unity游戏开发之——unity篇】 |
【制作100个Unity游戏】 |
【推荐100个unity插件】 |
【实现100个unity特效】 |
【unity框架开发】 |
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
相关文章:

【从零开始入门unity游戏开发之——C#篇33】C#委托(`Delegate`)和事件(`event` )、事件与委托的区别、Invoke()的解释
文章目录 一、委托(Delegate)1、什么是委托?2、委托的基本语法3、定义自定义委托4、如何使用自定义委托5、多播委托6、C# 中的系统委托7、GetInvocationList 获取多个函数返回值8、总结 二、事件(event )1、事件是什么…...

大数据与机器学习(它们有何关系?)
想了解大数据和机器学习吗?我们将为你解释它们是什么、彼此之间有何关联,以及它们为何在数据密集型应用中如此重要。 大数据和机器学习是如何相互关联的? 大数据指的是传统存储方法无法处理的海量数据。机器学习则是计算机系统从观察结果和…...

深入浅出 Spring(一) | Spring简介、IOC理论推导、快速上手 Spring
1. spring 1.1 简介 Spring : 春天 —>给软件行业带来了春天 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。 很难想象…...

IDEA 社区版 SpringBoot不能启动
报错原因,Failed to load class [javax.servlet.Filter] <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope> </dependency>…...

职场常用Excel基础01-数据验证
大家好,excel在职场中使用非常频繁,今天和大家一起分享一下excel中数据验证相关的内容~ 在Excel中,数据验证(Data Validation)是一项非常有用的功能,它可以帮助用户限制输入到单元格中的数据类型和范围&am…...

活动预告 |【Part1】Microsoft Azure 在线技术公开课:数据基础知识
课程介绍 参加“Azure 在线技术公开课:数据基础知识”活动,了解有关云环境和数据服务中核心数据库概念的基础知识。通过本次免费的介绍性活动,你将提升在关系数据、非关系数据、大数据和分析方面的技能。 活动时间:01 月 07 日…...

RabbitMQ - 1 ( 7000 字 RabbitMQ 入门级教程 )
一: 在互联网行业,许多公司喜欢用动物命名产品或作为公司的 Logo 和吉祥物,比如腾讯的企鹅、京东的狗、美团的袋鼠、携程的海豚,而阿里更是凭借蚂蚁、飞猪、天猫、菜鸟、闲鱼、盒马等,打造了一座“动物园”。Rabbit&a…...

Docker Compose 构建 EMQX 集群 实现mqqt 和websocket
EMQX 集群化管理mqqt真香 目录 #目录 /usr/emqx 容器构建 vim docker-compose.yml version: 3services:emqx1:image: emqx:5.8.3container_name: emqx1environment:- "EMQX_NODE_NAMEemqxnode1.emqx.io"- "EMQX_CLUSTER__DISCOVERY_STRATEGYstatic"- …...

Spring 过滤器:OncePerRequestFilter 应用详解
在Web应用中,过滤器(Filter)是一个强大的工具,它可以在请求到达目标资源之前或响应返回客户端之前对请求或响应进行拦截和处理。然而,在某些情况下,我们可能希望确保过滤器逻辑在一次完整的HTTP请求中仅执行…...

3.CSS字体属性
3.1字体系列 CSS使用font-family属性定义文本的字体系列。 p{font-family:"微软雅黑"} div{font-family:Arial,"Microsoft Yahei",微软雅黑} 3.2字体大小 css使用font-size属性定义字体大小 p{ font-size:20px; } px(像素)大小是我们网页的最常用的单…...

微信小程序 单选多选radio/checkbox 纯代码分享
单选按钮 <radio-group class"radiogroup" bindchange"radioChange"> <label class"radio" wx:for"{{items}}"> <radio value"{{item.name}}" checked"{{item.checked}}" /> {{item.value}} &…...

k8s 部署meilisearch UI
https://github.com/riccox/meilisearch-ui 拉取镜像 sudo docker pull riccoxie/meilisearch-ui:latestk8s 部署 apiVersion: v1 kind: Service metadata:name: meilisearch-uinamespace: meilisearch spec:type: NodePortselector:app: meilisearch-uiports:- port: 24900…...

gitlab 还原合并请求
事情是这样的: 菜鸡从 test 分支切了个名为 pref-art 的分支出来,发布后一机灵,发现错了,于是在本地用 git branch -d pref-art 将该分支删掉了。之后切到了 prod 分支,再切出了一个相同名称的 pref-art 分支出来&…...

ChatGPT最新版本“o3”的概要
o3简介 o3于2024年12月20日发布——也就是OpenAI 12天直播的最后一天。目前处于安全性测试阶段。它是o1的继任者,旨在处理更复杂的推理任务。o3特别针对数学、科学和编程等领域进行了优化。 o3在多项基准测试中表现出色。例如,在ARC-AGI基准测试中&…...

uniapp——App下载文件,保存、打开文件(二)
uniapp如何下载文件、保存、打开文件 时光荏苒,2024即将过去! 迈向2025,祝大家新的一年工作顺利、万事如意,少一点BUG,涨一点工资…↖(ω)↗ 文章目录 uniapp如何下载文件、保存、打开文件下载文件保存并打开文件处理 …...

Postman接口测试05|实战项目笔记
目录 一、项目接口概况 二、单接口测试-登录接口:POST 1、正例 2、反例 ①姓名未注册 ②密码错误 ③姓名为空 ④多参 ⑤少参 ⑥无参 三、批量运行测试用例 四、生成测试报告 1、Postman界面生成 2、Newman命令行生成 五、token鉴权(“…...

【paddle】初次尝试
张量 张量是 paddlepaddle, torch, tensorflow 等 python 主流机器学习包中唯一通货变量,因此应当了解其基本的功能。 张量 paddle.Tensor 与 numpy.array 的转化 import paddle as paddle import matplotlib.pyplot as plt apaddle.to_t…...

01-2023年上半年软件设计师考试java真题解析
1.真题内容 在某系统中,类 Interval(间隔) 代表由下界(lower bound(边界))上界(upper bound )定义的区间。 要求采用不同的格式显示区间范围。 如[lower bound , upper bound ]、[ lower bound … upper bound ]、[ lower bou nd - upper bound &#x…...

一文讲清楚CSS3新特性
文章目录 一文讲清楚CSS3新特性1. 新增选择器特性2. 新增的样式3. 新增布局方式 一文讲清楚CSS3新特性 1. 新增选择器特性 层次选择器(div~p)选择前面有div的p元素伪类选择器 :first-of-type 表示⼀组同级元素中其类型的第⼀个元素:last-of-type 表示⼀组同级元素中其类型的最…...

系统设计案例:设计 Spotify
https://levelup.gitconnected.com/system-design-interview-question-design-spotify-4a8a79697dda 这是一道系统设计面试题,即设计 Spotify。在真正的面试中,你通常会关注应用程序的一两个主要功能,但在本文中,我想从高层次概述…...

太速科技-633-4通道2Gsps 14bit AD采集PCie卡
4通道2Gsps 14bit AD采集PCie卡 一、板卡概述 二、性能指标 板卡功能 参数 内容 ADC 芯片型号 AD9689 路数 4路ADC, 采样率 2Gsps 数据位 14bit 数字接口 JESD204B 模拟接口 交流耦合 模拟输入 1V 连接器 6路 SMA 输入阻抗 50Ω 模拟指…...

图片叠加拖拽对比展示效果实现——Vue版
图片叠加拖拽对比展示效果实现——Vue版 项目中遇见一个需求:2张图片按竖线分割,左右两侧分别展示对应图片,通过滚动条拖动对应展示图片区域;; 网上搜索了下,没有找到直接可用的组件,这里自己封装了一个次功…...

结合长短期记忆网络(LSTM)和无迹卡尔曼滤波器(UKF)的技术在机器人导航和状态估计中的应用前景
结合长短期记忆网络(LSTM)和无迹卡尔曼滤波器(UKF)的技术在机器人导航和状态估计中具有广泛的应用前景。如有滤波、导航方面的代码定制需求,可通过文末卡片联系作者获得帮助 文章目录 结合LSTM和UKF的背景结合LSTM和UKF的优势应用实例研究现状MATLAB代码示例结论结合LSTM和…...

【MATLAB APP Designer】小波阈值去噪(第一期)
代码原理及流程 小波阈值去噪是一种信号处理方法,用于从信号中去除噪声。这种方法基于小波变换,它通过将信号分解到不同的尺度和频率上来实现。其基本原理可以分为以下几个步骤: (1)小波变换:首先对含噪信…...

ClickHouse副本搭建
一. 副本概述 副本的目的主要是保障数据的高可用性,ClickHouse中的副本没有主从之分。所有的副本都是平等的。 副本写入流程: 二. 副本搭建 1. 实验环境 hadoop1(192.168.47.128) hadoop2(192.168.47.129)2. 修改配置文件 修改两台主机/etc/click…...

K3知识点
提示:文章 文章目录 前言一、顺序队列和链式队列题目 顺序队列和链式队列的定义和特性实际应用场景顺序表题目 链式队列 二、AVL树三、红黑树四、二叉排序树五、树的概念题目1左子树右子树前序遍历、中序遍历,后序遍历先根遍历、中根遍历左孩子右孩子题目…...

cocos creator 3.x版本如何添加打开游戏时首屏加载进度条
前言 项目有一个打开游戏时添加载入进度条的需求。这个功能2.X版本是自带的,不知为何在3.X版本中移除了。 实现 先说一下解决思路,就是在引擎源码加载场景的位置插入一个方法,然后在游戏入口HTML处监听即可。 1.找到对应源码脚本 在coco…...

Fama MacBeth两步法与多因子模型的回归检验
Fama MacBeth两步法与多因子模型的回归检验 – 潘登同学的因子投资笔记 本文观点来自最近学习的石川老师《因子投资:方法与实践》一书 文章目录 Fama MacBeth两步法与多因子模型的回归检验 -- 潘登同学的因子投资笔记 多因子回归检验时序回归检验截面回归检验Fama–…...

IDEA 搭建 SpringBoot 项目之配置 Maven
目录 1?配置 Maven 1.1?打开 settings.xml 文件1.2?配置本地仓库路径1.3?配置中央仓库路径1.4?配置 JDK 版本1.5?重新下载项目依赖 2?配置 idea 2.1?在启动页打开设置2.2?配置 Java Compiler2.3?配置 File Encodings2.4?配置 Maven2.5?配置 Auto Import2.6?配置 C…...

node.js之---事件循环机制
事件循环机制 Node.js 事件循环机制(Event Loop)是其核心特性之一,它使得 Node.js 能够高效地处理大量并发的 I/O 操作。Node.js 基于 非阻塞 I/O,使用事件驱动的模型来实现异步编程。事件循环是 Node.js 实现异步编程的基础&…...