引言:

前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到“事件”这个概念的,尤其是写UI的时候,当我们点击一个按钮后VS就会自动帮我们生成一些后台的代码,然后我们就只需要在Click方法里面写代码就可以,所以可能有些刚接触C#的朋友就觉得这样很理所当然的,也没有去思考这是为什么的,为什么点击下事件就会触发我们在Click方法里面写的代码呢?事件到底扮演个什么样的角色呢?为了解除大家的这些疑惑,下面就详细介绍了事件,让一些初学者深入理解C#中的事件的概念。

一、为什么C#中会有事件的?

  前面专题中介绍了我理解的为什么需要委托的,所以这里我来分享下我理解的为什么C#中要引入事件这个概念的。下面就简单讲讲生活中事件的例子的,最近我生日刚过完的,我就以生日这个话题要谈谈的,日子一天天的过去,当生日的日期到的时候,这时候就触发了生日事件的,此时过生日的人就是触发生日事件的对象的,然后有些关系你的朋友就会对这个事件进行关注,一旦这个事件触发, 他们就可能会陪你一起庆祝生日,然后送礼物给你,当然并不是所有人都会对你的生日关注的,有些人肯定根本就不知道的, 只有对于你生日事件进行了关注了的人才会送礼物给你。这样的生活中的一个生日过程,然而对于为什么C#中会有事件这个概念当然就更好理解了,C#是一个面向对象的语言,我们使用C#语言进行编码也是为了用代码帮助我们完成现实生活中的事情的,所以当然也就必须有事件来反映生活中发生事情的情况了。

二、自己如何实现一个事件模式的?

  现在我们知道了为什么C#要引入事件了,但是对于我们在代码中使用的事件大部分都是.net类库为我们提供的,例如控件的各种事件,我们只需要点击按钮后就会触发点击事件的,但是我们很想理解这个事件是如何触发的,我们是否可以自己定义实现事件模式的一个程序的呢?答案当然是可以的,下面就以上面生日的例子来通过代码来解释下如何实现一个事件模式的。

具体代码为:

using System;
using System.Threading;

namespace BirthdayEventDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 实例化一个事件源对象
            Me eventSource = new Me("Learning Hard");

            // 实例化关注事件的对象
            Friend1 obj1 = new Friend1();
            Friend2 obj2 = new Friend2();

            // 使用委托把对象及其方法注册到事件中
            eventSource.BirthDayEvent+=new BirthDayEventHandle(obj1.SendGift);
            eventSource.BirthDayEvent+=new BirthDayEventHandle(obj2.Buycake);

            // 事件到了触发生日事件,事件的调用
            eventSource.TimeUp();
            Console.Read();
        }
    }
    // 第一步: 定义一个类型用来保存所有需要发送给事件接收者的附加信息
    public class BirthdayEventArgs : EventArgs
    {
        // 表示过生日人的姓名
        private readonly string name;

        public string Name
        {
            get { return name;}
        }

        public BirthdayEventArgs(string name)
        {
            this.name = name;
        }
    }
   // 第二步:定义一个生日事件,首先需要定义一个委托类型,用于指定事件触发时被调用的方法类型
    public delegate void BirthDayEventHandle(object sender, BirthdayEventArgs e);
    // 定义事件成员
    public class Subject
    {
        // 定义生日事件
        public event BirthDayEventHandle BirthDayEvent;

        // 第三步:定义一个负责引发事件的方法,它通知已关注的对象(通知我的好友)
        protected virtual void Notify(BirthdayEventArgs e)
        {
            // 出于线程安全的考虑,现在将对委托字段的引用复制到一个临时字段中
            BirthDayEventHandle temp = Interlocked.CompareExchange(ref BirthDayEvent, null, null);
            if (temp != null)
            {
                // 触发事件,与方法的使用方式相同
                // 事件通知委托对象,委托对象调用封装的方法
                temp(this, e);
            }
        }
    }

    // 定义触发事件的对象,事件源
    public class Me : Subject
    {
        private string name;
        public Me(string name)
        {
            this.name = name;
        }
        public void TimeUp()
        {
            BirthdayEventArgs eventarg = new BirthdayEventArgs(name);
            // 生日到了,通知朋友们
            this.Notify(eventarg);

        }
    }

    // 好友对象
    public class Friend1
    {
        public void SendGift(object sender,BirthdayEventArgs e)
        {
            Console.WriteLine(e.Name+" 生日到了,我要送礼物");
        }
    }
    public class Friend2
    {
        public void Buycake(object sender, BirthdayEventArgs e)
        {
            Console.WriteLine(e.Name + " 生日到了,我要准备买蛋糕");
        }
    }
}

