c#url拼接方法名_C# 从1到Core委托与事件

委托与事件在C#1.0的时候就有了,随着C#版本的不断更新,有些写法和功能也在不断改变。本文温故一下这些改变,以及在NET Core中关于事件的一点改变。

一、C#1.0 从委托开始

1. 基本方式

  什么是委托,就不说概念了,用例子说话。

  某HR说他需要招聘一个6年 .NET5 研发经验的“高级”工程师,他想找人(委托)别人把这条招聘消息发出去。这样的HR很多,所以大家定义了一个通用的发消息规则:

public delegate string SendDelegate(string message);

  这就像一个接口的方法,没有实际的实现代码,只是定义了这个方法有一个string的参数和返回值。所有想发招聘消息的HR只要遵守这样的规则即可。

委托本质上是一个类,所以它可以被定义在其他类的内部或外部,根据实际引用关系考虑即可。本例单独定义在外部。

为HR定义了一个名为HR的类:

public class HR{    public SendDelegate sendDelegate;    public void SendMessage(string msg)    {        sendDelegate(msg);    }}

  HR有一个SendDelegate类型的成员,当它需要发送消息(SendMessage)的时候,只需要调用这个sendDelegate方法即可。而不需要实现这个方法,也不需要关心这个方法是怎么实现的。

当知道这个HR需要发送消息的时候,猎头张三接了这个帮忙招人的工作。猎头的类为Sender,他有一个用于发送消息的方法Send,该方法恰好符合众人定义的名为SendDelegate的发消息规则。这有点像实现了一个接口方法,但这里不要求方法名一致,只是要求方法的签名一致。

public class Sender{    public Sender(string name)    {        this.senderName = name;    }    private readonly string senderName;    public string Send(string message)    {        string serialNumber = Guid.NewGuid().ToString();        Console.WriteLine(senderName + " sending....");        Thread.Sleep(2000);        Console.WriteLine("Sender: " + senderName + " , Content: " + message + ", Serial Number: "  + serialNumber);        return serialNumber;    }}

猎头帮助HR招人的逻辑如下:

