委托和事件

事件的由来

上文说到委托的安全性不佳,于是我们要将委托本身私有化,但还要暴露若干方法让外界使用。其中最重要的必然就是为委托挂接方法和调用委托,以便间接地调用委托所代表方法。那么事件event关键字就是c#提供给我们的一个语法糖。他并没有任何新的东西,只是减少了一些代码。所以,事件是一种特殊的委托,其特征有:

1 和一个(基底)委托类型合作,当声明了一个具有该基底委托类型的事件之后,用户在外部就可以为这个事件挂接方法,其方法类型必须和基底委托类型相同

2 可以将事件视为委托的方法链,使用event关键字时,该方法链是私有的,而使用委托类型名作为关键字时,该方法链是公共的

3 事件虽然是私有的,但编译器自动为我们建立了两个隐藏方法,并重载了+=和-=,使得我们可以不编写任何额外的代码,利用+=和-=操作事件上的方法,从而控制当事件触发时执行什么方法

复制代码
public class Program{public static void Main() { //创建了一个新的订阅者 var c = new Car("Mycar", 0, 100); //还记得委托么,如果methodList是一个CarEngineHandler类型则可以这样写 //现在methodList是一个事件类型,这样写会报错 //事件只能出现在+=或者-=的左方,不能出现在等于号的左方 //故此时用户不能随意为其赋值,保证了安全 c.methodList = OnCarEvent1; //这是正确的 c.methodList += OnCarEvent1; //不能在外部这样调用一个事件 //c.methodList.Invoke() } public static void OnCarEvent1(string msg) { Console.WriteLine("***** message from car *****"); Console.WriteLine("=> " + msg); Console.WriteLine("****************************"); } public static void OnCarEvent2(string msg) { Console.WriteLine("=> " + msg.ToUpper()); } } public class Car { public string name { get; set; } public int currentSpeed { get; set; } public int MaxSpeed { get; set; } private bool isDead { get; set; } public delegate void CarEngineHandler(string message); public event CarEngineHandler methodList; public Car(string name, int currentSpeed, int MaxSpeed) { this.name = name; this.currentSpeed = currentSpeed; this.MaxSpeed = MaxSpeed; this.isDead = false; } public void Accel(int delta) { //死亡时执行订阅列表中的方法 if (isDead) { if (methodList != null) //在内部调用事件和调用委托没区别 methodList("Sorry, car is broken"); } else { currentSpeed += delta; if (currentSpeed >= MaxSpeed) isDead = true; else Console.WriteLine("Current speed: " + currentSpeed); } } }
复制代码

 

事件的代码规范

微软为事件设置了代码规范,和委托相同,这些规范主要有:

1 委托名必须以Handler结尾

2 委托必须有且仅有两个参数,第一个为object类型的sender,第二个则是一个集成了EventArgs类型的自定义类类型,名字可以自定

那么我们使用代码规范来重新写一下上面的例子:

复制代码
public class Program{public static void Main() { //创建了一个新的订阅者 var c = new Car("Mycar", 0, 100); c.methodList += OnCarEvent1; for (int i = 0; i < 10; i++) { c.Accel(20); } Console.ReadKey(); } public static void OnCarEvent1(object sender, CarEventArgs e) { //现在我们知道是谁触发了事件 Console.WriteLine("***** message from car: " + sender.ToString() + " *****"); Console.WriteLine("=> " + e.message); Console.WriteLine("****************************"); } } public class Car { public string name { get; set; } public int currentSpeed { get; set; } public int MaxSpeed { get; set; } private bool isDead { get; set; } //标准化的委托定义 //sender: 当发生事件时,告知订阅者是谁触发了事件 //e: 当发生事件时传递的信息(可以自定义一个类继承EventArgs类,故可以传递任意类型的信息) public delegate void CarEngineHandler(object sender, CarEventArgs e); public event CarEngineHandler methodList; public Car(string name, int currentSpeed, int MaxSpeed) { this.name = name; this.currentSpeed = currentSpeed; this.MaxSpeed = MaxSpeed; this.isDead = false; } public void Accel(int delta) { //死亡时执行订阅列表中的方法 if (isDead) { if (methodList != null) //在内部调用事件和调用委托没区别 methodList(name, new CarEventArgs { message = "Sorry, this car is dead." } ); } else { currentSpeed += delta; if (currentSpeed >= MaxSpeed) isDead = true; else Console.WriteLine("Current speed: " + currentSpeed); } } } //自定义事件发生时发送的信息格式 public class CarEventArgs : EventArgs { public string message { get; set; } }
复制代码

 

sender和EventArgs

相信很多人在使用.net进行控件的拖拽时,如果拖拽一个按钮,然后双击它,就会发现代码多了几行(省略了无关的):

this.button1.Click += new System.EventHandler(this.button1_Click);
private void button1_Click(object sender, EventArgs e)
{
}

但可能不是所有人都明白那两个参数是做什么的,反正我在这个方法里写代码,然后点击那个按钮,编译器就会执行到这里面的代码。其实这正是符合微软命名规范的一个事件的例子。

1. 在初始化时,为这个对象的Click属性(注意该属性的类型是一个事件,他的基底委托是EventHandler类型的)绑定了button1_Click方法。此时如果这个事件被调用,那么就会执行button1_Click方法的代码。

EventHandler 是C#控件中最常见的委托,它没有返回类型:

public delegate void EventHandler (Object sender, EventArgs e)

我们注意到,这个委托正好和button1_Click方法签名相同,也就是说,我们可以将button1_Click方法加入到委托的方法链中。而在这里,方法链就是事件类型变量Click。

2. 当单击按钮时,经过一系列的消息轮询最终系统捕获到了你的单击动作,再经过一系列处理,最后到了Control.OnClick(System.EventArgs e)方法

3. 在这个方法里调用事件

复制代码
        protected virtual void OnClick(EventArgs e){//从Events委托集合中取出名为EventClick的委托 EventHandler handler = (EventHandler)base.Events[EventClick]; //如果不为空则执行这个委托上的方法,也就是button1_Click if (handler != null) { //this就是button1 handler(this, e); } }
复制代码

 

大略的整个过程就是:

1. 你的点击动作被windows捕获,windows把这个动作(this.button1.Click)作为系统消息发送给程序(底层消息轮询机制)

2. 程序从自己的消息队列中不断的取出消息,并在消息循环中寻找对应的处理方式

3. 对于这个消息,其sender(事件的来源)就是这个按钮,发送的消息全部在e里面,在某些事件里,e用处不大,比如在MouseEventArgs的Mouse事件中,可以看到e包括mouse的坐标值等,以供你的程序使用

4. 因为this.button1.Click挂接了方法button1_Click,故执行这里面的代码

所以说实际上当单击按钮时,其实编译器已经帮我们做了很多事情,例如委托的建立,事件的挂接和执行,这些都已经被封装到你根本就不需要知道也可以编写出来成功执行的代码的程度了。那么委托和事件这个话题差不多就结束了(在c#1.0这个层面上)。在更高版本的c#中,委托和事件被包装的更加简便易用了,基于泛型的委托action和func的出现,更是(基本上)完全替代了原生的委托delegate。这些精彩的内容当然要等待到介绍c#2和3的时候一块讲述。

转载于:https://www.cnblogs.com/lishuyi/p/4270213.html

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

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

相关文章

ios html高度自适应,iOS UILabel高度自适应终结篇

释放双眼&#xff0c;带上耳机&#xff0c;听听看~&#xff01;网上大部分的boundingRectWithSize和sizeWithFont 计算出来的宽高在某些有特殊情况下(如链接中有n等等)计算出来的还是有偏差不准&#xff0c;此时用NSAttributedString和label的attributedText计算会迎刃而解1.给…

HiveQL: 数据操作

文章目录1. 向管理表中装载数据2. 通过查询语句向表中插入数据3. 动态分区插入4. 从单个查询语句创建表并加载数据5. 导出数据学习自《Hive编程指南》 1. 向管理表中装载数据 hive (default)> load data local inpath "/home/hadoop/workspace/student.txt">…

formdata.append加多个值_redis的五种数据结构和应用场景:微博微信点赞+加购物车等...

Redis五种数据结构如下&#xff1a;1.String 字符串类型是redis中最基本的数据类型&#xff0c;一个key对应一个value。String类型是二进制安全的&#xff0c;意思是 redis 的 string 可以包含任何数据。如数字&#xff0c;字符串&#xff0c;jpg图片或者序列化的对象。2.Hash …

bakaxl启动器怎么导入整合包_bakaxl启动器加皮肤光影mod

bakaxl启动器加皮肤光影mod是一款超级有趣的像素风格的冒险类的手游哦&#xff0c;此次为玩家带来的是不一样的游戏模组哦&#xff0c;在这里玩家可以拥有超级多的任务可以进行&#xff0c;你可以随时开启地图探索哦&#xff0c;你还可以将全新获得的材质包加入其中&#xff0c…

iOS开发-自动隐藏键盘及状态栏

1.隐藏状态栏 iOS升级至7.0以后&#xff0c;很多API被废止&#xff0c;其中原有隐藏状态栏StatusBar的方法就失效了。 原有方案 [[UIApplication sharedApplication] setStatusBarHidden:YES]; 但很不幸&#xff0c;在后来的版本中实效了&#xff0c;因此我们可以使用新的API来…

python gevent模块 下载_【python安全攻防】包、模块、类、对象

终于又到了一周一度的整理博客的时间了&#xff0c;博主平时课余时间看书&#xff0c;周末统一整理&#xff0c;坚持周更真是爱了爱了 &#xff5e;今天要说的是python面向对象这一部分的内容&#xff0c;今天这是基础篇的第二篇&#xff0c;也是最后一篇。说来基础篇还真是少呢…

LeetCode LCP 33. 蓄水(暴力枚举)

文章目录1. 题目2. 解题1. 题目 给定 N 个无限容量且初始均空的水缸&#xff0c;每个水缸配有一个水桶用来打水&#xff0c;第 i 个水缸配备的水桶容量记作 bucket[i]。小扣有以下两种操作&#xff1a; 升级水桶&#xff1a;选择任意一个水桶&#xff0c;使其容量增加为 buck…

svr公式推导_ML-支持向量:SVM、SVC、SVR、SMO原理推导及实现

目录1.导出目标2拉格朗日转换3对偶问题&#xff1a;因为是希望得出L最小时的一些参数w,b,a&#xff0c;但是目前很难一起求得最佳参数&#xff0c;所以换个思路。因为&#xff1a;所以能够容易的计算出拉格朗日乘子a约束时的最坏情况是&#xff1a;但是m个a的值还是无法求出&am…

302状态码_你见过 HTTP 哪些状态码?

❝好久没有写技术文章&#xff0c;今天在四川广元无事&#xff0c;总结一篇。附一张今天早上在嘉陵江遇见的白鹡鸰 (不是我拍的)❞白鹡鸰101 Switch Protocol200 Ok201 Created204 No Content206 Partial Content301 Moved Permanently302 Found304 Not Modified307 Temporary …

羽毛球 机器人 Robocon 2015 泰国预选赛(全国大学生机器人竞赛)

羽毛球 机器人 Robocon 2015 泰国预选赛(全国大学生机器人竞赛) 我把视频传我的优酷上了, 大家可以看看 http://i.youku.com/pomodori posted on 2015-02-04 11:26 rex686568 阅读(...) 评论(...) 编辑 收藏 转载于:https://www.cnblogs.com/Pomodori/p/4316622.html

山西大学计算机应用专业,山西大学计算机应用技术专业

在主要课程学完并确定论文题目后&#xff0c;研究生撰写论文的时间为一年半至两年。(一)论文选题的要求1. 选题必须有一定的理论意义或应用价值论文选题必须对国民经济建设或在学术上有一定的价值。2&#xff0e;国内外研究动态论文选题时&#xff0c;学生必须掌握与该课题有关…

LeetCode LCP 34. 二叉树染色(树上DP)

文章目录1. 题目2. 解题1. 题目 小扣有一个根结点为 root 的二叉树模型&#xff0c;初始所有结点均为白色&#xff0c;可以用蓝色染料给模型结点染色&#xff0c;模型的每个结点有一个 val 价值。 小扣出于美观考虑&#xff0c;希望最后二叉树上每个蓝色相连部分的结点个数不能…

uart口图片_uart 加强了的串口调试助手,可以自动记录传输数据,并且显示图片,示波器等功能 Com Port 编程 267万源代码下载- www.pudn.com...

文件名称: uart下载 收藏√ [5 4 3 2 1 ]开发工具: C#文件大小: 10479 KB上传时间: 2014-06-06下载次数: 62提 供 者: 林元峰详细说明&#xff1a;加强了的串口调试助手&#xff0c;可以自动记录传输数据&#xff0c;并且显示图片&#xff0c;示波器等功能-Enhanced seri…

delphi 串口通信发送_关于串口通信232、485、422和常见问题,就没见过能讲这么清楚的...

先讲串口通信的一些基本概念&#xff0c;术语。如果对串口通信比较熟悉的&#xff0c;就当复习&#xff0c;如果哪里讲的不到位&#xff0c;欢迎及时指出。这里并不对串口的编程作讲解&#xff0c;主要是从应用的角度去讲一讲。因为更多的时候&#xff0c;都是产品做好了&#…

LeetCode 1822. 数组元素积的符号

文章目录1. 题目2. 解题1. 题目 已知函数 signFunc(x) 将会根据 x 的正负返回特定值&#xff1a; 如果 x 是正数&#xff0c;返回 1 。如果 x 是负数&#xff0c;返回 -1 。如果 x 是等于 0 &#xff0c;返回 0 。 给你一个整数数组 nums 。 令 product 为数组 nums 中所有元…

快速替换图片的组合-AE-样片!

模板下载网址&#xff1a;http://pan.baidu.com/s/1hqCbErM转载于:https://www.cnblogs.com/nedtwo/p/4278337.html

南昌理工学院计算机网络技术专业怎么样,南昌理工学院怎么样 重点专业是什么...

毕业季即将来临&#xff0c;报考的时候同学们和家长朋友们最关心的就是学校好不好的问题&#xff0c;想要了解学校有哪些特色专业、师资力量怎么样。下面小编整理了南昌理工学院的信息&#xff0c;供大家参考。南昌理工学院重点专业国家级特色专业&#xff1a;计算机科学与技术…

英特尔cpu发布时间表_英特尔10nm芯片开始大规模出货,先进制程时间表浮出水面...

多年延期之后&#xff0c;英特尔终于宣布其 10nm 芯片产品开始大量出货。近日&#xff0c;英特尔公布了公司 2019 年 Q3 财报。在财报会议中&#xff0c;英特尔透露了这一消息。具体而言&#xff0c;英特尔已有晶圆厂开始大批量生产 10nm 芯片&#xff0c;未来也还准备建立新厂…

matplotlib绘图_使用matplotlib库绘图

本代码演示对列表元素进行绘图并可视化代码如下&#xff1a;import matplotlib.pyplot as plt #导入matplotlib绘图库&#xff0c;并设置简称为pltlist1[i*2 for i in range(1,10)] #遍历range(1,10)里的元素&#xff0c;并每个乘以2&#xff0c;并将最终的列表赋值给list1plt.…

LeetCode 1824. 最少侧跳次数(DP)

文章目录1. 题目2. 解题1. 题目 给你一个长度为 n 的 3 跑道道路 &#xff0c;它总共包含 n 1 个 点 &#xff0c;编号为 0 到 n 。 一只青蛙从 0 号点第二条跑道 出发 &#xff0c;它想要跳到点 n 处。然而道路上可能有一些障碍。 给你一个长度为 n 1 的数组 obstacles &a…