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

C#中委托与事件

一、委托

在面向对象中,我们可以将任何数据类型作为参数传递给方法,能否将一个方法作为参数传递给另一个方法?C#中通过委托可以实现将方法作为参数进行传递。

1.1概念

  1. 委托是一种引用类型,它可以用于封装并传递方法作为参数。委托可以理解为是一个指向方法的**“指针”,它允许将方法作为参数传递给其他方法或存储在数据结构中,然后稍后调用这些方法。(委托可以看作时函数的容器)**

  2. 委托与类同级别
    在这里插入图片描述

  3. 委托需要有与之签名相同的方法与之对应

1.2目的作用

委托的目的就是将方法当作参数进行传递 ;
封装变化的请求

1.3定义方法的委托

  1. 委托:将方法作为参数进行传递 方法是有签名( 签名:返回值类型 ,参数列表(参数的个数 ,参数类型))。
  2. 委托需要和方法有相同的签名 也就是说委托和方法 要参数相同 返回值相同 你才可以引用这个方法。
  3. 访问修饰符:public protected internal…委托的访问修饰符一般都定义成public,因为我们的程序可能会涉及到跨程序集调用。
  4. 委托和类是平级的 不要定义在类里面
//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匿名方法创建委托

  1. 当使用的方法只使用一次时,没必要单独创建一个方法,直接定义委托对象时创建匿名方法
DelTest delTest = delegate () { Console.WriteLine("没有返回值没有参数"); };//后面加封号
delTest();
  1. 匿名方法允许你在需要委托的地方直接定义方法体,而不需要显式地创建一个单独的方法。
//没有参数 没有返回值
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方式简化创建(常用)

  1. Lambda格式
(parameters参数) => expression表达式

其中,parameters是传递给委托的参数列表,可以是空的或包含多个参数;expression是一个表达式,它将计算并返回给委托调用者的值。
2. 创建委托:匿名函数+Lambda+委托

1.没有参数 没有返回值
1.1匿名方法
DelTest1 delTest1 = delegate () { Console.WriteLine("没有参数 没有返回值"); };
delTest1();1.2lambda1
DelTest1 delTest11 = () => { Console.WriteLine("没有参数 没有返回值"); };
delTest11();
  1. 图中箭头指向的地方对应位置作用一样
    在这里插入图片描述
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事件的定义

  1. 声明事件:在C#中,事件通常通过event关键字声明。它定义了一个可以由多个方法订阅的特殊类型的委托。例如,声明一个没有返回值且不带参数的事件:public event Action MyEvent;。
  2. 委托类型:事件的背后是一个委托类型,它定义了订阅事件的方法的签名。例如,使用内置的Action委托或自定义委托来定义事件。
  3. 访问修饰符:事件的声明可以包含访问修饰符,如public或private,以控制对事件的访问级别。

2.2事件的订阅

  1. 订阅事件:订阅事件是通过+=运算符完成的。例如,myObject.MyEvent += MyMethod;,其中MyMethod是响应事件的方法。
  2. 触发事件:当特定条件满足时,事件会被触发。这通常是在类的内部,通过EventName?.Invoke()形式调用。例如,MyEvent?.Invoke();会触发所有订阅了MyEvent的方法。
  3. 取消订阅:使用-=运算符可以取消事件订阅,如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();//跳转下一曲}}

三、委托与事件区别

  1. 事件只能在类的内部进行触发,不能在类的外部进行触发。而委托在类的内部和外部都可触发;
  2. 事件是一个特殊的委托,查看反编译工具之后的代码,发现事件是一个 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#中委托与事件

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

通用后台管理(二)——项目搭建

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

多模态大模型之达摩院通义MPLUG

引言 随着人工智能技术的飞速发展&#xff0c;多模态技术逐渐成为研究的热点。它结合了文本、图像、声音等多种数据类型&#xff0c;为机器理解世界提供了更丰富的视角。本文根据严明老师的达摩院通义MPLUG多模态预训练技术分享&#xff0c;及其在电商等行业的应用实践&#x…...

文章翻译记录

以 PINN 为基础&#xff0c;我们开发了一个框架&#xff0c;用于在不同震源位置和速度模型下进行地震建模。本研究的显著贡献包括&#xff1a; 1. 为了提高网络对不同速度模型的泛化能力&#xff0c;必须将速度变量 vp 作为系统的输入参数。本研究从监督学习中汲取灵感&#xf…...

C++ 语法习题(2)