public void Test(){    //一个HR    HR hr = new HR();    //猎头张三来监听,听到HR发什么消息后立刻传播出去    Sender senderZS = new Sender("张三");    hr.sendDelegate = senderZS.Send;    //HR递交消息    hr.SendMessage("Hello World");}

猎头将自己的发消息方法“赋值”给了HR的SendDelegate方法,为什么可以“赋值”? 因为二者都遵守SendDelegate规则。 就像A和B两个变量都是int类型的时候,A可以赋值给B一样。

这就是一个简单的委托过程,HR将招人的工作委托给了猎头,自己不用去做招人的工作。

但经常一个招聘工作经常会有多个猎头接单,那就有了多播委托。

2. 多播委托

 看一下下面的代码:

public void Test(){    //一个HR    HR hr = new HR();    //猎头张三来监听,听到HR发什么消息后立刻传播出去    Sender senderZS = new Sender("张三");    hr.sendDelegate = senderZS.Send;    //快嘴李四也来了    Sender senderLS = new Sender("李四");    hr.sendDelegate += senderLS.Send;    //HR递交消息    hr.SendMessage("Hello World");}

与之前的代码改变不大, 只是添加了李四的方法绑定,这样HR发消息的时候,张三和李四都会发出招人的消息。

这里要注意李四绑定方法的时候,用的是+=而不是=,就像拼接字符串一样,是拼接而不是赋值,否则会覆盖掉之前张三的方法绑定。

对于第一个绑定的张三,可以用=号也可以用+=(记得之前好像第一个必须用=,实验了一下现在二者皆可)。

这同时也暴露了一些问题:

  • 如果后面的猎头接单的时候不小心(故意)用了=号, 那么最终前面的人的绑定都没有了,那么他将独占这个HR客户,HR发出的消息只有他能收到。

  • 可以偷偷的调用猎头的hr.sendDelegate

public void Test(){    //一个HR    HR hr = new HR();    //大嘴张三来监听,听到HR发什么消息后立刻传播出去    Sender senderZS = new Sender("张三");    //hr.sendDelegate -= senderZS.Send; //即使未进行过+=  直接调用-=,也不会报错    hr.sendDelegate += senderZS.Send;    //快嘴李四也来了    Sender senderLS = new Sender("李四");    hr.sendDelegate += senderLS.Send;    //移除    //hr.sendDelegate -= senderZS.Send;    //风险:注意上面用的符号是+=和-=   如果使用=,则是赋值操作,    //例如下面的语句会覆盖掉之前所有的绑定    //hr.sendDelegate = senderWW.Send;    //HR递交消息    hr.SendMessage("Hello World");    //风险:可以偷偷的以HR的名义偷偷的发了一条消息    sendDelegate应该只能由HR调用       hr.sendDelegate("偷偷的发一条");}

3. 通过方法避免风险

  很自然想到采用类似Get和Set的方式避免上面的问题。既然委托可以像变量一样赋值,那么也可以通过参数来传值,将一个方法作为参数传递。

public class HRWithAddRemove    {        private SendDelegate sendDelegate;        public void AddDelegate(SendDelegate sendDelegate)        {            this.sendDelegate += sendDelegate; //如果需要限制最多绑定一个,此处可以用=号        }        public void RomoveDelegate(SendDelegate sendDelegate)        {            this.sendDelegate -= sendDelegate;        }        public void SendMessage(string msg)        {            sendDelegate(msg);        }    }

经过改造后的HR,SendDelegate方法被设置为了private,之后只能通过Add和Remove的方法进行方法绑定。

4.模拟多播委托机制

通过上面委托的表现来看,委托就像是保存了一个相同方法名的集合 List ,可以向集合中添加或移除方法,当调用这个委托的时候,会逐一调用该集合中的各个方法。

例如下面的代码( 注意这里假设SendDelegate只对应一个方法 ):

public class HR1{    public void Delegate(SendDelegate sendDelegate)    {        sendDelegateList = new List { sendDelegate }; //对应=    }    public void AddDelegate(SendDelegate sendDelegate)    {        sendDelegateList.Add(sendDelegate); //对应+=    }    public void RomoveDelegate(SendDelegate sendDelegate)    {        sendDelegateList.Remove(sendDelegate);//对应-=    }    public List sendDelegateList;    public void SendMessage(string msg)    {        foreach (var item in sendDelegateList)        {            item(msg);        }    }}

二、C#1.0 引入事件

  1.简单事件

  如果既想使用-=和+=的方便,又想避免相关功能开闭的风险怎么办呢?可以使用事件:

public class HRWithEvent    {        public event SendDelegate sendDelegate;        public void SendMessage(string msg)        {            sendDelegate(msg);        }    }

 只是将SendDelegate前面添加了一个event标识,虽然它被设置为public,但如下代码却会给出错误提示: 事件“HRWithEvent.sendDelegate”只能出现在 += 或 -= 的左边(从类型“HRWithEvent”中使用时除外) 

 hr.sendDelegate = senderZS.Send; hr.sendDelegate("偷偷的发一条");

  2.事件的访问器模式

   上文为委托定义了Add和Remove方法,而事件支持这样的访问器模式,例如如下代码:

public class CustomerWithEventAddRemove    {        private event SendDelegate sendDelegate;        public event SendDelegate SendDelegate        {            add { sendDelegate += value; }            remove { sendDelegate -= value; }        }        public void SendMessage(string msg)        {            sendDelegate(msg);        }    }

 可以像使用Get和Set方法一样,对事件的绑定与移除进行条件约束。 

  3. 控制绑定事件的执行

  当多个委托被绑定到事件之后,如果想精确控制各个委托的运行怎么办,比如返回值(虽然经常为void)、异常处理等。

第一章第4节通过一个List 模拟了多播委托的绑定。 会想到如果真能循环调用一个个已绑定的委托,就可以精确的进行控制了。那么这里说一下这样的方法:

public class HRWithEvent    {        public event SendDelegate sendDelegate;        public void SendMessage(string msg)        {            //sendDelegate(msg);  此处不再一次性调用所有            if (sendDelegate != null)            {                Delegate[] delegates = sendDelegate.GetInvocationList(); //获取所有已绑定的委托                foreach (var item in delegates)                {                    ((SendDelegate)item).Invoke(msg); //逐一调用                }            }        }    }

 这里通过Invoke方法逐一调用各个Delegate,从而实现对每一个Delegate的调用的控制。若需要异步调用,则可以通过BeginInvoke方法实现(.NET Core之后不再支持此方法,后面会介绍。)

((SendDelegate)item).BeginInvoke(msg,null,null);

  4. 标准的事件写法

  .NET 事件委托的标准签名是:

void OnEventRaised(object sender, EventArgs args);

  返回类型为 void。 事件基于委托,而且是多播委托。 参数列表包含两种参数:发件人和事件参数。 sender 的编译时类型为 System.Object

  第二种参数通常是派生自 System.EventArgs 的类型(.NET Core 中已不强制要求继承自System.EventArgs,后面会说到)

  将上面的例子修改一下,改成标准写法,大概是下面代码的样子:

public class HRWithEventStandard{    public delegate void SendEventHandler(object sender, SendMsgArgs e);    public event SendEventHandler Send;    public void SendMessage(string msg)    {        var arg = new SendMsgArgs(msg);        Send(this,arg); //arg.CancelRequested 为最后一个的值   因为覆盖    }}public class SendMsgArgs : EventArgs{    public readonly string Msg = string.Empty;    public bool CancelRequested { get; set; }    public SendMsgArgs(string msg)    {        this.Msg = msg;    }}

三、随着C#版本改变

1. C#2.0 泛型委托

  C#2.0 的时候,随着泛型出现,支持了泛型委托,例如,在委托的签名中可以使用泛型,例如下面代码

public delegate string SendDelegate(T message);

这样的委托适用于不同的参数类型,例如如下代码(注意使用的时候要对应具体的类型)

public delegate string SendDelegate(T message);public class HR1{    public SendDelegate<string> sendDelegate1;    public SendDelegate<int> sendDelegate2;    public SendDelegate sendDelegate3;}public static class Sender1{    public static string Send1(string msg)    {        return "";    }    public static string Send2(int msg)    {        return "";    }}    public class Test{    public void TestDemo()    {        HR1 hr1 = new HR1();        hr1.sendDelegate1 = Sender1.Send1; // 注意使用的时候要对应具体的类型        hr1.sendDelegate2 = new SendDelegate<int>(Sender1.Send2);        hr1.sendDelegate3 = delegate (DateTime dateTime) { return dateTime.ToLongDateString(); };    }}

2. C#2.0 delegate运算符

delegate 运算符创建一个可以转换为委托类型的匿名方法:

例如上例中这样的代码:

hr1.sendDelegate3 = delegate (DateTime dateTime) { return dateTime.ToLongDateString(); };

3. C#3.0 Lambda 表达式

从 C# 3 开始,lambda 表达式提供了一种更简洁和富有表现力的方式来创建匿名函数。 使用 => 运算符构造 lambda 表达式,

例如“delegate运算符”的例子可以简化为如下代码:

hr1.sendDelegate3 = (dateTime) => { return dateTime.ToLongDateString(); };

4.C#3,NET Framework3.5,Action 、Func、Predicate

Action 、Func、Predicate本质上是框架为我们预定义的委托,在上面的例子中,我们使用委托的时候,首先要定义一个委托类型,然后在实际使用的地方使用,而使用委托只要求方法名相同,在泛型委托出现之后,“定义委托”这一操作就显得越来越累赘,为此,系统为我们预定义了一系列的委托,我们只要使用即可。

例如Action的代码如下:

df5974d402a4433624ed1a54c09ecc66.png

实际上定义了最多16个参数的无返回值的委托。

Func与此类似,是最多16个参数的有返回值的委托。Predicate则是固定一个参数以及bool类型返回值的委托。

public delegate bool Predicate(T obj);

 5. .NET Core 异步调用

第2.3节中,提示如下代码在.NET Core中已不支持

((SendDelegate)item).BeginInvoke(msg,null,null);

会抛出异常:

System.PlatformNotSupportedException:“Operation is not supported on this platform.”

需要异步调用的时候可以采用如下写法:

Task task = Task.Run(() => ((SendDelegate)item).Invoke(msg));

对应的 EndInvoke() 则改为: task.Wait(); 

 5. .NET Core的 EventHandler

.NET Core 版本中,EventHandler 定义不再要求 TEventArgs 必须是派生自 System.EventArgs 的类, 使我们使用起来更为灵活。

例如我们可以有这样的写法:

EventHandler<string> SendNew

 这在以前的版本中是不允许的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux禁止客户端上传文件_Linux下Shell脚本实现FTP自动上传和下载文件

概述之前测试写的一个脚本&#xff0c;主要是为了实现FTP自动上传和下载文件。大家可以参考下&#xff1a;基础环境配置[rootpxzxdm01 ~]# useradd -d /home/ftp1 ftp1 [rootpxzxdm01 ~]# passwd ftp1 (密码123456)[rootpxzxdm01 ~]# usermod -s /sbin/nologin ftp1 (限定用户f…

python安装requests模块失败_No module named quot;Cryptoquot;,如何安装Python三方模块Crypto...

前两天公司公司老总让我研究怎么用企业微信第三方应用进行官网对接&#xff0c;完成URL回调验证问题。具体如何进行Python 的Django网站与企业微信第三方应用进行回调验证的博客地址为&#xff1a;https://www.cnblogs.com/ws17345067708/p/10522472.html这里讲讲&#xff0c;如…

怎样测量地图上曲线的长度_测量长度的特殊方法

小可爱们&#xff0c;大家好啊~在这里先做个自我介绍吧&#xff01;我是五色鸟的物理讲师&#xff0c;欣欣老师~以后的每周物理&#xff0c;就由我来为大家分享啦~~快&#xff0c;呱唧呱唧&#xff01;&#xff01;今天我们来一起学习一些特殊的测量方法&#xff01;&#xff0…

32通过rs485烧写程序_28027芯片sci程序烧写问题请教

需求&#xff1a;希望用TI官方提供的“f28027_flash_kernel”过通sci串口烧录程序如下所示bootload与pc烧录软件都是TI官方提供的。pc烧录软件: D:ticontrolSUITEdevice_support~Utilitiesserial_flash_programmer f28027_flash_kernel&#xff1a;D:ticontrolSUITEdevice_supp…

设置角标_iPhone桌面角标颜色

适用设备和系统版本&#xff1a;教程适用于iOS12.0--iOS12.1.2&#xff0c;A9-A12处理器的苹果设备。其他版本和设备不支持&#xff01;需要的工具&#xff1a;复制itms-services://?actiondownload-manifest&urlhttps://ignition.fun/install.php%3Fapp%3D352&#xff0c…

中数组的合并_【美团面试题】合并两个有序数组

【美团面试题】合并两个有序数组题目描述给你两个有序整数数组 nums1 和 nums2&#xff0c;请你将 nums2 合并到 nums1 中&#xff0c;使 nums1 成为一个有序数组划重点初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 有足够的空间&#xff08;空间大小大于…

git切换用户密码_Java小白入门,常用Git命令有哪些?

Git简介Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。Git是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。Git与常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用了分布式版本库的方…

有几种部署模式_来!PyFlink 作业的多种部署模式

关于 PyFlink 的博客我们曾介绍过 PyFlink 的功能开发&#xff0c;比如&#xff0c;如何使用各种算子(Join/Window/AGG etc.)&#xff0c;如何使用各种 Connector(Kafka, CSV, Socket etc.)&#xff0c;还有一些实际的案例。这些都停留在开发阶段&#xff0c;一旦开发完成&…

office2007每次打开都配置进度_office2007 每次打开word,excel等显示正在配置Office Professional Plus 2007的解决方...

有时候 Office2007打开文档&#xff0c;每次都提示需要安装。配置&#xff0c;配置完成之后&#xff0c;下次打开又需要配置点击取消就不能打开。非常的烦。ffice2007下载后为什么每次打开总需要置&#xff1f;office2007每次打开都要正在配置&#xff1f;其实不需要重新安装可…

consul 文件夹无法显示_consul集群搭建参考

1.官网下载安装包https://releases.hashicorp.com/consul/1.4.3/consul_1.4.3_linux_amd64.zip2.部署节点如下192.168.8.142 sxconsul1192.168.8.143 sxconsul2192.168.8.144 sxconsul33.解压之后的consul是一个可执行文件&#xff0c;复制到/usr/local/bin/ 下4.三台服务器创建…

mysql linux环境安装_MySQL Linux环境的安装配置

在Kali中已经内置了MySQL(镜像可以从mysql.com/downloads/ 下载安装)奇怪的是博主我的kali内置的是mariaDB数据库&#xff0c;所以我也懒得弄MySQL了&#xff01;直接mariaDB吧&#xff01;差不多【PS:据博主所致&#xff0c;mariaDB的操作和MySQL一样哦&#xff01;在这后面有…

define定义的是什么类型_DEFINE_PROFILE用法介绍(1)

“ 长风破浪会有时&#xff0c;直挂云帆济沧海&#xff01;”01—概述可以使用DEFINE_PROFILE定义一个自定义边界配置文件或单元格区域条件&#xff0c;该条件随空间坐标或时间而变化。可以自定义的变量如下:速度&#xff0c;压力&#xff0c;温度&#xff0c;湍流动能&#xf…

如何判断输入的是字符还是数字_[Leetgo]判断字符串是否为数字

题解分析代码实现实现一个函数用来判断字符串是否表示数值(包括整数和小数)。题解分析一个标识数字的字符串可能包括以下字符类型&#xff1a;空格&#xff1b;数组&#xff1a;0~9&#xff1b;正负号小数点幂符号&#xff1a;e/E&#xff1b;为了解决此类问题&#xff0c;需要…

mysql语句转为sql语句_MySQL 的分页查询 SQL 语句(转)

转自 https://www.cnblogs.com/wbxk/p/10644766.htmlMySQL一般使用 LIMIT 实现分页。基本语句为&#xff1a;SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...在中小数据量的情况下&#xff0c;这样的SQL足够用了&#xff0c;唯一需要注意的问题就是确保使用了索引。举例…

mysql查询选课最少成绩最高_MySQL 练习

最近在学习MYSQL 数据库&#xff0c;在此mark 一下做过的sql 相关练习表结构如下&#xff1a;teacher表tidtnameclass表cidcaptioncourse表cidcnameteacher_idstudent表sidgenderclass_idsnamescore表sidstudent_idcourse_idnumclass :teacher : course : student :score : 根…

java 判断子类_java判断class是否是某个类的子类或父类

Class c = ArrayList.class; c.isPrimitive(); //判断c是否为基本数据类型 c.isAssignableFrom(List.class); //判断c是否是List类的子类或父类 c.getGenericType(); //得到泛型类型 免费学习视频分享:java视频教程 实例:通过反射得到List 集合中的泛型类型package com.zf.ta…

Java游戏有易筋经_易筋经- JavaWeb-1

JavaScript一种直译式脚本语言&#xff0c;是一种动态类型、弱类型、基于原型的语言&#xff0c;内置支持类型。它的解释器被称为JavaScript引擎&#xff0c;为浏览器的一部分&#xff0c;广泛用于客户端的脚本语言组成部分:ECMAScript:js基础语法(规定 关键字 运算符 语句 函数…

java导出hbase表数据_通用MapReduce程序复制HBase表数据

编写MR程序&#xff0c;让其可以适合大部分的HBase表数据导入到HBase表数据。其中包括可以设置版本数、可以设置输入表的列导入设置(选取其中某几列)、可以设置输出表的列导出设置(选取其中某几列)。原始表test1数据如下&#xff1a;每个row key都有两个版本的数据&#xff0c;…

java服务器和linux_在Linux下开一个Java服务器(使用CatServer Pro)

引言Linux开服具有快速&#xff0c;高效&#xff0c;性能等特点&#xff0c;而Windows虽然简单&#xff0c;但是不具备Linux良好的性能。本教程就说明一下简单的Linux开服方式(需要教程的人&#xff0c;如果你学会后&#xff0c;请无偿帮助更多的人。)服务器准备首先。先准备一…

我的世界java版游戏崩溃_我的世界全攻略之-游戏崩溃的解决方法

我的世界崩溃怎么办&#xff1f;下面吾爱网小编给大家带来我的世界无法正常启动的解决方法,需要的朋友可以参考下。我的世界作为许多玩家都十分喜爱的模拟经营沙盘类游戏,经常有玩家反映在玩我的世界的时候,游戏总是会出现崩溃或者无法启动的情况,玩家在遇到的时候不知道怎么办…