一文说通Dotnet的委托

简单的概念,也需要经常看看。

一、前言

先简单说说Delegate的由来。最早在C/C++中,有一个概念叫函数指针。其实就是一个内存指针,指向一个函数。调用函数时,只要调用函数指针就可以了,至于函数本身的实现,可以放在其它地方,也可以后实现。到了.Net,没有指针的概念了,但这种方式很实用,所以这个概念也保留了下来,形成了现在的委托Delegate。

另外,在.Net中,也把委托延伸了,与执行回调设计成了同一种机制,允许开发者定义回调的签名和类型。

当我们声明一个委托时,编译器会生成一个从MulticastDelegate派生的类。MulticastDelegate还包含几个方法,不过因为这些方法是CLR运行时动态生成的,代码IL中看不到,也不需要关心。

委托最大的特性是不需要进行强耦合。所以调用者其实并不知道所调用的是静态方法还是实例方法,也不知道具体调用的内容。举个常见的例子,UI编程中的按钮Button类。按钮类本身并不知道它的OnClick事件是如何处理的,也不需要知道。所以实际中,OnClick事件就是使用委托发布的。开发者在开发过程中实现OnClick事件的处理,并由UI订阅使用。

这种方式,就是委托对类的解耦。

二、简单委托

委托有一个非常简单的规则,就是:要引用的方法的返回类型或参数要与委托类型声明相匹配。

听着有点绕口,我们拿一个例子来说。

我们有一个方法:

void PrintInfo(string message);

按照规则,这个方法对应的委托方法可以写成:

void Delegate_PrintInfo(string message);

这样,按照规则,委托使用时就可以写成:

Delegate_PrintInfo = PrintInfo;

这样,当我们调用Delegate_PrintInfo("Hello WangPlus")的时候,实际执行的是PrintInfo("Hello WangPlus")了。

下面,我们来看看委托的声明。

public delegate int Delegate_Method(int x, int y);  

委托可以封装任何方法。在上面这个例子里,我们接受两个参数,并返回一个int值。

在这样一个声明中,delegate是一个关键词,表明我们声明的是一个委托。而其它部分,跟我们正常的代码方式没有任何区别。

多举几个例子看看:

public delegate void Demo_Func1(string para);
public delegate ClassA Demo_Func2(ClassB para);
private delegate StructA Demo_Func3(int para);

除了delegate,其它内容跟正常方法没有区别。

声明有了,如何用呢?看例子:

class Program
{public delegate int Delegate_Method(int x, int y);static void Main(string[] args){Delegate_Method handler = SumMethod;int result = handler(3, 4);}static int Sum(int x, int y){return x + y;}
}

这是一个简单的例子。

我们先定义了一个委托,接受两个参数,并返回int值。我希望这个委托调用下面的Sum方法,因此Sum方法和委托Delegate_Method的签名(参数和返回值)兼容。这儿要注意理解这个兼容的概念,不是完全相同,是兼容。

再写个稍微复杂一点的例子:

public delegate void Delegate_Method(int x, int y);class ExampleClass
{public void Sum(int x, int y){Console.WriteLine(x + y);}public void Sub(int x, int y){Console.WriteLine(x - y);}
}
class Program
{static void Main(string[] args){ExampleClass example = new ExampleClass();Delegate_Method delegate_1;Delegate_Method delegate_2;delegate_1 = example.Sum;delegate_2 = example.Sub;delegate_1(100, 50);delegate_2(100, 50);}
}

如果第一个例子明白了,那这个例子也不难理解。

三、委托链

委托链的核心的维护一个可调用的委托列表。当调用列表时,列表中的所有委托都会被调用。同时,委托链可以使用操作符,用+来组合,用-来删除。

看例子:

public delegate void Delegate_Method(int x, int y);class ExampleClass
{public void Sum(int x, int y){Console.WriteLine(x + y);}public void Sub(int x, int y){Console.WriteLine(x - y);}
}
class Program
{static void Main(string[] args){ExampleClass example = new ExampleClass();Delegate_Method[] delegate_list = new Delegate_Method[] { example.Sum, example.Sub };Delegate_Method delegate_chain = delegate_list[0] + delegate_list[1];delegate_chain(100, 50);}
}

在这个例子中,定义了一个委托数组,然后用+操作符组合这些方法。

Delegate_Method delegate_chain = delegate_list[0] + delegate_list[1];  
Delegate_Method delegate_chain1 = delegate_chain - delegate_list[0];  

上面两行代码,CLR将解释为(Sum + Sub) - Sum,并只执行Sub方法。这是一个使用-操作符从委托链中移除委托的例子。

您还可以遍历委托链:

public delegate void Delegate_Method(int x, int y);class ExampleClass
{public void Sum(int x, int y){Console.WriteLine(x + y);}public void Sub(int x, int y){Console.WriteLine(x - y);}
}
class Program
{static void Main(string[] args){ExampleClass example = new ExampleClass();Delegate_Method[] delegate_list = new Delegate_Method[] { example.Sum, example.Sub };Delegate_Method delegate_chain = delegate_list[0] + delegate_list[1];Delegate[] delegates = delegate_chain.GetInvocationList();for (int i = 0; i < delegates.Length; i++){Delegate_Method _delegate = (Delegate_Method)delegates[i];_delegate(100, 50);}}
}

