C#之委托与事件
委托与事件
废话一堆:网上关于委托、事件的文章有很多,一千个哈姆雷特就有一千个莎士比亚,以下内容均是本人个人见解。
1. 委托
1.1 委托的使用
这一小章来学习一下怎么简单的使用委托,了解一些基本的知识。
这里先看一下其他所要用到的类的信息
/// <summary>
/// 函数用例
/// </summary>
public class ManyMethodClass
{
public ManyMethodClass() { }
/// <summary>
/// 实例函数
/// </summary>
/// <param name="strmes"></param>
public void InstanceMethod(string strmes)
{
Console.WriteLine("实例函数输出:" + strmes);
}
/// <summary>
/// 静态函数
/// </summary>
/// <param name="strmes"></param>
public static void StaticMethod(string strmes)
{
Console.WriteLine("静态函数输出:" + strmes);
}
}
定义一个委托类型
public delegate void DisplayStringDelegate(string strmes);
实例化委托类型
//绑定实例函数 第一种定义方式(实例函数)
DisplayStringDelegate disstrdele_instance = new DisplayStringDelegate(newManyMethodClass().InstanceMethod);
//绑定静态函数 第二种定义方式(静态函数)
DisplayStringDelegate disstrdele_static = new DisplayStringDelegate(ManyMethodClass.StaticMethod);
//绑定委托实例 第三种定义方式(委托实例)
DisplayStringDelegate disstrdele_delegate = new DisplayStringDelegate(disstrdele_instance);
调用委托实例
disstrdele_instance.Invoke("Hello Word");
disstrdele_static("Hello Word");
disstrdele_delegate("Hello Word");
(ps:这里不同的调用方式效果都是一样的,在下一节会有讲到)
最后让我们看一下最终的效果图:
1.2 委托的定义
委托类型是类类型
/// <summary>
/// 委托示例类
/// </summary>
public class DelegateDemonStration
{
public DelegateDemonStration() { }
public delegate void DisplayStringDelegate(string strmes);
}
在上面的示例代码中,我们定义了一个委托示例类DelegateDemonStration,在DelegateDemonStration的内部我们定义了上一节中讲到的委托的类型DisplayStringDelegate,
在这里先不谈DisplayStringDelegate它定义签名类型,先来谈谈它到底是什么样的存在。
我们来看一下代码:
/// <summary>
/// 委托示例类
/// </summary>
public class DelegateDemonStration
{
public DelegateDemonStration() { }
public class DisplayStringDelegate : MulticastDelegate
{
public DisplayStringDelegate(object @object, IntPtr method){}
public virtual IAsyncResult BeginInvoke(string strmes, AsyncCallback callback, object @object);
public virtual void EndInvoke(IAsyncResult result);
public virtual void Invoke(string strmes);
}
}
当我们定义好一个委托类型的时候,在运行时C#编译器把【public delegate void DisplayStringDelegate(string strmes);】
编译为【public class DisplayStringDelegate : MulticastDelegate】
所以,各位客官只要记住 在你定义一个委托类型的时候实际上你是定义了一个类
委托实例是函数指针
先让我们来看一下这一小节所要用到的示例代码(所用到的对象类型还是上一小节的内容):
DisplayStringDelegate disstrdele = newDisplayStringDelegate(newManyMethodClass().InstanceMethod);
disstrdele += ManyMethodClass.StaticMethod;//这里是委托类型推断 绑定函数的一种方式 几种方式可以自行百度
disstrdele += new DisplayStringDelegate(ManyMethodClass.StaticMethod);
1.第一步 在我们执行DisplayStringDelegate disstrdele = newDisplayStringDelegate(newManyMethodClass().InstanceMethod);的时候,disstrdele实例是如下图一样:
2.第二步 当我们继续执行代码 disstrdele += ManyMethodClass.StaticMethod;的时候,请再来看图:
这是disstrdele多指向了一个函数地址,而它的内部实现并不是外表看起来这样简单的:
这里简要的说一下执行第二步的时候
L_0012:是将第一步的disstrdele实例压入栈中
L_0013:将一个空引用压入栈中
L_0014:加载ManyMethodClass.StaticMethod函数地址
L_001a:实例化DisplayStringDelegate委托类型,假如它叫A
L_001f:将disstrdele和A合并
L_0024:将合并好的值转换成DisplayStringDelegate类型,并且存入L_0013中
L_0029:将L_0013赋值到disstrdele实例
(以上纯属个人理解,如有偏差请帮忙纠正,谢谢)
3.第三步 当我们继续执行代码disstrdele += new DisplayStringDelegate(ManyMethodClass.StaticMethod);的时候,过程和第二步相同:
终上所述,委托实例都会指向一个函数地址,当然喽合并过后新的委托实例宁当别论,所以 委托实例是函数指针 或者委托实例是函数指针列表
1.3 委托实例的调用
我们来看调用的代码:
disstrdele("Hello Word");
这里的内容是接着上一小节的内容继续讲的,拆分开来讲是为了让大家能看的更清楚。
看一下示意图吧,
从这里就可以看出来了,虽然disstrdele 实例是合并后的委托实例,它的屁股上挂上了好多函数地址,但是执行这样函数的入口还只是一个,那就是Delegate.Invoke(string),
如果有人要问它的Invoke(string)内部是怎么实现的,暂时回答不了,因为具体的代码是动态生成的吧,不过我们可以自己猜想或者想象一下它是怎么实现的。
在这里就不多说了。
看了以上的内容能大概的知道或者分清一些概念性的东西。
这里本人只是讲了一把剑是由铁铸成的,可以用来切割、刺、劈、砍,至于这把剑怎么用就因人而异了。
2. 事件
2.1 事件是什么?
这一章我们来学习一下事件
照旧我们先来看一下示例代码:
public class EventDomeStration
{
public EventDomeStration() { }
/// <summary>
/// 定义好的一个示例
/// </summary>
public event DisplayStringDelegate DisplayStringEventHandler;
}
在上面的代码中,我们定义了一个事件DisplayStringEventHandler,而在它名称前面的是一个DisplayStringDelegate委托类型(就是我们上一节所说的委托)。
就对于它而言来看一下MSIL,看看它究竟是什么样的:
.class public auto ansi beforefieldinit EventDomeStration extends [mscorlib]System.Object
{
.field private class DelegateCaseAndEventCase.DisplayStringDelegate DisplayStringEventHandler
.event DelegateCaseAndEventCase.DisplayStringDelegate DisplayStringEventHandler
{
.addon instance void DelegateCaseAndEventCase.EventDomeStration::add_DisplayStringEventHandler(class DelegateCaseAndEventCase.DisplayStringDelegate)
.removeon instance void DelegateCaseAndEventCase.EventDomeStration::remove_DisplayStringEventHandler(class DelegateCaseAndEventCase.DisplayStringDelegate)
}
}
从这里可以看到,定义好的一个事件就是一个私有(DisplayStringDelegate委托类型)的字段加上一个事件访问器,也就是相当于C#代码的这样:
public class EventDomeStration
{
public EventDomeStration() { }
private DisplayStringDelegate displaystringdele;
public event DisplayStringDelegate DisplaystringEventHandler
{
add
{
displaystringdele += value;
}
remove
{
displaystringdele -= value;
}
}
}
在这里本人对事件的定义是:
事件就是所在对象里的属性,而属性的类型是委托类型,它是负责架设与外部对象即时通讯(传递消息)的桥梁,桥梁本身就是委托。
2.2 事件的使用
出门在外务工,难免要租房子住,每次找房子是件头疼的事请各位客官一起来看一下,就拿这个来举个例子。
先定义一个委托:publicdelegatevoid消息反馈(string 反馈房子信息);
首先得有一个中介,暂且叫它为×××中介1, 来看一下它的内部定义:
public class ×××中介1
{
public ×××中介1() { }
public ×××中介1(string 具体要求)
{
房源信息处理(具体要求);
}
public event 消息反馈 消息通知;
public string 客户要求的信息_1 = string.Empty;
private void 房源信息处理(string 客户要求的信息)
{
//逻辑处理
//假如是有客户需要的 则通知客户
if (消息通知 != null)
{
消息通知("有房子,什么样的,信息等等");
}
客户要求的信息_1 = 客户要求的信息;
//逻辑处理要是没有 把信息移交给了中介2
×××中介2 中介2 = new ×××中介2(this);
}
public void 其它中介消息通知(string 房子信息)
{
if (消息通知 != null)
{
消息通知(房子信息);
}
}
}
中介有了,那现在我想要开始租房子怎么办?没关系,找中介。
首先先把“我”定义出来:
public class 我
{
//比如我要租房子
public 我(){ }
public void 我要租房子(string 具体的要求)
{
//找的是×××中介1
×××中介1 中介1 = new ×××中介1(具体的要求);
中介1.消息通知 += new 消息反馈(中介1_消息通知);
}
void 中介1_消息通知(string 房子信息)
{
//我可以从【房子信息】中知道是否有有合适的房子,如果有得话,是什么样的
}
}
public class ×××中介2
{
public string 客户要求信息 = string.Empty;
public ×××中介2() { }
public ×××中介2(×××中介1 中介1)
{
房源信息处理(中介1);
}
private void 房源信息处理(×××中介1 中介1)
{
//逻辑处理
//判断是否有满足 ->中介1.客户要求的信息_1值条件的房源
//如果有
中介1.其它中介消息通知("房子有的,房子的信息等等");
}
}
上面定义了 我、×××中介1、×××中介2,现在 我 要找房子了
我 me = new 我();
me.我要租房子("有床就行");
好了,“我”已经把信息发布出去了,现在就坐等×××中介1的通知了。
因为事件只能是对象本身内部触发,这个设计是合理,为什么不能用委托,委托也可以实现同样的功能啊?
假如用了委托,这个例子的意思就是“我”可以控制×××中介1有没有找到房子信息,这个是不符合常理的。
因为“我”是找的×××中介1,×××中介1根据“我”的要求没找到,把这个信息交给了×××中介2,让×××中介2帮忙找找,然后中×××中介2找到了,
但是中×××中介2并不能直接通知“我”,如果可以直接通知的话,这样“我”会告×××中介1侵犯客户隐私,
所以只能是×××中介2告诉×××中介1找到了【也就是代码:×××中介1.其它中介消息通知("房子有的,房子的信息等等");】,然后再由×××中介1来通知“我”。
作者:金源
出处:http://jinyuan.blog.51cto.com/
本文版权归作者和51CTO共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面
转载于:https://blog.51cto.com/jinyuan/1403817
相关文章:

