C#基础原理拾遗——面试都爱问的委托和事件(纠正)
这篇博客是我昨天写的,文中的观点有些问题,后经过网友留言和个人学习发现错误,原文还是保留,更改补在后面,不怕贻笑大方,唯恐误人子弟。不知道还能不能放在首页,让被误导的同学再被反误导一次。
一、原文
几乎所有的面试题都会问:事件是委托吗,说说委托和事件的联系和区别?每次答这个题都很蛋疼,因为把它们的关系说简单了就描述不准确,想说清楚就不是一两句话的事了。我通常在回答中加这么一句:委托与事件的关系好比字段与属性的关系。很多人理解它们的关系时也做这样的类比,虽然简单一句话概括了它们的关系,但总不能让我感到满意。
1、委托与事件到底什么关系?
当我们谈委托与事件的关系时,是说委托这种类型和事件这种类型的关系呢,还是具体的委托对象和事件对象之间的关系?我以为是前者。那么委托和事件是两种类型,而字段和属性是具体的对象,虽然都是封装,我觉得两者之间还是有区别的,事件对委托的封装是在类级别的、抽象层次、稳定的封装,而属性对字段的封装是在对象级别的、具体的、可自定义的封装。(个人理解,后来证明有偏差)由此造成的最直观的区别就是,在发布者类中使用事件时,不需要提供对应的委托对象;而在类中使用属性时,一般要提供对应的字段让属性来进行封装。
Reflector查看类之间的继承关系如下:

MSDN中有这么一句:“事件是特殊类型的多路广播委托,仅可从声明它们的类或结构(发行者类)中调用。”如此看来事件与委托的关系应该是继承关系,在继承的过程中在EventHandler类中进行的封装。
以上是从理论的角度分析了事件和委托的关系,下面通过代码加强认识。
2、委托模拟事件
实现一个非常简单的功能:控制台输入一个数,如果输入100的话就显示”Game Over”。用事件和委托模拟的事件分别实现该功能。
class Program
{
static void Main(string[] args)
{
//事件实现
Game1 game = new Game1();
game.GameOverEvent += game_GameOver; ////委托实现
//Game2 game = new Game2();
//game.AddMethod(game_GameOver); while (true)
{
Console.WriteLine("输入:"); game.Scroe = Convert.ToInt32(Console.ReadLine());
}
} static void game_GameOver(object sender, EventArgs e)
{
Console.WriteLine("Game Over");
} static void game_GameOver()
{
Console.WriteLine("Game Over");
}
} //事件实现
class Game1
{
//event 关键字用于在发行者类中声明事件。
public event EventHandler GameOverEvent; private int scroe; public int Scroe
{
get { return scroe; }
set
{
this.scroe = value;
if (this.GameOverEvent != null)
{
if (value == )
{
this.GameOverEvent(this, new EventArgs());//触发事件
}
}
}
}
} //委托实现
class Game2
{
public delegate void OverDelegate(); //将委托声明为private,防止订阅者直接调用,使用new等功能
private OverDelegate GameOverDelegate; //AddMethod和RemoveMethod模拟事件的+=和-=赋值
public void AddMethod(OverDelegate over)
{
this.GameOverDelegate += over;
} public void RemoveMethod(OverDelegate over)
{
this.GameOverDelegate -= over;
} private int scroe; public int Scroe
{
get { return scroe; }
set
{
this.scroe = value;
if (this.GameOverDelegate != null)
{
if (value == )
{
this.GameOverDelegate();//触发委托
}
}
}
}
}
以上Game2类中,委托对事件的模拟即是在对象级别做的封装,AddMethod和RemoveMethod方法是在Game2类中实现的,而不是在OverDelegate委托中实现的,因此事件的模拟依赖了OverDelegate和Game2两个类。而Game1中EventHandler类本身就封装了委托,限制了其在外部(订阅者)的实例化,这种功能的实现没有依赖于Game1。所以事件的这种内部封装机制减少了依赖,符合松耦合要求。
以上是我个人的一点理解,有失偏颇之处还望批评指正。
然而EventHandler内部是怎么封装的呢?我的思路尚不清晰,Reflector查看也没看出个所以然,我自己会认真学习探索,在此也向各位请教,希望大家能告诉我答案或提供一些思路。为谢!
二、改正
3、其实不是EventHandler一人的功劳
在上文中我以为事件对委托的封装是EventHandler类一人的功劳,其实没有深入理解的话很容易这么想,因为在声明事件的时候并没有声明对应的委托,直观上就感觉这个封装是发生在EventHandler类内部的。看了 @小AI 给我推荐的http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html一文,发现问题所在。
在Reflector中反编译上面的例子,Game1类的结构如下:

