C# 委托和事件 笔记

参考:C#事件解析 徐洪军

     C#委托,事件理解入门 finesite

    委托与事件代码详解 kein

上面的几位前辈写的文章都很好,这里只是总结一下。

 

一、什么是委托和事件

委托仅仅是函数指针,它能够引用函数,通过传递地址的机制完成,委托是一个类,对委托实例化时,要提供一个引用函
数,将它作为构造函数的参数。
每一个委托都有一个签名,被引用的函数必须和委托有相同的签名。
Delegate int SomeDelegate(string s,bool b);
private int SomeFunction(string str,bool bln){..}
SomeDelegate sd=new SomeDelegate(SomeFunction);
SomeFunction已经被SomeDelegate注册,如果调用sd,那么SomeFunction也会被调用。
总之,委托可以理解为函数指针,和函数指针不同的是委托是面向对象的。

事件简单分为两个部分:事件发生的类和事件接收的类。发生的类就是在这个类里面触发事件,但是它不知道究竟是哪个
对象或方法会处理这个事件。接收的类有一个处理事件的方法。

 

二、使用事件的步骤

  1. 创建一个委托
  2. 将委托与特定的事件关联
  3. 编写事件处理程序
  4. 利用事件处理程序生成一个委托实例
  5. 把委托实例添加到产生事件对象的事件列表中

 

 

A产生事件,事件为a,B接受事件,b处理;当a事件产生后,A通过事件列表中的委托对象将事件通知给B,B接收到一个事
件通知并调用b来处理。

public class A
{
   public delegate void EventHandler(object sender);
   public event EventHandler a;
   public void run()
   {
     a(this);
   }
}

class B
{
   private void b(object sender)
   {
     ...
   } 
    public B(A a)
   {
      a.a+=new A.EventHandler(this.b);
   }
}

三、事件和委托的关系

事件是对委托的封装,事件底层靠委托实现。

事件是委托类型的一个变量,所以必须声明在类的内部,因为事件本来就是一个委托,所以可以将赋给委托的方法赋给事件。
不管将委托声明为public还是protected,它总是在编译的时候被声明为private。所以,只能在声明事件的类的内部对事件进行"="赋值操作。
委托封装了两个操作"+="和"-=",这两个操作专门用来在类的客户端注册方法。
委托在编译的时候会编译成类。

有了委托为什么还要有事件?
事件是一种委托链,只能够用+=或者-=,防止之前添加的委托被覆盖。
如果不想让事件被人在类外面随便操作,必须使用事件来定义。

 

四、使用事件和委托的典型例子

例子一:按键事件

首先,有几点要说明一下,1.EventArgs是包含事件数据的类的基类,如果事件处理程序需要状态信息,则应用程序必须从此类派生一个类来保存数据。2.object sender的sender 是事件源,表示触发此事件的对象;EventArgs e的e是事件参数,包含该事件相关的信息,比如参数,它用来辅助你处理事件,还可以传递引用,在方法中直接访问类的成员等。

步骤就是:创建一个事件发生的类,定义委托和事件,里面的某一个动作会触发事件;创建一个接收事件的类,生成一个委托实例,并添加到事件列表中。

internal class KeyEventArgs:EventArgs
{
  private char keychar;
  public KeyEventArgs(char keyChar):base()
  {
    this.keyChar=keyChar;
  }
  public char KeyChar
  {
    get
    {
      return keyChar;
    }
  }
}

internal class KeyInputMointor
{
public delegate void KeyDownEventHandler(object sender,KeyEventArgs e);
public event KeyDownHandler KeyDown;

public void run()
{
 bool finished=false;
 while(!finished)
 {
  Console.WriteLine("Input a char...");
  string response=Console.ReadLine();
  char responseChar=(response=="")?'':char.ToUpper(response[0]);
  if(responseChar=='X') finished=true;
  else{
   //包含事件的数据
   KeyEventArgs keyEventArgs=new KeyEventArgs(responseChar);
   //触发事件
   KeyDown(this,keyEventArgs);
   }
  }
}

internal class EventReceiver
{
  public EventReceiver(KeyInputMonitor monitor)
  {
    //产生一个委托实例,并添加到事件列表中
    monitor.KeyDown+=new KeyInputMonitor.KeyDownHandler(this.onKeyDown);
    private void OnKeyDown(object sender,KeyEventArgs e)
    {
     Console.WriteLine("Capture Key:{0}",e.KeyChar);
    }
}

public class MainEntryPoint
{
  public static void Start()
  {
   //事件发送器
   KeyInputMointor monitor=new KeyInputMonitor();
   //事件接收器
   EventReceiver eventReceiver=new EventReceiver(monitor);
   //运行
   monitor.run();
  }
}

例子二、热水器烧水事件

public class Heater
{
  private int temperature;
  //声明事件和委托
  public delegate void BoiledEventHandler(Object sender,BoiledEventArgs e);
  public event BoiledEventHandler Boiled;

  //传递温度数据
  public class BoiledEventArgs:EventArgs
  {
    public readonly int temperture;
    public BoidEventArgs(int temperature)
    {
       this.temperature=temperature;
    }
  }

  //可供子类重写
  protected virtual void OnBoiled(BoiledEventArgs e)
  {
    //如果有对象注册,就触发Boiled事件,即调用所有注册对象的方法
    if(Boiled!=null)
    {
      Boiled(this,e);
     }
   }

   public void BoilWater()
   {
     for(int i=0;i<100;i++)
     {
        temperature=i;
        if(temperature>95)
        {
          //new一个EventArgs对象并传递给OnBoiled函数
          BoiledEventArgs e=new BoiledEventArgs(temperature);
          OnBoiled(e);
        }
     }
   }
}

//警报器
public class Alarm
{
  public void MakeAlert(Object sender,Heater.BoiledEventArgs e)
  {
     Console.WriteLine("Alarm: the temperature now is {0} ",e.temperature);
  }
}

//显示器
public class Display
{
  public static void ShowMsg(Object sender,Heater.BoiledEventArgs e)
  {
    Console.WriteLine("Display:the current is {0} temperature",e.temperature);
  }
}

class Program
{
  static void Main()
  {
    Heater heater=new Heater();
    Alarm alarm=new Alarm();
   
    //注册方法有很多种
    heater.Boiled+=alarm.MakeAlert;
    heater.Boiled+=(new Alarm()).MakeAlert();//匿名对象
    heater.Boiled+=new Heater.BoiledEventHandler(alarm.MakeAlert);//和第一种一样
    heater.Boiled+=Display.ShowMsg;//静态的方法
    
    heater.BoiledWater();
    Console.ReadKey();
  }
}

五、观察者模式

观察者模式是为了定义对象间的一种一对多的依赖关系。它是一种松耦合的设计模式。
observer在subject里面注册,subject某事件发生时通知observer,observer采取相应的行动。


上一节里面热水器烧水的例子中,Heater就是subject,Alarm和Display就是observer。首先Alarm和Display给Heater进行注册,然后水温高于95度时会通知Alarm和Display,它们分别调用MakeAlarm和ShowMsg方法。

posted @ 2012-11-13 21:19  cubika  阅读(207)  评论(0编辑  收藏  举报