一文说通Dotnet的委托
简单的概念,也需要经常看看。
一、前言
先简单说说Delegate的由来。最早在C/C++中,有一个概念叫函数指针。其实就是一个内存指针,指向一个函数。调用函数时,只要调用函数指针就可以了,至于函数本身的实现,可以放在其它地方,也可以后实现。到了.Net,没有指针的概念了,但这种方式很实用,所以这个概念也保留了下来,形成了现在的委托Delegate。
另外,在.Net中,也把委托延伸了,与执行回调设计成了同一种机制,允许开发者定义回调的签名和类型。
当我们声明一个委托时,编译器会生成一个从MulticastDelegate派生的类。MulticastDelegate还包含几个方法,不过因为这些方法是CLR运行时动态生成的,代码IL中看不到,也不需要关心。
委托最大的特性是不需要进行强耦合。所以调用者其实并不知道所调用的是静态方法还是实例方法,也不知道具体调用的内容。举个见的例子,UI编程中的按钮Button类。按钮类本身并不知道它的OnClick事件是如何处理的,也不需要知道。所以实际中,OnClick事件就是使用委托发布的。开发者在开发过程中实现OnClick事件的处理,并由UI订阅使用。
这种方式,就是委托对类的解耦。
为了防止不提供原网址的转载,特在这里加上原文链接:https://www.cnblogs.com/tiger-wang/p/14330314.html
二、简单委托
委托有一个非常简单的规则,就是:要引用的方法的返回类型或参数要与委托类型声明相匹配。
听着有点绕口,我们拿一个例子来说。
我们有一个方法:
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;
等同于挨个调用Sum
和Sub
;delegate4 = delegate3 - delegate2;
等同于调用(Sum + Sub) - Sub
,实际最后调用的是Sum
。
五、结论
委托在Dotnet里,是一个很常用的代码组成。用好委托,可以很漂亮地实现诸如事件、回调等操作,所以必须要熟练。
最后再说一下委托的基本内容:
- 委托是面向对象的操作,类型安全,数据安全;
- 委托派生自Dotnet的
Delegate
类,它是一个类; - 委托类型是密封(sealed)的,所以不能从委托继承。
微信公众号:老王Plus 扫描二维码,关注个人公众号,可以第一时间得到最新的个人文章和内容推送 本文版权归作者所有,转载请保留此声明和原文链接 |
一文说通Dotnet的委托的更多相关文章
- 一文说通Dotnet Core的后台任务
这是一文说通系列的第二篇,里面有些内容会用到第一篇中间件的部分概念.如果需要,可以参看第一篇:一文说通Dotnet Core的中间件 一.前言 后台任务在一些特殊的应用场合,有相当的需求. 比方, ...
- 一文说通Dotnet Core的中间件
前几天,公众号后台有朋友在问Core的中间件,所以专门抽时间整理了这样一篇文章. 一.前言 中间件(Middleware)最初是一个机械上的概念,说的是两个不同的运动结构中间的连接件.后来这个概念 ...
- 一文说通C#中的异步编程补遗
前文写了关于C#中的异步编程.后台有无数人在讨论,很多人把异步和多线程混了. 文章在这儿:一文说通C#中的异步编程 所以,本文从体系的角度,再写一下这个异步编程. 一.C#中的异步编程演变 1. ...
- 一文说通Blazor for Server-Side的项目结构
用C#代替Javascript来做Web应用,是有多爽? 今天聊聊 Blazor. Blazor 是一个 Web UI 框架.这个框架允许开发者使用 C# 来创建可运行于浏览器的具有完全交互 UI ...
- 一文说通MongoDB via Python操作
Python并不仅仅是一个做Machine Learning的语言. 说到Python,一般都会感觉它关联着ML,如果不是做ML开发,就会觉得离自己很远.而实际上,作为一门语言,Python在应用 ...
- 一文说通C#中的异步迭代器
今天来写写C#中的异步迭代器 - 机制.概念和一些好用的特性 迭代器的概念 迭代器的概念在C#中出现的比较早,很多人可能已经比较熟悉了. 通常迭代器会用在一些特定的场景中. 举个例子:有一个for ...
- 一文说通C#中的异步编程
天天写,不一定就明白. 又及,前两天看了一个关于同步方法中调用异步方法的文章,里面有些概念不太正确,所以整理了这个文章. 一.同步和异步. 先说同步. 同步概念大家都很熟悉.在异步概念出来之前,我 ...
- 一文说通Jwt、Session、Cooike区别
JWT 全称是 JSON Web Token,是目前非常流行的跨域认证解决方案,在单点登录场景中经常使用到. 有些人觉得它非常好用,用了它之后就不用在服务端借助 redis 实现认证过程了,但是,还有 ...
- 打造自己的.NET Core项目模板
前言 每个人都有自己习惯的项目结构,有人的喜欢在项目里面建解决方案文件夹:有的人喜欢传统的三层命名:有的人喜欢单一,简单的项目一个csproj就搞定.. 反正就是萝卜青菜,各有所爱. 可能不同的公司对 ...
随机推荐
- sqli-labs 20-22 --cookie注入
异常处理 一开始打开这个题目的时候找不到cookie... 登录成功就是没有cookie cookie注入没有cookie... 第二天重新做的时候,同学讲自己设置cookie可以用 用插件EditT ...
- ASP.NET Web API运行提示:找到了与该请求匹配的多个操作的解决方法
- 修改oracle监听占用8080端口号的问题
前期: 先确认Oracle已经安装并正常运行 输入lsnrctl start 启动 Oracle 输入lsnrctl status 查看Oracle运行状态 可以看到第二个PORT = 8081 是我 ...
- SecureCRT SSH Linux中不显示彩色 字体颜色、文件夹和文件显示的颜色区别开解决办法
SecureCRT SSH Linux中不显示彩色 字体颜色.文件夹和文件显示的颜色区别开解决办法 实验环境: 刚开始我的情况是这样的:带颜色的显示不出来,然后还能看到,此处有内容,猜测是Secure ...
- 第三章 IP地址规划设计技术(很重要)
知识重点: 选择题考点 IP基础(网络地址.子网掩码) 网络地址转换 NAT 的原理 CIDR (计算方法) IPv6 地址表示 综合题 IP地址的分类与计算 VLSM 地址规划 3.1 基础知识 3 ...
- linux系统操作系统网卡漂移解决方案及问题原因
一.问题描述 公司有100-150台服务器安装RHEL7.4&中标麒麟7.4系统,为方便编辑配置网卡,使用脚本方式配置为biosname=0,ifname=0,目的是为将en1o2p此类长字符 ...
- ASP.Net Core 3.1 使用实时应用SignalR入门
参考文章:微软官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction?view=aspnetcore-3.1 和 ...
- MP(MyBatis-Plus)实现乐观锁更新功能
实现步骤 step1:添加乐观锁拦截器 MP的其他拦截器功能可以参考官网 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { ...
- Head First 设计模式 —— 07. 适配器模式
思考题 你能想到真实世界中,还有哪些适配器的例子? P236 HDMI 转 VGA 转换器 Type-C 转 3.5mm 线 适配器模式解析 客户使用适配器的过程: P241 客户通过目标接口调用适配 ...
- 阿里云centos7[linux]安装nginx
标题 说明 服务器版本 Centos7 x64 nginx版本 1.19.6 作者 walton 一.准备 创建安装包目录并进入 mkdir /usr/dev/nginx cd /usr/dev/ng ...