24式加速你的Python
作者 | 梁云1991来源 Python与算法之美一、分析代码运行时间第1式,测算代码运行时间平凡方法快捷方法(jupyter环境)第2式,测算代码多次运行平均时间平凡方法快捷方法(jupyter环境)第3式,按调用函…
pip、NumPy、Matplotlib在Windows上的安装过程
Windows上Python 3.6.2 64位的安装步骤:1. 从 https://www.python.org/downloads/windows/ 下载Windows x86-64 executable installer(即python-3.6.2-amd64.exe);2. 直接以管理员身份运行安装,勾选添加到环境变量、pip等即可。可以同时在Wi…

分享:个人是怎么学习新知识的
为什么80%的码农都做不了架构师?>>> 挺多童鞋问我是怎么学习新知识的,干脆写篇文章总结一下,希望对大家有所帮助。对照书、技术博客、极客时间等学习的方式我就不说了。 一、早期 在15年及更早,由于知识储备少&#x…

easyui的datagrid
datagrid数据的绑定方式: 1)data 后跟数据行的json串 2)url 后跟{"total":,"rows":,"foot":},其中total代码返回总行数,rows为数据行json串 .NET MVC,controll控制类方法中获取datagrid…
线性回归介绍及分别使用最小二乘法和梯度下降法对线性回归C++实现
回归:在这类任务中,计算机程序需要对给定输入预测数值。为了解决这个任务,学习算法需要输出函数f:Rn→R。除了返回结果的形式不一样外,这类问题和分类问题是很像的。这类任务的一个示例是预测投保人的索赔金额(用于设置保险费)&am…