第三讲 循环语句 1.偶数 编写一个程序&#xff0c;输出 1 到 100之间&#xff08;包括 1 和 100&#xff09;的全部偶数。 输入格式 无输入。 输出格式 输出全部偶数&#xff0c;每个偶数占一行。 输入样例 No input输出样例 2 4 6 ... 100 参考代码: #include <i…...

使用Gstreamer时遇到WARNING: erroneous pipeline: no element “x264enc“(亲测有效)

WARNING: erroneous pipeline: no element “x264enc” 解决&#xff1a; 我下了gstreamer1.0-plugins-ugly包就解决了 sudo apt install -y gstreamer1.0-plugins-ugly...

SAP 新增移动类型简介

在SAP系统中新增移动类型的过程涉及多个步骤,‌包括复制现有的移动类型、‌调整科目设置以及进行必要的测试。‌以下是新增移动类型的一般步骤和关键点:‌ 复制现有的移动类型:‌ 使用事务代码OMJJ进入移动类型维护界面。‌ 勾选移动类型 这里不填写移动类型,然后直接下…...

SQL性能优化策略

发现问题 通过业务监控发现慢SQL或接口响应延迟。利用性能分析工具定位问题。 定位SQL语句 使用监控工具确定影响性能的SQL语句和表。 SQL查询变慢原因 索引失效&#xff1a;查询未使用索引或索引效率低。多表连接&#xff1a;JOIN操作导致性能下降。查询字段过多&#xf…...

代码随想录第四十八天 | 198.打家劫舍, 213.打家劫舍II,337.打家劫舍III

198.打家劫舍 看完想法&#xff1a;这里的偷/不偷&#xff0c;和背包问题中的放/不放感觉是一个道理&#xff0c;所以在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大都是静态类&#xff0c;加密解密&#xff0c;反射操作&#xff0c;树结构&#xff0c;文件探测&#xff0c;权重随机筛选算法&#xff0c;分布式短id&#xff0c;表达式树&#xff0c;linq扩展&#xff0c;文件压缩&#xff0c;多线程下载&#xf…...

首席数据官CDO证书报考指南:方式、流程、适考人群与考试难度

在信息泛滥的今天&#xff0c;数据已转变为企业不可或缺的宝贵资源。 面对海量的信息&#xff0c;如何提炼出价值&#xff0c;为企业带来实质性的收益&#xff1f;首席数据官&#xff08;CDO&#xff09;认证的出现正是为了满足这一需求&#xff0c;它不仅是个人专业能力的体现…...

数据库基础复习

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

探索AI大模型(LLM)减少幻觉的三种策略

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

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第一篇 嵌入式Linux入门篇-第十三章 Linux连接档

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...

鸿蒙语言基础类库:【@ohos.uri (URI字符串解析)】

URI字符串解析 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 导入…...

JavaScript---new Map()用法

new Map 创建 Map 对象设置键值对获取值检查键是否存在键值对数量删除键值对清空所有键值对迭代 Map 在JavaScript中&#xff0c;Map 是一个构造函数&#xff0c;用于创建 Map 对象&#xff0c;它可以存储键值对集合。与普通的对象不同&#xff0c;Map 的键可以是任何类型的值&…...

【数据基础】— 基于Go1.19的站点模板爬虫的实现

目录 1. 定义目标站点 2. 使用Go的库 3. 发送HTTP请求 4. 解析HTML并提取数据 5. 存储数据 6. 并发处理 示例代码 基于Go 1.19的站点模板爬虫实现通常涉及几个关键步骤&#xff1a;定义目标站点、解析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&#xff0c; 给代码中注入新的方法# http…...

el-table 鼠标移入更改悬停背景颜色

鼠标悬停时需要更改当前行背景颜色&#xff0c;一开始写的颜色会改变&#xff0c;但是一闪而过就没了 这是因为移入移出的动画效果导致的 .el-table__body {.el-table__row:hover {background-color: pink !important;}} 更改为后面的代码&#xff0c;就可以了 .el-table__…...

【《无主之地3》风格角色渲染在Unity URP下的实现_角色渲染(第四篇) 】

文章目录 概要描边问题外秒变分叉解决办法1:测试效果如下:外秒变分叉解决办法2:URP管线下PBR渲染源码关键词解释:完整shader代码如下:URP管线下二次元皮肤渲染源码URP管线下二次元头发渲染源码简要介绍文章的目的、主要内容和读者将获得的知识。 概要 提示:《无主之地3》…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...