在这个例子中,使用了GetInvocationList方法获取委托链中的所有委托。这个方法帮助我们引用委托链中的每个委托,我们也可以从委托链中以任何顺序调用委托。

四、多播委托

委托在被调用时可以调用多个方法,这称之为多播。委托对象的一个非常有用的属性是,它们可以被分配给一个委托实例,以便使用+/-操作符进行多播。组合委托调用由它组成的多个委托。

多播委托时,只能组合相同类型的委托。操作符可用于从组合委托中增加/删除委托组件。

此外,多播委托返回类型总是void。

class Program
{public delegate void Delegate_Method(int x, int y);public static void Sum(int i, int j){Console.WriteLine(i + j);}public static void Sub(int i, int j){Console.WriteLine(i - j);}static void Main(string[] args){Delegate_Method delegate1, delegate2, delegate3, delegate4;delegate1 = Sum;delegate2 = Sub;delegate3 = delegate1 + delegate2;delegate3(100, 50);delegate4 = delegate3 - delegate2;delegate4(100, 50);}
}

这段代码里,delegate3 = delegate1 + delegate2;等同于挨个调用SumSubdelegate4 = delegate3 - delegate2;等同于调用(Sum + Sub) - Sub,实际最后调用的是Sum

五、结论

委托在Dotnet里,是一个很常用的代码组成。用好委托,可以很漂亮地实现诸如事件、回调等操作,所以必须要熟练。

最后再说一下委托的基本内容:

  • 委托是面向对象的操作,类型安全,数据安全;

  • 委托派生自Dotnet的Delegate类,它是一个类;

  • 委托类型是密封(sealed)的,所以不能从委托继承。

喜欢就来个三连,让更多人因你而受益

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

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

相关文章

ansys fluent udf manual 下载_FLUENT流固耦合柱体结构涡激振动仿真案例解析

作者 | 陈东阳博士 仿真秀科普作者一、柱体结构涡激振动定义对于海洋工程、风工程上普遍采用的圆柱形断面结构物&#xff0c;流体绕过柱体时会产生交替发放的泻涡&#xff0c;这种交替发放的泻涡又会在柱体上生成顺流向及横流向周期性变化的脉动压力。如果此时柱体是弹性支撑的…

SQL Server的数据导入MySQL数据库方法简介

第一种是安装mysql ODBC&#xff0c;利用sql server的导出功能&#xff0c;选择mysql数据源&#xff0c;进行数据的直接导出&#xff0c;这种方法很简便&#xff0c;但是针对实际应用有很多弊端&#xff0c;最主要体现就是数据类型问题&#xff0c;首先&#xff0c;sql server数…

添加图标_win10系统轻松添加显示桌面图标的操作方法

win10系统想必大家都非常熟悉吧&#xff0c;然而有时候可能会碰到win10系统对win10系统添加显示桌面图标进行设置&#xff0c;比如近日有用户到本站反映说对win10系统添加显示桌面图标设置的情况&#xff0c;到底该怎么设置win10系统添加显示桌面图标呢&#xff1f;我们只用按照…

分享干货:靠刷算法题,真的可以刷进大厂吗?

在国外 Facebook&#xff0c;Google 等互联网巨头中&#xff0c;算法无疑是面试中的重头戏&#xff0c;不过&#xff0c;在最近几年国内互联网大厂面试中&#xff0c;算法的比重也越来越高&#xff0c;算法不扎实&#xff0c;笔试可能就直接被刷了。那你知道大厂的技术面为什么…

SwitchBox系列 - 介绍

很多客户问到了如何让iAMT能够穿过防火墙或网关被管理&#xff0c;他们中的部分也了解到了Intel工程师开发的SwitchBox方案&#xff0c;但都不是很了解。基于此&#xff0c;我正好有机会来共享一些在SwitchBox方面的经验和理解。虽然Intel AMT技术是基于TCP/IP协议上的&#xf…

python找出一个数的所有因子_python – 找到最大素因子的正确算法

我试图找出任何数字的最大素数因子.我正在为python中的这个问题做程序,但是我正在遵循的算法似乎有问题.它似乎陷入无限循环.该计划如下&#xff1a; def prime(n): i0; while(n!2): for i in range(2,n): if(n%i0): prime(n/i); else: continue; print("The highest prim…

获取两个数据的交集_MySQL交集和差集的实现方法

在MySQL中&#xff0c;只支持Union(并集)集合运算&#xff0c;而对于交集Intersect和差集Except并不支持。那么如何才能在MySQL中实现交集和差集呢&#xff1f;一般在MySQL中&#xff0c;我们可以通过in和not in来间接实现交集和差集&#xff0c;当然也有一定局限性&#xff0c…

Asp.Net Core之Identity应用(上篇)

一、前言在前面的篇章介绍中&#xff0c;简单介绍了「IdentityServer4」持久化存储机制相关配置和操作数据&#xff0c;实现了数据迁移&#xff0c;但是未对用户实现持久化操作说明。在总结中我们也提到了&#xff0c;因为「IdentityServer4」本就支持了接入其他认证方式&#…