Game1在此就是发布者类,对事件GameOverEvent的封装还是在发布者类中完成的,并不是在EventHandler类中就已经完成。这种封装模式和属性对字段的封装模式是完全一样的,只不过这个封装过程是由于有了event关键字在编译阶段由编译器自动完成的,在发布者类中将EventHandler类型的GameOverEvent委托设为私有,并加上Add和Remove方法,和上面用委托模拟的事件是一样的,也就是事件的本质就是在小标题2中用委托模拟的事件。这么说来,事件对委托的封装是可以类比为属性对字段的封装的,如果说有区别,那就是前者是编译阶段完成的、不可自定义的封装,后者是coding阶段实现的、可自定义的封装,但它们都是在对象级别完成的。
虽然这次出了错,但并没有使我灰心,相反,如果我不把自己的想法拿出来和大家交流,可能问题永远得不到解决。唯一的弊端就是大胆的写自己的想法,如果是错误的话,很可能误导比我还菜的同学,我能做的就是尽量多方求证,并且出错后及时改正。也勉励和我一样的菜鸟多写博客,别怕暴漏错误,大家对于大牛中规中矩的blog已经看的boring了,小菜错误的想法里可能偶尔就有着不被规则约束的创新。hehe
C#基础原理拾遗——面试都爱问的委托和事件(纠正)的更多相关文章
- C#基础原理拾遗——引用类型的值传递和引用传递
C#基础原理拾遗——引用类型的值传递和引用传递 以前写博客不深动,只搭个架子,像做笔记,没有自己的思考,也没什么人来看.这个毛病得改,就从这一篇开始… 最近准备面试,深感基础之重要,奈何我不是计算机科 ...
- 应届生/社招面试最爱问的几道Java基础问题
本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错 ...
- BATJ都爱问的多线程面试题
# 一 面试中关于 synchronized 关键字的 5 连击 ### 1.1 说一说自己对于 synchronized 关键字的了解 synchronized关键字解决的是多个线程之间访问资源的同 ...
- 面试 HTTP ,99% 的面试官都爱问这些问题
HTTP 和 HTTPS 的区别 HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol),HTTP 是一个在计算机世界里专门在两点之间传输文字.图片.音频.视频等超 ...
- 这些Servlet知识你一定要知道,金九银十大厂面试官都爱问
前言 Servlet是服务器端的Java应用程序,可以生产动态Web页面.透过JSP执行过程可以知道JSP最终被编译成一个.class文件,查看该文件对应的Java类,发现该Java类继承自org.a ...
- 阿里四面P7稳了,得亏我会这些Spring面试题,果然大厂都爱问它们
前言 先说一下本人情况吧,末流985毕业,毕业之后一直在一家不大不小的公司里安稳上班.上半年因为疫情的原因公司调整了工资,我也是随波逐流跟随大家辞了职.辞职之后向阿里.字节这些都投了简历(但是只收到了 ...
- C#基础原理拾遗——引用类型的值传递和引用传递
以前写博客不深动,只搭个架子,像做笔记,没有自己的思考,也没什么人来看.这个毛病得改,就从这一篇开始- 最近准备面试,深感基础之重要,奈何我不是计算机科班出身,基础方面有些捉襟见肘.短期怎么补?做面实 ...
- 面试都在问的微服务、服务治理、RPC、下一代微服务框架... 一文带你彻底搞懂!
文章每周持续更新,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 单体式应用程序 与微服务相对的另一个概念是传统的单体式应用程序( ...
- 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!
❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...
随机推荐
- 转: HHVM at Baidu
评注: 一个项目迁移的问题考虑与实现使用.非常之详细. 转:http://lamp.baidu.com/2014/11/04/hhvm-in-baidu/ 在这之前我们介绍了我们为什么要迁移PHP到H ...
- List<T>.Sort() 排序的用法
List<T> 可以通过 .Sort()进行排序,但是当 T 对象为自定义类型时(比如自定义模型),就需要 IComparable接口重写其中的方法来实现,实现代码如下: class Pr ...
- 【转】https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题
正需要这个,写的很好,就转过来了 转自: http://www.cnblogs.com/naniannayue/ 一:什么是https SSL(Security Socket Layer)全称 ...
- 正则表达式删除指定的HTML 标签
1.抓取某网页的数据后(比如描述),如果照原样显示的话,可能会因为它里面包含没有闭合的HTML标签而打乱了格式,也可能它里面用了比较让人 "费解" 的HTML标签,把预订的格式搅乱 ...
- 面向对象的异常处理之深入理解java异常处理机制
什么是异常? 异常是对问题的描述,将问题的对象进行封装: 异常体系的特点:异常体系中的所有类以及建立的对象: 都具有可抛性,也就是说可以被throw和throws关键字所操作,只有异常体系具有该特点: ...
- 关于Windows® API Code Pack for Microsoft® .NET Framework
相比之前的操作系统,Window 7(or Vista)提供了很多新特性,我们在应用实现中可以利用这些特性来提升用户体验. 这些特性主要包括以下几个方面: Shell Enhancements Dir ...
- JAVA:二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题(5)
一.二进制,位运算,移位运算 1.二进制 对于原码, 反码, 补码而言, 需要注意以下几点: (1).Java中没有无符号数, 换言之, Java中的数都是有符号的; (2).二进制的最高位是符号位, ...
- daily news新闻阅读客户端应用源码(兼容iPhone和iPad)
daily news新闻阅读客户端应用源码(兼容iPhone和iPad),也是一款兼容性较好的应用,可以支iphone和ipad的阅读阅读器源码,设计风格和排列效果很不错,现在做新闻资讯客户端的朋友可 ...
- 开始进入Windows Phone 8开发
和大家一起分享从零开始,入手Windows Phone 8开发,大家一起来吧!
- 方便实用的jQuery checkbox复选框全选功能
// 主复选框 <input type="checkbox" id="ck" name="ckAll">// 子复选框项 < ...