简单聊聊c# 事件

引言:

前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到“事件”这个概念的,尤其是写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# 事件的详细内容,更多关于c# 事件的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解c# 委托链

    引言: 上一专题介绍了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的介绍都是委托只是封装一个方法,那委托能不能封装多个方法呢?因为生活中经常会听到,我代表大家的意见等这样的说话,既然委托也是一个代表,那他如果只能代表一个人,那他的魅力就不是很大了吧,所以我们就会委托能不能代表多个方法的? 答案是可以的,这就是本专题要讲的内容--委托链,委托链也是一个委托,只是因为它是把多个委托链在一起,所以我们就以委托链来这么称呼它的. 一.到底什么是委托链

  • C#中互操作性简介

    一.引言 这个系列是在C#基础知识中遗留下来的一个系列的,因为在C# 4.0中的一个新特性就是对COM互操作改进,然而COM互操作性却是.NET平台下其中一种互操作技术,为了帮助大家更好的了解.NET平台下的互操作技术,所以才有了这个系列.然而有些朋友们可能会有这样的疑问--"为什么我们需要掌握互操作技术的呢?" 对于这个问题的解释就是--掌握了.NET平台下的互操作性技术可以帮助我们在.NET中调用非托管的dll和COM组件..NET是建立在操作系统的之上的一个开发框架,其中.NET

  • Python调用C# Com dll组件实战教程

    之前公司有套C# AES加解密方案,但是方案加密用的是Rijndael类,而非AES的四种模式(ECB.CBC.CFB.OFB,这四种用的是RijndaelManaged类),Python下Crypto库AES也只有这四种模式,进而Python下无法实现C# AES Rijndael类加密效果了. 类似于这种C# 能实现的功能而在Python下实现不了的,搜集资料有两种解决方案,第一种方式,使用IronPython 直接调用C# dll文件,教程网上很多,不在赘述了,这种方式有个缺点,用的是ir

  • 如何在C#中调用COM组件

    一.引言 COM(Component Object Modele,组件对象模型)是微软以前推崇的一个开发技术,所以现在微软的很多产品都用到了COM组件,如Office,IE 等.然而如果.NET 平台下的程序想访问COM组件的方式来实现某个功能怎么办呢? 正是由于开发人员有这个需求,所以微软在.NET FrameWork中为COM和托管代码之间进行互操作提供了支持,这种互操作性的技术就是COM Interop. 但是COM Interop(COm互操作)这项技术,不仅支持在托管代码中使用COM对

  • C# 引入委托的目的是什么

    引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以有了这个系列,希望通过这个系列让朋友对C#的基础知识理解能够更进一步.然而委托又是C#基础知识中比较重要的一点,基本上后面的特性都和委托有点关系,所以这里就和大家先说说委托,为什么我们需要委托. 一.C#委托是什么的? 在正式介绍委托之前,我想下看看生活中委托的例子--生活中,如果如果我们需要打官司

  • c#对XML文档的创建与增删改查的示例代码

    一.创建的第一种方式 //1.创建一个XML文档 XmlDocument doc = new XmlDocument(); //2.创建第一行描述信息 XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null); //3.将创建的第一行描述信息添加到文档中 doc.AppendChild(dec); //4.给文档添加根节点 XmlElement Books = doc.CreateElem

  • c#创建vc可调用的com组件方法分享

    开发工具:VS2008 VS2008命令提示符(呵呵,这个你应该可以找到在什么地方吧) 附:本文适用任何VS系列工具. 在用C#创建COM时,一定要记住以下几点: 1:所要导出的类必须为公有: 2:所有属性.方法也必须为公有: 3:要导出的属性.方法必须用接口方式:如果没有在接口中声明,即使该方法(属性)为公有,也不能正常导出到COM.但他们可以被别的.NET程序所使用: 4:所有的事件也必须用接口方式: 现在我们开始正题: 一.新建一个Visual C#工程(习惯这种叫法了,现在应该叫解决方案

  • c# 委托的本质是什么

    引言 上一个专题已经和大家分享了我理解的--C#中为什么需要委托,专题中简单介绍了下委托是什么以及委托简单的应用的,在这个专题中将对委托做进一步的介绍的,本专题主要对委本质和委托链进行讨论. 一.委托的本质 平时我们很容易使用委托--用C# delegate关键字定义委托,再用new操作符构造委托实例,然后通过调用委托实例来调用回调方法(就是用一个了委托对象的变量来代替方法名,这句话如果刚接触的人不好理解的话,这里给个例子:MyDelegate mydelegate =new Mydelegat

  • 详解c# 数组(Array)

    数组是一个存储相同类型元素的固定大小的顺序集合.数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合. 声明数组变量并不是声明 number0.number1.....number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用 numbers[0].numbers[1].....numbers[99] 来表示一个个单独的变量.数组中某个指定的元素是通过索引来访问的. 所有的数组都是由连续的内存位置组成的.最低的地址对应第一个元素,最高的地址对应最后一个元

  • 深入分析c# 继承

    继承是面向对象程序设计中最重要的概念之一.继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易.同时也有利于重用代码和节省开发时间. 当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可.这个已有的类被称为的基类,这个新的类被称为派生类. 继承的思想实现了 属于(IS-A) 关系.例如,哺乳动物 属于(IS-A) 动物,狗 属于(IS-A) 哺乳动物,因此狗 属于(IS-A) 动物. 基类和派生类 一个类可以派生自多个

随机推荐