运行结果为:

三、编译器是如何解释事件的呢?

  上面我们已经介绍了如何去实现自己去实现一个事件模式的,大家可以展开代码来具体的查看的,实现过程主要是——定义触发对象的事件源(指的是谁过生日)->定义关注你生日事件的朋友对象-> 方法登记对事件的关注,当事件触发时通知登记的方法被调用。然而相信大家还有有疑问——到底C#中的事件是什么呢?编译器又是如何去解释它的?下面就为大家解除下疑惑的:

  首先事件其实就是委托的(确切的说事件就是委托链),从上面的代码中,我们定义的事件除了使用event关键字外,还用到了一个委托类型,然而委托是一个类,类肯定就有属性字段的,然而我们就可以把事件理解为委托的一个属性,属性的返回值是一个委托类型。说事件是委托的一个属性,是有根据的,我们通过中间语言代码可以知道编译器是如何去解释我们定义的事件的。

 // 第二步:定义一个生日事件,首先需要定义一个委托类型,用于指定事件触发时被调用的方法类型
    public delegate void BirthDayEventHandle(object sender, BirthdayEventArgs e);
    // 定义生日事件
        public event BirthDayEventHandle BirthDayEvent;

当我们像上面定义一个事件时,编译器会把它转换为3段代码(大家可以通过IL反汇编程序来查看的):

     // 1. 一个被初始化为null的私有委托字段
       private BirthDayEventHandle BirthDayEvent =null;

        //2. 一个公共add_BirthDayEvent方法
        public void add_BirthDayEvent(BirthDayEventHandle value)
        {
            // 以一种线程安全的方式从事件中添加一个委托
        }
        // 3. 一个公共的remove_BirthDayEvent方法
        public void remove_BirthDayEvent(BirthDayEventHandle value)
        {
            // 以一种线程安全的方式从事件中移除一个委托
        }

第一段代码一个委托的私有字段,该字段是对一个委托列表的头部的引用,事件发生时会通知这个列表中的委托。字段初始化为null,表明无关注人登记了对事件的关注。
第二段代码是一个以add为前缀的方法,该方法是由编译器自动命名的,代码内容调用Delegate.Combine方法将委托实例添加到委托列表中,返回新的列表地址,并将这个地址存回字段。

第三段代码也是一个方法,它使得一个对象注销对事件的关注,同样的方法体调用Delegate.Remove方法将委托实例从委托列表中删除,返回新的列表地址,并将这个地址存回字段中。(注,如果试图删除一个从未添加过的方法,Delegate.Remove方法在内部将不做任何事情,也就是说,不会抛出任何一次,也不会显示任何警告,事件的方法集合保持不变)。

同时大家也可以通过调试来说明事件是一个委托链的,大家可以在 eventSource.BirthDayEvent+=new BirthDayEventHandle(obj2.Buycake);这行代码设置一个断点调试的,下面是我调试过程中的一个截图,大家也可以自己调试看看的,这样将会更加理解事件是一个委托链的概念:

按F10运行一行后的截图

通过上面的截图,相信大家对于事件是一个委托链的概念相信会有进一步的理解的。

四、小结

  到这里本专题的内容也就介绍完了, 希望通过本专题,大家可以对事件有进一步的理解,理解事件与委托之间的关系。这个专题通过自己实现的一个事件模式里解释事件的本质,然而我们经常使用的是Net类库中定义好的事件,然而有些刚接触C#的人却不理解Net中定义的事件背后所做的事情,只是知道点下按钮后在Click方法里面写入自己的一些控制代码,然而背后的过程具体是怎样的,既然事件是委托,那么Click事件是委托类型,其中的委托类型又是怎么被实例化的呢?这些内容将在下一个专题给大家分享下的。

