`
devgis
  • 浏览: 134358 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

事件揭秘

 
阅读更多

引言:

前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到“事件”这个概念的,尤其是写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事件是委托类型,其中的委托类型又是怎么被实例化的呢?这些内容将在下一个专题给大家分享下的。

分享到:
评论

相关推荐

    onscroll事件揭秘

    onscroll事件揭秘

    android开发揭秘PDF

    4.1.2 事件处理 4.2 常用控件应用 4.2.1 文本框(Textiew) 4.2.2 列表(ListView) 4.2.3 提示(T0ast) 4.2.4 编辑框(EditText) 4.2.5 单项选择(RadioGroup、RadioButton 4.2.6 多项选择(CheckBox) 4.2.7 下拉列表...

    WPF揭秘 第一章 wpf 开发

     第2章 XAML揭秘   2.1 XAML定义   2.2 元素和特性   2.3 命名空间   2.4 属性元素   2.5 类型转换器   2.6 标记扩展   2.7 对象元素的子元素   2.7.1 内容属性   2.7.2 集合项   ...

    Silverlight揭秘.pdf

    《Silverlight揭秘》涵盖了Silverlight XAML 的方方面面,深入探讨了Silverlight 的特性,如图形、文字、音频、视频、动画和事件等,也讲述了如何使用构建块创建可重用的控件,执行普通的任务(如拖放),并解释了如何...

    《Android应用开发揭秘》附带光盘代码.

    《Android应用开发揭秘》全部实例源代码,配合《Android应用开发揭秘》使用 前言  第一部分 准备篇  第1章 Android开发简介  1.1 Android基本概念  1.1.1 Android简介  1.1.2 Android的系统构架  1.1.3 ...

    Android应用开发揭秘pdf高清版

    《Android应用开发揭秘》内容全面,不仅详细讲解了Android框架、Android组件、用户界面开发、游戏开发、数据存储、多媒体开发和网络开发等基础知识,而且还深入阐述了传感器、语音识别、桌面组件开发、Android游戏...

    《Android应用开发揭秘》源码

     4.1.2 事件处理  4.2 常用控件应用  4.2.1 文本框(Textiew)  4.2.2 列表(ListView)  4.2.3 提示(T0ast)  4.2.4 编辑框(EditText)  4.2.5 单项选择(RadioGroup、RadioButton  4.2.6 多项选择(CheckBox)  ...

    WPF揭秘 第四章 wpf 开发

     第2章 XAML揭秘   2.1 XAML定义   2.2 元素和特性   2.3 命名空间   2.4 属性元素   2.5 类型转换器   2.6 标记扩展   2.7 对象元素的子元素   2.7.1 内容属性   2.7.2 集合项   ...

    WPF揭秘 第二章 wpf 开发

     第2章 XAML揭秘   2.1 XAML定义   2.2 元素和特性   2.3 命名空间   2.4 属性元素   2.5 类型转换器   2.6 标记扩展   2.7 对象元素的子元素   2.7.1 内容属性   2.7.2 集合项   ...

    WPF揭秘 第三章 wpf 开发

     第2章 XAML揭秘   2.1 XAML定义   2.2 元素和特性   2.3 命名空间   2.4 属性元素   2.5 类型转换器   2.6 标记扩展   2.7 对象元素的子元素   2.7.1 内容属性   2.7.2 集合项   ...

    Spring的IoC容器(《Spring揭秘》的精选版)

    迷你书是《Spring揭秘》的精选版,节选了原书中介绍Spring IoC...5.3 容器内部事件发布 5.4 多配置模块加载的简化 5.5 小结 Spring IoC容器之扩展篇 6.1 Spring 2.5的基于注解的依赖注入 6.2 Spring 3.0展望 6.3 小结

    ASP.NET.4揭秘

    asp.net 4揭秘.第1卷》 第一部分 构建asp.net页面 第1章 asp.net framework概览2 1.1 asp.net和.net framework5 1.1.1 框架类库5 1.1.2 公共语言运行库10 1.2 asp.net控件11 1.2.1 asp.net控件概览11 1.2.2 html控件...

    《Google Android 揭秘》(美) W.Frank Ableson 著

    3.2.7 掌握事件 72 3.3 使用资源 73 3.3.1 支持的资源类型 73 3.3.2 在Java中引用资源 74 3.3.3 通过XML资源定义视图和布局 76 3.3.4 值的外化 78 3.3.5 提供动画 81 3.4 理解AndroidManifest文件 81 3.5 小结 84 ...

    揭秘jbpm流程引擎内核设计思想及构架

    本文揭秘了jbpm引擎内核的设计思想和构架,但不是jbpm引擎的全部。请注意是“流程引擎内核”,而不是“流程引擎”。如果我们把流程引擎比作工作流系统的“发动机”,那么“引擎内核”则是一个引擎的“灵魂”。 流程...

    wpf揭秘基本介绍

    wpf,控件,事件,绑定,路由,xaml

    11 spring IoC容器中事件event消息的发送和接收内部工作原理揭秘慕课专栏1

    背景消息机制是使用消息通知的方式,解耦生产者与消费者。编程上体现的是职责分割,使得消息处理的扩展性得到增强,符合设计原则中的单一职责以及开闭原则。Java 提供

Global site tag (gtag.js) - Google Analytics