C#中委托与事件
一、委托
在面向对象中,我们可以将任何数据类型作为参数传递给方法,能否将一个方法作为参数传递给另一个方法?C#中通过委托可以实现将方法作为参数进行传递。
1.1概念
-
委托是一种引用类型,它可以用于封装并传递方法作为参数。委托可以理解为是一个指向方法的**“指针”,它允许将方法作为参数传递给其他方法或存储在数据结构中,然后稍后调用这些方法。(委托可以看作时函数的容器)**
-
委托与类同级别
-
委托需要有与之签名相同的方法与之对应
1.2目的作用
委托的目的就是将方法当作参数进行传递 ;
封装变化的请求
1.3定义方法的委托
- 委托:将方法作为参数进行传递 方法是有签名( 签名:返回值类型 ,参数列表(参数的个数 ,参数类型))。
- 委托需要和方法有相同的签名 也就是说委托和方法 要参数相同 返回值相同 你才可以引用这个方法。
- 访问修饰符:public protected internal…委托的访问修饰符一般都定义成public,因为我们的程序可能会涉及到跨程序集调用。
- 委托和类是平级的 不要定义在类里面
//01、声明一个委托public delegate void DelTest();internal class Program{static void Main(string[] args){ //03.创建委托(对象),两种方式DelTest delTest = new DelTest(Test);//方法1//或者 创建委托(变量)可以省略new 关键字,直接将要传递的方法赋值即可,注意,方法在赋值的时候,不要写();//虽然我们没有创建委托对象(new出来的才叫对象),但是编译器在编译代码的时候还是去帮助我们new了委托对象DelTest delTest1 = Test;//方法2//04.调用委托,两种方式:delTest();//委托名字()delTest.Invoke();//调用(委托)对象里面存在的方法,委托名字.Invoke();// delTest1();}//02.定义方法public static void Test(){Console.WriteLine("没有参数没有返回值");}}
}
1.4匿名方法创建委托
- 当使用的方法只使用一次时,没必要单独创建一个方法,直接定义委托对象时创建匿名方法
DelTest delTest = delegate () { Console.WriteLine("没有返回值没有参数"); };//后面加封号
delTest();
- 匿名方法允许你在需要委托的地方直接定义方法体,而不需要显式地创建一个单独的方法。
//没有参数 没有返回值
DelTest1 delTest1 = delegate () { Console.WriteLine("没有参数 没有返回值"); };
delTest1();//没有参数 有返回值
DelTest2 delTest2 = delegate () { return "没有参数 有返回值"; };
Console.WriteLine(delTest2());//有参数 没有返回值
DelTest3 delTest3 = delegate (string str) { Console.WriteLine("有参数 没有返回值"+str); };
delTest3("delTest3");//有参数 有返回值
DelTest4 delTest4 = delegate (int n1,int n2) { return n1 + n2; };
Console.WriteLine(delTest4(10,20));//没有参数 没有返回值
public delegate void DelTest1();
//没有参数 有返回值
public delegate string DelTest2();
//有参数 没有返回值
public delegate void DelTest3(string name);
//有参数 有返回值
public delegate int DelTest4(int n1,int n2);
1.5委托+Lambda方式简化创建(常用)
- Lambda格式
(parameters参数) => expression表达式
其中,parameters是传递给委托的参数列表,可以是空的或包含多个参数;expression是一个表达式,它将计算并返回给委托调用者的值。
2. 创建委托:匿名函数+Lambda+委托
1.没有参数 没有返回值
1.1匿名方法
DelTest1 delTest1 = delegate () { Console.WriteLine("没有参数 没有返回值"); };
delTest1();1.2lambda1
DelTest1 delTest11 = () => { Console.WriteLine("没有参数 没有返回值"); };
delTest11();
- 图中箭头指向的地方对应位置作用一样
2.有参数 没有返回值DelTest3 delTest3 = delegate (string name) { Console.WriteLine(name); };
delTest3("有参数 没有返回值");DelTest3 delTest31 = (name) => { Console.WriteLine(name); };当你是一个参数的时候可以去掉() 但是你没有参数列表的时候需要加上()
DelTest3 delTest32 = name => { Console.WriteLine(name); };
delTest31("有参数 没有返回值");
1.6Action/Func创建委托
Action委托本身不包含返回值,主要封装那些没有返回结果的函数。Func委托则可用于需要返回值的情况。
//00、自己定义的泛型委托
DelTest4<int> delTest4 = n1 => { Console.WriteLine(n1); };
delTest4(230);
泛型委托
public delegate void DelTest4<T>(T t);//01、 Action
//Action就是系统帮我们定义好的一个没有参数没有返回值的委托
//委托 没有参数没有返回值 直接使用 不需要再去定义一个委托
Action action = () => { Console.WriteLine("a"); };
//调用内置委托和调用普通委托一样
action();//TResult 代表返回值的类型
//第一个int 表示参数一为int类型
//第二个int 表示参数二为int类型
//第三个int 表示参数三为int类型
//第四个string 表示参数四为返回值为string
//最后一个永远是返回值
Func<int, int, int,string> func = (n1, n2,n3) => { return "张三的成绩为"+n1+n2+n3; };
Console.WriteLine(func(100, 200,300));
1.7多播委托
多播委托时为了让同一个委托对象,一行调用多个方法同时执行,如游戏中,一个人物按下开火键(委托对象),手中的冲锋枪和步枪同时开火,如果不使用多播委托,需要先进行武器切换选择其中一个进行开火。
Action action = M1;
//如果使用 多播委托 必须在调用之前进行绑定//多播委托和字符串拼接类似 每拼接一次 在实际的内存中 都会创建一个新的委托对象
//我们看到的是一个委托对象指向了多个方法
//实际上每次拼接后 创建出来新的对象结果action += M2;
action += M3;
action += M4;
action += M5;//从结果来看 多播委托的调用是顺序的 ,但是不代表多播委托 的调用就是顺序的 微软随时可以改变顺序//使用队列action();static void M1()
{Console.WriteLine("我是第1个方法");
}
static void M2()
{Console.WriteLine("我是第2个方法");
}
static void M3()
{Console.WriteLine("我是第3个方法");
}
static void M4()
{Console.WriteLine("我是第4个方法");
}
static void M5()
{Console.WriteLine("我是第5个方法");
}
1.8委托注意事项
委托不能为空,为空的委托不能调用,报错;
空委托不能调用,所以使用委托前要进行判断:
if(attack!=null)
{attack();//调用委托
}
上面的方式过于繁琐,可以简化:
//无参数
attack?.Invoke();
//有参数
attack?.Invoke(a,b);
二、事件
1.概念
C#中的事件是一种特别的委托,它可以被多个方法订阅,并且在特定情况下触发这些方法的执行。
2.使用
2.1事件的定义
- 声明事件:在C#中,事件通常通过event关键字声明。它定义了一个可以由多个方法订阅的特殊类型的委托。例如,声明一个没有返回值且不带参数的事件:
public event Action MyEvent;。
- 委托类型:事件的背后是一个委托类型,它定义了订阅事件的方法的签名。例如,使用内置的Action委托或自定义委托来定义事件。
- 访问修饰符:事件的声明可以包含访问修饰符,如public或private,以控制对事件的访问级别。
2.2事件的订阅
- 订阅事件:订阅事件是通过+=运算符完成的。例如,
myObject.MyEvent += MyMethod;
,其中MyMethod是响应事件的方法。 - 触发事件:当特定条件满足时,事件会被触发。这通常是在类的内部,通过
EventName?.Invoke()
形式调用。例如,MyEvent?.Invoke();
会触发所有订阅了MyEvent的方法。 - 取消订阅:使用-=运算符可以取消事件订阅,如
myObject.MyEvent -= MyMethod;
。
3.案例
委托会遇到的问题
//问题1:我们通过委托来实现事件 由于委托在定义的时候,一般都定义成public 这样就导致了 用户端 不给委托赋值的情况了,也可以直接调用
//问题2:由于委托本质上就是一个数据类型 所以如果赋值为null的话 就会把之前的值覆盖掉 变成一个null对象
//01、直接调用没赋值的委托,可以调用,但是这是不正确的
musicPlayer.BeforePlayMusic();
musicPlayer.AfterPlayMusic();
//02、直接调用事件
//外界不允许直接调用事件 因为事件的语法规定了这一点
musicPlayer.BeforePlayMusic();
musicPlayer.AfterPlayMusic();
//通过音乐播放器 讲解事件在程序中是啥//实例化音乐播放器类
MusicPlayer musicPlayer = new MusicPlayer();
musicPlayer.BeforePlayMusic += () => { Console.WriteLine("加载我们的歌词"); };
musicPlayer.AfterPlayMusic += () => { Console.WriteLine("跳转下一曲"); };musicPlayer.BeforePlayMusic += null;
musicPlayer.AfterPlayMusic += null;//用户单击了播放按钮
musicPlayer.StartMusic();
musicPlayer.EndMusic();//通俗的说:就是发生【某个变化】的是时候 触发某段代码
//没有用到事件---- - 没有发生变化
//需求1: 我希望在播放音乐之前 ,能够加载我们的歌词
//需求2:我希望播放音乐结束后 能够自动跳转下一曲
//需求应该交给用户 :委托
class MusicPlayer
{//01、声明两个事件public event Action BeforePlayMusic;public event Action AfterPlayMusic;//播放音乐 不能给别人调用private void MusicPlay(){//判断用户是否是会员//判断用户播放的音乐是否是会员音乐//去数据量里面加载音乐文件//开始播放.....//加载我们的歌词BeforePlayMusic();Console.WriteLine("音乐播放中");}//点击按钮播放音乐public void StartMusic(){this.MusicPlay();Thread.Sleep(2000);}public void EndMusic(){Console.WriteLine("音乐关闭中");AfterPlayMusic();//跳转下一曲}}
三、委托与事件区别
- 事件只能在类的内部进行触发,不能在类的外部进行触发。而委托在类的内部和外部都可触发;
- 事件是一个特殊的委托,查看反编译工具之后的代码,发现事件是一个 private 委托
委托测试:
//03、触发类内部定义的委托
Test te = new Test();
te.SayHello();
//04在类外部进行委托对象赋值并处罚
Test.Foo fo= () => { Console.WriteLine("02、类外部触发的委托!"); };
fo();//创建类
class Test
{//01定义委托 public delegate void Foo();public void SayHello(){//02在类内部对委托对象进行赋值Foo fo=()=> { Console.WriteLine("01、类内部触发的委托!"); };fo();}}
事件测试:
//03实例化类并触发事件
Test te = new Test();
te.SayHello();class Test
{
//01、定义事件public event Foo Foo1;public void SayHello(){//02、方法绑定Foo1+= () => { Console.WriteLine("类内部触发的事件!"); };Foo1();}}
//02类外部进行绑定方法,并触发
te.Foo1 += () => { Console.WriteLine("a2"); };
te.Foo1();class Test
{//01定义事件public event Foo Foo1;
}
报错,事件无法在类的外部进行触发
相关文章:

C#中委托与事件
一、委托 在面向对象中,我们可以将任何数据类型作为参数传递给方法,能否将一个方法作为参数传递给另一个方法?C#中通过委托可以实现将方法作为参数进行传递。 1.1概念 委托是一种引用类型,它可以用于封装并传递方法作为参数。委…...

通用后台管理(二)——项目搭建
目录 前言 一、安装vue-cli依赖 1、使用yarn下载vue-cli 2、使用npm下载 3、检查一下是否下载成功 二、创建项目 1、创建项目,my-app是项目名称 2、 这里选择vue 2,蓝色表示选中的。 3、启动项目 三、下载项目依赖 四、配置项目 1、修改esli…...

多模态大模型之达摩院通义MPLUG
引言 随着人工智能技术的飞速发展,多模态技术逐渐成为研究的热点。它结合了文本、图像、声音等多种数据类型,为机器理解世界提供了更丰富的视角。本文根据严明老师的达摩院通义MPLUG多模态预训练技术分享,及其在电商等行业的应用实践&#x…...
文章翻译记录
以 PINN 为基础,我们开发了一个框架,用于在不同震源位置和速度模型下进行地震建模。本研究的显著贡献包括: 1. 为了提高网络对不同速度模型的泛化能力,必须将速度变量 vp 作为系统的输入参数。本研究从监督学习中汲取灵感…...

C++ 语法习题(2)
第三讲 循环语句 1.偶数 编写一个程序,输出 1 到 100之间(包括 1 和 100)的全部偶数。 输入格式 无输入。 输出格式 输出全部偶数,每个偶数占一行。 输入样例 No input输出样例 2 4 6 ... 100 参考代码: #include <i…...
使用Gstreamer时遇到WARNING: erroneous pipeline: no element “x264enc“(亲测有效)
WARNING: erroneous pipeline: no element “x264enc” 解决: 我下了gstreamer1.0-plugins-ugly包就解决了 sudo apt install -y gstreamer1.0-plugins-ugly...

SAP 新增移动类型简介
在SAP系统中新增移动类型的过程涉及多个步骤,包括复制现有的移动类型、调整科目设置以及进行必要的测试。以下是新增移动类型的一般步骤和关键点: 复制现有的移动类型: 使用事务代码OMJJ进入移动类型维护界面。 勾选移动类型 这里不填写移动类型,然后直接下…...
SQL性能优化策略
发现问题 通过业务监控发现慢SQL或接口响应延迟。利用性能分析工具定位问题。 定位SQL语句 使用监控工具确定影响性能的SQL语句和表。 SQL查询变慢原因 索引失效:查询未使用索引或索引效率低。多表连接:JOIN操作导致性能下降。查询字段过多…...
代码随想录第四十八天 | 198.打家劫舍, 213.打家劫舍II,337.打家劫舍III
198.打家劫舍 看完想法:这里的偷/不偷,和背包问题中的放/不放感觉是一个道理,所以在dp递推公式中仍旧使用max(dp[i-2] nums[i], dp[i-1]) int rob(vector<int>& nums) {vector<int> dp(nums.size()1,0);if(nums.size()0) …...
C#实用的工具类库
Masuit.Tools Masuit.Tools大都是静态类,加密解密,反射操作,树结构,文件探测,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载…...

首席数据官CDO证书报考指南:方式、流程、适考人群与考试难度
在信息泛滥的今天,数据已转变为企业不可或缺的宝贵资源。 面对海量的信息,如何提炼出价值,为企业带来实质性的收益?首席数据官(CDO)认证的出现正是为了满足这一需求,它不仅是个人专业能力的体现…...

数据库基础复习
数据库简介 关系型数据库:Mysql 、Oracle 、SqlServer.... DB2 达梦 非关系型数据库:Redis 、MongoDB... MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管…...

探索AI大模型(LLM)减少幻觉的三种策略
大型语言模型(LLM)在生成文本方面具有令人瞩目的能力,但在面对陌生概念和查询时,它们有时会输出看似合理却实际错误的信息,这种现象被称为“幻觉”。近期的研究发现,通过策略性微调和情境学习、检索增强等方…...

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第一篇 嵌入式Linux入门篇-第十三章 Linux连接档
i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...

鸿蒙语言基础类库:【@ohos.uri (URI字符串解析)】
URI字符串解析 说明: 本模块首批接口从API version 8开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 导入…...
JavaScript---new Map()用法
new Map 创建 Map 对象设置键值对获取值检查键是否存在键值对数量删除键值对清空所有键值对迭代 Map 在JavaScript中,Map 是一个构造函数,用于创建 Map 对象,它可以存储键值对集合。与普通的对象不同,Map 的键可以是任何类型的值&…...
【数据基础】— 基于Go1.19的站点模板爬虫的实现
目录 1. 定义目标站点 2. 使用Go的库 3. 发送HTTP请求 4. 解析HTML并提取数据 5. 存储数据 6. 并发处理 示例代码 基于Go 1.19的站点模板爬虫实现通常涉及几个关键步骤:定义目标站点、解析HTML页面、提取所需数据、存储数据以及可能的并发处理。下面我将详细…...

Angular进阶之九: JS code coverage是如何运作的
环境准备 需要用到的包 node 18.16.0# Javascript 代码编辑"babel/core": "^7.24.7","babel/preset-env": "^7.24.7","babel-loader": "^9.1.3",# 打包时使用的 module, 给代码中注入新的方法# http…...
el-table 鼠标移入更改悬停背景颜色
鼠标悬停时需要更改当前行背景颜色,一开始写的颜色会改变,但是一闪而过就没了 这是因为移入移出的动画效果导致的 .el-table__body {.el-table__row:hover {background-color: pink !important;}} 更改为后面的代码,就可以了 .el-table__…...

【《无主之地3》风格角色渲染在Unity URP下的实现_角色渲染(第四篇) 】
文章目录 概要描边问题外秒变分叉解决办法1:测试效果如下:外秒变分叉解决办法2:URP管线下PBR渲染源码关键词解释:完整shader代码如下:URP管线下二次元皮肤渲染源码URP管线下二次元头发渲染源码简要介绍文章的目的、主要内容和读者将获得的知识。 概要 提示:《无主之地3》…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...

Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
StarRocks 全面向量化执行引擎深度解析
StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计,相比传统行式处理引擎(如MySQL),性能可提升 5-10倍。以下是分层拆解: 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...
el-amap-bezier-curve运用及线弧度设置
文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...
SpringCloud优势
目录 完善的微服务支持 高可用性和容错性 灵活的配置管理 强大的服务网关 分布式追踪能力 丰富的社区生态 易于与其他技术栈集成 完善的微服务支持 Spring Cloud 提供了一整套工具和组件来支持微服务架构的开发,包括服务注册与发现、负载均衡、断路器、配置管理等功能…...