4种最常问的编码算法面试问题,你会吗?
导语:面试是测查和评价人员能力素质的一种考试活动。最常问的编码算法面试问题你知道多少呢?作者 | Rahul Sabnis译者 | 苏本如,编辑 | 刘静来源 | CSDN(ID:CSDNnews)在许多采访中,我经常被要求…

[小梅的体验课堂]Microsoft edge canary mac版本体验
简介 华硕微软越来越没有自己的JC了,不经在windows里面加了wsl而且还废弃了自己的老edge浏览器,重新基于chromium内核开发了新的edge浏览器了,不管怎么说mac上又多了一款新的浏览器,对于一个爱好新鲜的我来说那就简单安装体验下 下…

SQL Server用户自定义函数
用户自定义函数不能用于执行一系列改变数据库状态的操作,但它可以像系统 函数一样在查询或存储过程等的程序段中使用,也可以像存储过程一样通过EXECUTE 命令来执行。在 SQL Server 中根据函数返回值形式的不同将用户自 定义函数分为三种类型:…

C++11中std::initializer_list的使用
initializer_list是一种标准库类型,用于表示某种特定类型的值的数组。和vector一样,initializer_list也是一种模板类型,定义initializer_list对象时,必须说明列表中所含元素的类型。和vector不一样的是,initializer_li…