c++ 读文件_第十六节:读文件,文件的创建,写文件,文件的读写以及鼠标键盘事件和图形绘制...

读文件//读文件文件的创建public 写文件public 文件的读写重点&#xff1a;文件类主要功能&#xff1a;创建&#xff0c;读属性&#xff0c;写属性&#xff0c;删除等文件读写操作File类File类的对象用来获取文件本身的信息&#xff0c;如文件所在目录、文件长度、文件读写权限…

MYSQL 表的修复(不断更新)

我的表一直没有怎么坏过。直到昨天我重装系统后再次装MYSQL的时候提示服务启动不起来。修复如下&#xff1a;MYSQL安装目录/DATA/MYSQL改名为MYSQL2然后重新安装MYSQL数据库。启动后导入相关数据&#xff0c;select * from mysqldb into outfile c:\\db.sql;关于表的所属恢复se…

ios 中间 图片缩放_在 iOS 开发中如何优雅地进行图片缩放?

对于图片拉伸是移动开发中很常见的需求&#xff0c;在前一阵子做项目中需要做一个类似于QQ聊天气泡&#xff0c;这个气泡会根据文字的多少而变化&#xff0c;当时有了三种方案&#xff1a;重写drawRect方法&#xff0c;使用贝赛尔曲线画一个气泡用一个大图作为背景&#xff0c;…

docker安装jenkins并用docker部署net

1. 部署jenkins1.1 创建jenkins的工作目录//创建工作目录 mkdir /var/jenkins_home //赋予权限 chown -R 1000 /var/jenkins_home1.2 通过docker部署1.2.1 拉取镜像docker pull jenkinsci/blueocean1.2.2 生成容器#注意&#xff1a;切换一行执行命令 docker run -u root -d…

页面验证

例子&#xff1a;仅供测试 注册帐号 密码 重新输入密码 身份证号码 银行存款 拥有几家世界500强企业 出生日期 下辈子出生日期 对输入内容进行验证 //这是把JS文件全放在了html里面 可以把里面的JS内容分出2个JS来 另外 身份证的验证 在另一文章里面//http://www.cnb…

python导入文件列行_python读写csv文件并增加行列的实例代码

python读写csv文件并增加行列&#xff0c;具体代码如下所示&#xff1a; # -*- coding: utf-8 -*- """ Created on Thu Aug 17 11:28:17 2017 author: Shawn Yuen """ import csv d list(range(38685)) with open(./kinetics_test.csv) as f1: …

ie插件获取dom_读书笔记《DOM编程艺术》DOM

DOM的理解1.1 D&#xff1a;当创建了一个网页并把它加载到Web浏览器中时&#xff0c;DOM就生成了&#xff0c;它将我们编写的网页文档转换成一个文档对象。1.2 O&#xff1a;“对象”是一种自足的数据集合&#xff0c;相关联的变量称为这个对象的属性&#xff0c;只能通过这个对…

针对Fluent-Bit采集容器日志的补充

hello&#xff0c;之前我写过《一套标准的ASP.NET Core容器化应用日志收集分析方案》&#xff0c;在公司团队、微信公众号、Github上反映良好。其中配置Fluent-bit使用Forward协议收集容器日志&#xff0c;需要在Docker-Compose App配置Loging DriverFluentd实践中&#xff0c;…

Oracle结构设计技巧(访问数据库象访问内存一样 快)

尽管Oracle系统本身已经提供了若干种对系统性能进行调节的技术&#xff0c;但是&#xff0c;假如数据库设计本身就有问题特别是在结构上设计得尤其糟糕&#xff0c;那你纵有天大的本事又能奈何?因此&#xff0c;Oracle数据库的设计者完全有必要弄清楚(从项目着手设计开始)该如…

js template换行_JavaScript字符串换行符?

小编典典我刚刚使用了一些愚蠢的JavaScript测试了一些浏览器&#xff1a;function log_newline(msg, test_value) {if (!test_value) {test_value document.getElementById(test).value;}console.log(msg : (test_value.match(/\r/) ? CR : ) (test_value.match(/\n/) …

在每个运行中运行多个查询_在Kubernetes中运行OpenEBS

什么是OpenEBS&#xff1f;现在&#xff0c;OpenEBS是kubernetes下与容器原生和容器附加存储类型相关通用的领先开源项目之一。 通过为每个工作负载指定专用的存储控制器&#xff0c;OpenEBS遵循容器附加存储或CAS的脚步。 为了向用户提供更多功能&#xff0c;OpenEBS具有精细的…

如何在 C# 8 中使用默认接口方法

C# 8 中新增了一个非常有趣的特性&#xff0c;叫做 默认接口方法 (又称虚拟扩展方法)&#xff0c;这篇文章将会讨论 C# 8 中的默认接口方法以及如何使用。在 C# 8 之前&#xff0c;接口不能包含方法定义&#xff0c;只能在接口中定义方法签名&#xff0c;还有一个就是接口的成员…