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」「服务治理」「下一代微服务」一文带你彻底搞懂!
❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...
随机推荐
- 初识 Asp.Net内置对象之Response对象
Response对象 Respose对象用于将数据从服务器发送回浏览器.它允许将数据作为请求的结果发送到浏览器,并提供有光响应的信息,可以用来在页面中输入数据,在页面中跳转,还可以传递各个页面的参数, ...
- Codevs 1287 矩阵乘法&&Noi.cn 09:矩阵乘法(矩阵乘法练手题)
1287 矩阵乘法 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 小明最近在为线性代数而头疼, ...
- AI-->从新建文档开始说起,串联相关色彩知识
相关概念:AI.PS.矢量图形 AI: Adobe Illustrator 是Adobe公司出品的一款用于矢量图形设计的软件. 矢量图形:用通俗的大白话讲与分辨率无关,可以任意的放大缩小而不会失真图 ...
- 基于ASP.Net +easyUI框架上传图片,实现图片上传,提交表单
<body> <link href="../../Easyui/themes/easyui.css" rel="stylesheet" typ ...
- list转换成DataTable
list转换成DataTable类如下: public static DataTable ToDataTable<T>(this IList<T> datas) { DataT ...
- JavaScript之放大镜效果2
在放大图片效果的同时,我们怎么原图和放大窗体增加间隔呢? 我们只需应用一个table就行了: 源码上: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML ...
- JavaScript之动画3
给一个div添加颜色,使其产生渐变效果,我们设置index为变量,使用setInterval函数方法改变rgb颜色值. window.onload = function(){ var boxDom = ...
- 【转】K3Cloud 二次开发 单据转换系列
Entity, EntryEntity, SubEntryEntity 这三个对象具有继承关系:Entity 是实体基类,用于定义各种实体的公共属性:EntryEntity 是单据体实体类,从Enti ...
- 研究validation插件到现在的感受
1.比较累 2.看了几十个页面参考是有的,要抓住问题的实质,实质在于要改插件代码.因为它本身不提供这个方法. 3.对了,还没有描述这个问题,问题就是再次验证时,成功的样式不消失.解决方法如下: 修改了 ...
- SQLSERVER中按年月分组
SQLSERVER中按年月分组 一个表有三个字段id,dt,d 分别存放id,时间,数值 id dt d 1 2004-08-11 12:12:00.000 9 2 2005-09- ...