WijmoJS 2019V1正式发布:全新的在线 Demo 系统,助您快速上手,开发无忧
2019独角兽企业重金招聘Python工程师标准>>> 下载WijmoJS 2019 v1 WijmoJS是为企业应用程序开发而推出的一系列包含HTML5和JavaScript的开发控件。其中包含了金融图表、FlexSheet、先进的JavaScript控件(Wijmo 5)和经典的jQuery小部件&#x…

最后3天,BDTC 2019早鸟票即将售罄,超强阵容及议题抢先曝光!
大会官网:https://t.csdnimg.cn/U1wA2019 年12月5-7 日,由中国计算机学会主办,CCF 大数据专家委员会承办,CSDN、中科天玑数据科技股份有限公司协办的 2019 中国大数据技术大会,将于北京长城饭店隆重举行。届时…

php_mongo.dll下载(php操作mongoDB需要)
php_mongo.dll下载(php操作mongoDB需要)如果PHP连接操作mongoDB就必须要加入此扩展:php_mongo.dll,放到你对应php的扩展目录在你的php.ini中加入:extensionphp_mongo.dll重启apache,在phpinfo()中查看是否有…

十大机器智能新型芯片:华为抢占一席,Google占比最多
(图片付费下载自视觉中国)整理 | 胡巍巍来源 | CSDN(ID:CSDNnews)当年,阿基米德爷爷说出“给我一个支点,我就能撬动地球”这句话时,估计没少遭受嘲讽。然而后来的我们,都…

C++/C++11中头文件numeric的使用
<numeric>是C标准程序库中的一个头文件,定义了C STL标准中的基础性的数值算法(均为函数模板): (1)、accumulate: 以init为初值,对迭代器给出的值序列做累加,返回累加结果值,值类型必须支持””算符。它还有一个…

Spring基础16——使用FactoryBean来创建
1.配置bean的方式 配置bean有三种方式:通过全类名(class反射)、通过工厂方法(静态工厂&实例工厂)、通过FactoryBean。前面我们已经一起学习过全类名方式和工厂方法方式,下面通过这篇文章来学习一下Fact…

查看进程 端口
2019独角兽企业重金招聘Python工程师标准>>> 一 进程 ps -ef 1.UID 用户ID2.PID 进程ID3.PPID 父进程ID4.C CPU占用率5.STIME 开始时间6.TTY 开始此进程的TTY7.TIME 此进程运行的总时间8.CMD 命令名 二端口 netstat Linux下如果我…

