这篇博客是我昨天写的,文中的观点有些问题,后经过网友留言和个人学习发现错误,原文还是保留,更改补在后面,不怕贻笑大方,唯恐误人子弟。不知道还能不能放在首页,让被误导的同学再被反误导一次。

一、原文

几乎所有的面试题都会问:事件是委托吗,说说委托和事件的联系和区别?每次答这个题都很蛋疼,因为把它们的关系说简单了就描述不准确,想说清楚就不是一两句话的事了。我通常在回答中加这么一句:委托与事件的关系好比字段与属性的关系。很多人理解它们的关系时也做这样的类比,虽然简单一句话概括了它们的关系,但总不能让我感到满意。

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#基础原理拾遗——面试都爱问的委托和事件(纠正)的更多相关文章

  1. C#基础原理拾遗——引用类型的值传递和引用传递

    C#基础原理拾遗——引用类型的值传递和引用传递 以前写博客不深动,只搭个架子,像做笔记,没有自己的思考,也没什么人来看.这个毛病得改,就从这一篇开始… 最近准备面试,深感基础之重要,奈何我不是计算机科 ...

  2. 应届生/社招面试最爱问的几道Java基础问题

    本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错 ...

  3. BATJ都爱问的多线程面试题

    # 一 面试中关于 synchronized 关键字的 5 连击 ### 1.1 说一说自己对于 synchronized 关键字的了解 synchronized关键字解决的是多个线程之间访问资源的同 ...

  4. 面试 HTTP ,99% 的面试官都爱问这些问题

    HTTP 和 HTTPS 的区别 HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol),HTTP 是一个在计算机世界里专门在两点之间传输文字.图片.音频.视频等超 ...

  5. 这些Servlet知识你一定要知道,金九银十大厂面试官都爱问

    前言 Servlet是服务器端的Java应用程序,可以生产动态Web页面.透过JSP执行过程可以知道JSP最终被编译成一个.class文件,查看该文件对应的Java类,发现该Java类继承自org.a ...

  6. 阿里四面P7稳了,得亏我会这些Spring面试题,果然大厂都爱问它们

    前言 先说一下本人情况吧,末流985毕业,毕业之后一直在一家不大不小的公司里安稳上班.上半年因为疫情的原因公司调整了工资,我也是随波逐流跟随大家辞了职.辞职之后向阿里.字节这些都投了简历(但是只收到了 ...

  7. C#基础原理拾遗——引用类型的值传递和引用传递

    以前写博客不深动,只搭个架子,像做笔记,没有自己的思考,也没什么人来看.这个毛病得改,就从这一篇开始- 最近准备面试,深感基础之重要,奈何我不是计算机科班出身,基础方面有些捉襟见肘.短期怎么补?做面实 ...

  8. 面试都在问的微服务、服务治理、RPC、下一代微服务框架... 一文带你彻底搞懂!

    文章每周持续更新,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 单体式应用程序 与微服务相对的另一个概念是传统的单体式应用程序( ...

  9. 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!

    ❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...

随机推荐

  1. UISearchDisplayController隐藏navigationBar需注意

    不能调用self.navigationController.navigationBar.hidden = YES: 调用此代码的话,你隐藏了navigationBar搜索展示控制器就拿不到导航条:就会 ...

  2. canvas基础2--绘制图形

    栅格 绘制矩形 不同于SVG,HTML中的元素canvas只支持一种原生的图形绘制:矩形.所有其他的图形的绘制都至少需要生成一条路径.不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能. 首先 ...

  3. 五.CSS盒子模型

    所谓盒模型,就是浏览器为每个HTML元素生成的矩形盒子.即HTML页面实际上就是由一系列盒子组成.这些盒子是按照可见版式在页面上排布的.并由三个属性进行控制:position属性,display属性, ...

  4. 将java项目打包成jar文件并在cmd运行

    保证java项目不报错能够通过运行 选择export 选择一个你要运行的程序入口Main方法 打开cmd,前提是你的javajdk及环境变量配置完成

  5. 谷歌浏览器Chrome不再支持showModalDialog的解决办法

    问题重现 弹出窗口编码: JavaScript 0 1 2 3 4 5 6 7   var obj = new Object();   var retval = window.showModalDia ...

  6. iOS 复杂tableView的 cell一般设计思路

  7. cocos2d-x中Node中重要的属性

    Node还有两个非常重要的属性:position和anchorPoint. position(位置)属性是Node对象的实际位置.position属性往往还要配合使用anchorPoint属性,为了将 ...

  8. 导航栏 UITabBarController等颜色的区别

    //tint color是设置你选中的那个tabBar的颜色,默认是蓝色,点击是设置的红色    vc.tabBar.tintColor = [UIColor redColor];        // ...

  9. Mysql 存储程序

    #1存储过程create procedure greeting() BEGIN # 77 = 16 FOR username + 60 for hostname + 1 for '@' DECLARE ...

  10. 使用FormData上传文件、图片

    关于FormData XMLHttpRequest Level 2添加了一个新的接口  ---- FormData 利用FormData对象,可以通过js用一些键值对来模拟一系列表单控件,可以使用XM ...