【从零开始入门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。在真正的面试中,你通常会关注应用程序的一两个主要功能,但在本文中,我想从高层次概述…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