深度学习中的欠拟合和过拟合简介
通常情况下,当我们训练机器学习模型时,我们可以使用某个训练集,在训练集上计算一些被称为训练误差(training error)的度量误差,目标是降低训练误差。机器学习和优化不同的地方在于,我们也希望泛化误差(generalization …

今日头条首次改进DQN网络,解决推荐中的在线广告投放问题
(图片付费下载自视觉中国)作者 | 深度传送门来源 | 深度传送门(ID:gh_5faae7b50fc5)【导读】本文主要介绍今日头条推出的强化学习应用在推荐的最新论文[1],首次改进DQN网络解决推荐中的在线广告投放问题。背景介绍随着…

RPA实施过程中可能会遇到的14个坑
RPA的实施过程并非如我们所想的那样,总是一帆风顺。碰坑,在所难免。但也不必为此过于惊慌,因为,我们已经帮你把RPA实施之路上的坑找了出来。RPA实施过程中,将会遇到哪些坑? 【不看全文大纲版】●组织层面&a…

Android问题汇总
2019独角兽企业重金招聘Python工程师标准>>> 1. Only the original thread that created a view hierarchy can touch its views 在初始化activity是需要下载图片,所以重新开启了一个线程,下载图片更新ui,此时就出现了上面的错误。…

深度学习中的验证集和超参数简介
大多数机器学习算法都有超参数,可以设置来控制算法行为。超参数的值不是通过学习算法本身学习出来的(尽管我们可以设计一个嵌套的学习过程,一个学习算法为另一个学习算法学出最优超参数)。在多项式回归示例中,有一个超参数:多项式…

自定义View合辑(8)-跳跃的小球(贝塞尔曲线)
为了加强对自定义 View 的认知以及开发能力,我计划这段时间陆续来完成几个难度从易到难的自定义 View,并简单的写几篇博客来进行介绍,所有的代码也都会开源,也希望读者能给个 star 哈 GitHub 地址:github.com/leavesC/…

分析Booking的150种机器学习模型,我总结了六条成功经验
(图片付费下载自视觉中国)作者 | Adrian Colyer译者 | Monanfei出品 | AI科技大本营(ID:rgznai100)本文是一篇有趣的论文(150 successful machine learning models: 6 lessons learned at Booking.com Bernadi et al.,…

Android官方提供的支持不同屏幕大小的全部方法
2019独角兽企业重金招聘Python工程师标准>>> 本文将告诉你如何让你的应用程序支持各种不同屏幕大小,主要通过以下几种办法: 让你的布局能充分的自适应屏幕根据屏幕的配置来加载合适的UI布局确保正确的布局应用在正确的设备屏幕上提供可以根据…

C++/C++11中头文件iterator的使用
<iterator>是C标准程序库中的一个头文件,定义了C STL标准中的一些迭代器模板类,这些类都是以std::iterator为基类派生出来的。迭代器提供对集合(容器)元素的操作能力。迭代器提供的基本操作就是访问和遍历。迭代器模拟了C中的指针,可以…

从多媒体技术演进看AI技术
(图片付费下载自视觉中国)文 / LiveVideoStack主编 包研在8月的LiveVideoStackCon2019北京开场致辞中,我分享了一组数据——把2019年和2017年两场LiveVideoStackCon上的AI相关的话题做了统计,这是数字从9.3%增长到31%,…

五. python的日历模块
一 .日历 import calendar# 日历模块# 使用# 返回指定某年某月的日历 print(calendar.month(2017,7))# July 2017 # Mo Tu We Th Fr Sa Su # 1 2 # 3 4 5 6 7 8 9 # 10 11 12 13 14 15 16 # 17 18 19 20 21 22 23 # 24 25 26 27 28 29 30 # 31# 返…

Linux下的Shell工作原理
为什么80%的码农都做不了架构师?>>> Linux系统提供给用户的最重要的系统程序是Shell命令语言解释程序。它不 属于内核部分,而是在核心之外,以用户态方式运行。其基本功能是解释并 执行用户打入的各种命令,实现用户与L…

C++/C++11中头文件functional的使用
<functional>是C标准库中的一个头文件,定义了C标准中多个用于表示函数对象(function object)的类模板,包括算法操作、比较操作、逻辑操作;以及用于绑定函数对象的实参值的绑定器(binder)。这些类模板的实例是具有函数调用运算符(functi…

飞天AI平台到底哪里与众不同?听听它的架构者怎么说
采访嘉宾 | 林伟 整理 | 夕颜 出品 | AI科技大本营(ID:rgznai100) 天下没有不散的宴席。 9 月 25 日,云栖大会在云栖小镇开始,历经三天的技术盛宴,于 9 月 27 日的傍晚结束。 三天、全球6.7万人现场参会、超1250万人…