[C# 基础知识系列]专题四:事件揭秘 (转载)的更多相关文章

  1. [C# 基础知识系列]专题四:事件揭秘

    转自http://www.cnblogs.com/zhili/archive/2012/10/27/Event.html 引言: 前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听 ...

  2. [C# 基础知识系列]专题一:深入解析委托——C#中为什么要引入委托

    转自http://www.cnblogs.com/zhili/archive/2012/10/22/Delegate.html 引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不 ...

  3. [C# 基础知识系列]专题九: 深入理解泛型可变性

    引言: 在C# 2.0中泛型并不支持可变性的(可变性指的就是协变性和逆变性),我们知道在面向对象的继承中就具有可变性,当方法声明返回类型为Stream,我们可以在实现中返回一个FileStream的类 ...

  4. [C# 基础知识系列]专题五:当点击按钮时触发Click事件背后发生的事情 (转载)

    当我们在点击窗口中的Button控件VS会帮我们自动生成一些代码,我们只需要在Click方法中写一些自己的代码就可以实现触发Click事件后我们Click方法中代码就会执行,然而我一直有一个疑问的—— ...

  5. [C# 基础知识系列]专题七: 泛型深入理解(一) (转载)

    引言: 在上一个专题中介绍了C#2.0 中引入泛型的原因以及有了泛型后所带来的好处,然而上一专题相当于是介绍了泛型的一些基本知识的,对于泛型的性能为什么会比非泛型的性能高却没有给出理由,所以在这个专题 ...

  6. 方法字段[C# 基础知识系列]专题二:委托的本质论

    首先声明,我是一个菜鸟.一下文章中出现技术误导情况盖不负责 引言: 上一个专题已和大家分享了我懂得的——C#中为什么须要委托,专题中简略介绍了下委托是什么以及委托简略的应用的,在这个专题中将对委托做进 ...

  7. [C# 基础知识系列]专题十六:Linq介绍

    转自http://www.cnblogs.com/zhili/archive/2012/12/24/Linq.html 本专题概要: Linq是什么 使用Linq的好处在哪里 Linq的实际操作例子— ...

  8. [C# 基础知识系列]专题三:如何用委托包装多个方法——委托链 (转载)

    引言: 上一专题介绍了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的介绍都是委托只是封装一个方法,那委托能不能封装多个方法呢?因为生活中经常会听到, ...

  9. [C# 基础知识系列]专题二:委托的本质论 (转载)

    引言: 上一个专题已经和大家分享了我理解的——C#中为什么需要委托,专题中简单介绍了下委托是什么以及委托简单的应用的,在这个专题中将对委托做进一步的介绍的,本专题主要对委本质和委托链进行讨论. 一.委 ...

随机推荐

  1. expect使用

    expect时用与提供自动交互的工具.比如如果想要用ssh登陆服务器,每次都输入密码你觉得麻烦,那你就可以使用expect来做自动交互,这样的话就不用每次都输入密码了. 先看例子: #!/usr/bi ...

  2. 第八届蓝桥杯 4、方格分割 DFS

    标题:方格分割 6x6的方格,沿着格子的边线剪开成两部分. 要求这两部分的形状完全相同. 如图:p1.png, p2.png, p3.png 就是可行的分割法. 试计算: 包括这3种分法在内,一共有多 ...

  3. OLLVM特性、使用原理

    一.OLLVM特性 目前ollvm支持的特性有以下几种: 指令替换 -mllvm -sub 虚假控制流 -mllvm -bcf 打平控制流 -mllvm -fla 函数(Funtions)注解 二.指 ...

  4. Java使用RSA加密解密签名及校验

    RSA加密解密类: 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 32 33 ...

  5. C#实现数字字符串左补齐0的方法

    如下: ; , '); //0003 (推荐) s = string.Format("{0:d4}", n); //0003 再如: ; 方法1:Console.WriteLine ...

  6. Hive中数据的导入与导出

    最近在做一个小任务,将一个CDH平台中Hive的部分数据同步到另一个平台中.毕竟我也刚开始工作,在正式开始做之前,首先进行了一段时间的练习,下面的内容就是练习时写的文档中的内容.如果哪里有错误或者疏漏 ...

  7. 【nowcoder】 4th T2 区间

    题目链接:https://www.nowcoder.com/acm/contest/175/B 当你为时间复杂度挠头的时候 别人已经33行拿满分了 #include<cstdio> #in ...

  8. 华为S5700配置端口镜像和华三S5120配置802.1X认证记录

    一.说明 事情的起因是我们部门有个华为的S5700交换机,想配置端口镜像抓包但让助理买的串口线很久都还没到:而昨天测试部的同事说他们那有台华三的S5120想要配802.1X认证,但只有华为交换机的文档 ...

  9. oracle如何创建存储过程和调用

    oracle存储过程的创建语法 create or replace procedure 存储过程名称 ( --定义输入.输出参数-- 参数名1 in 参数类型, 参数名2 in 参数类型, 参数名3 ...

  10. java前的部分了解(计算机小白)

    一.加密 对称加密: des 3des AES rc4 (数据加密) 会话密钥 非对称加密(成对:公钥/私钥(一个加密一个解密)):RSA DSA 密钥交换 / 数字签名(用私钥加密摘要算法出的一串数 ...