探寻C#事件本质

我最先在学习C#事件的时候,阅读了许多书籍,但总是不能对事件建立起一个比较清晰的概念,对其内部机制和原理也是似是而非,因为这些书籍在描述事件的时候总是夹杂许多其他不能理解的抽象术语,相信许多初学者都有这样的感觉。

同时,在利用IDE进行GUI编程时,IDE总是在内部帮助我们实现了事件必须的一些代码,我们只需双击对象,编事件处理函数就行了,而我们常常对内部的工作一无所知,或没有去主动理解这些自动生成的代码。

现在我试着回答学习时遇到的几个问题,这将会有助于你理解到底什么是事件。

如何定义一个事件?

语法:修饰符 event关键字 委托类名 事件名;

比如:public event somedelegatename myeventname;

事件到底是什么类型(引用?值?)

事件其实就是一个特殊的委托。

MSDN中的C#参考里这样描述:事件是特殊类型的多路广播委托,仅可从声明它们的类或结构(发行者类)中调用。如果其他类或结构订阅了该事件,则当发行者类引发该事件时,会调用其事件处理程序方法。

对于这一点,我们也可以从事件的定义中看出,不过就是在定义委托对象时加了event关键字表明这个委托现在叫事件。

何谓事件的订阅(subscribe)?

我们用VS2005建立一个只有一个按钮的winform程序,双击按钮,设计器会在Form1.Designer.cs中自动生成一行代码

 this.button1.Click += new System.EventHandler(this.button1_Click);

这就是事件的订阅!其实就是用事件处理函数this.button1_Click来实例化一个委托System.EventHandler(这个函数与委托拥有同样的签名,为什么需要有同样签名,我理解就是要保证委托与函数的类型一致,这样才能把函数句柄赋给一个委托对象,即所谓的用委托封装方法或C++中把一个方法句柄赋给一个函数指针),并把这个委托对象赋给事件Click(因这Click其实就是一个委托引用,所以可以这样做,从C++的角度来理解,就是把事件处理函数赋给了一个函数指针变量,这样就可以通过调用这个函数指针以执行事件处理函数)

一句话,订阅事件就是将某个委托对象指向一个具体的方法。

什么是引发事件?

当程序中满足某个条件时调用事件就是引发了事件,为什么可以调用事件呢?因为事件就是一个委托,而委托具有C++中函数指针的作用,调用委托就是调用委托中封装的事件处理函数

为什么事件可以被多个对象订阅?

知道了事件实际是一个多播委托后,这个问题不难理解,所谓被多个对象订阅,就是事件被触发后,可以导致多个对象做出反应,也就是多个对象的某个事件处理函数被调用。为什么会这样呢,因为事件是多播委托,即封装了多个函数的委托,调用这个委托实际上就是在调用被封装的这多个函数。

 

下面用一个最简单的控制台程序例子来说明事件的运作方式

using System;
namespace  最简单的自定义事件
{
  
///   <summary>
  
///  事件发送类,即调用事件的类
  
///   </summary>
   class  Class1
  {
    
public   delegate   void  mydelegate( object  sender,EventArgs e);  // 定义委托
     public   event  mydelegate myevent;  // 定义一个委托类型的事件,即定义一个委托对象

    
public   void  run()
    {
      
// 死循环,不停测试某个条件是否满足,即所谓的监听
       while ( true )
      {
        
if (Console.ReadLine() == " a " )
        {
          myevent(
this , new  EventArgs());  // 调用事件
        }
      }
    }
  }

  
///   <summary>
  
///  事件接收类,即事件处理的类
  
///   </summary>
   class  Class2
  {
    
static   void  Main( string [] args)
    {
      Class1 c1 
=   new  Class1();
      c1.myevent
+=   new  Class1.mydelegate(c1_myevent);  // 将委托对象指向具体的事件处理函数,即所谓的订阅事件
      c1.run(); // 运行这个事件,因为此时委托已经指向了具体方法,可以运行了
    }

    
private   static   void  c1_myevent( object  sender, EventArgs e)
    {
// 事件处理方法
      Console.WriteLine( " 你触发了事件! " );
    }
  }
}

 对于这个例子,为了加深理解事件的本质,我们可以把事件订阅c1.myevent+= new Class1.mydelegate(c1_myevent); 这句注释掉,即事件没有指向任何具体的方法,编译,不会报错,运行它,如果我们不输入字符串“a”,程序仍然运行正常,因为这时没有满足事件触发条件,一旦输入a回车,就会报如下错误:

未处理的异常:System.NullReferenceException未将对象引用设置到对象实例。

很显然,就是相当于我们调用了一个没有指向任何函数的函数指针。

这是我学习事件的一点心得,特别欢迎大家批评指正,因为知道错了不可怕,可怕的是不知道自己错了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值