技术文章 > 软件工程 > C#编程利器之四:委托与事件(Delegate and event)
  本文试图在.net Framework环境下,使用C#语言来描述委托、事件的概貌。希望本文能有助于大家理解委托、事件的概念,理解委托、事件的用途,理解它的C#实现方法,理解委托与事件为我们带来的好处。
     C#是一种新的语言,希望大家能通过本文清楚地看到这些,从而可以对委托、事件等技术进行更深入的理解和探索。     关于委托与事件这两个知识点,一下很难以介绍清楚,所以我一分为二,分两篇文章来介绍,详细请可点击下面连接进行查看文章:
    本文试图在.net Framework环境下,使用C#语言来描述委托、事件的概貌。希望本文能有助于大家理解委托、事件的概念,理解委托、事件的用途,理解它的C#实现方法,理解委托与事件为我们带来的好处。C#是一种新的语言,希望大家能通过本文清楚地看到这些,从而可以对委托、事件等技术进行更深入的理解和探索。

一. 委托


委托的本质
  --在C#中,委托是一个特殊的类;
  --在某种程度上,相当于C++的函数指针;
  --在某种程度上,相当于接口(Interface);

委托的定义
  --关键字:delegate
  --public delegate void MyDelegate(string message);
  注:在这里我们先了解一个概念,什么是函数签名?(在这里我不做过多解释,大家知道这个概念就行)。

使用委托
  我们先来看看一个小的委托示例:
  平时,如果说我们要设计一个做简单加减运算的方法,通常是怎么做的呢?看看下面代码:
 1class Program
 2    {
 3        /// <summary>
 4        /// 加法运算
 5        /// </summary>
 6        /// <param name="x">x</param>
 7        /// <param name="y">y</param>
 8        /// <returns></returns>

 9        private static int Add(int x, int y)
10        {
11            int result = x + y;
12            Console.WriteLine("x + y = {0}",result);
13            return result;
14        }

15
16        /// <summary>
17        /// 减法运算
18        /// </summary>
19        /// <param name="x">x</param>
20        /// <param name="y">y</param>
21        /// <returns></returns>

22        private static int Sub(int x, int y)
23        {
24            int result = x - y;
25            Console.WriteLine("x - y = {0}", result);
26            return result;
27        }

28
29        static void Main(string[] args)
30        {
31            Add(88);
32            Sub(81);
33            Console.Read();
34        }

35    }
   
   上面的代码只要是学过程序的人都能看懂,也写得出,不过我们怎么通过委托来处理+,-运算呢?请看下面定义:
 1namespace DelegateSample1
 2{
 3    //定义一委托
 4    public delegate int OperationDelegate(int x,int y);
 5    public class Operator
 6    {
 7        private int _x, _y;
 8        public Operator(int x, int y)
 9        {
10            this._x = x;
11            this._y = y;
12        }

13
14        public void Operate(OperationDelegate del)
15        {
16            del(_x, _y);
17        }

18    }

19}

  上面定义一个返回int类型需要两个int参数的委托。Operator里提供了一个操作方法带有一个委托参数。那通过委托怎么来处理这个简单的运算呢?好,现在我们来修改我们之前定义的主方法,如下:
 1namespace DelegateSample1
 2{
 3    class Program
 4    {
 5        /// <summary>
 6        /// 加法运算
 7        /// </summary>
 8        /// <param name="x">x</param>
 9        /// <param name="y">y</param>
10        /// <returns></returns>

11        private static int Add(int x, int y)
12        {
13            int result = x + y;
14            Console.WriteLine("x + y = {0}",result);
15            return result;
16        }

17
18        /// <summary>
19        /// 减法运算
20        /// </summary>
21        /// <param name="x">x</param>
22        /// <param name="y">y</param>
23        /// <returns></returns>

24        private static int Sub(int x, int y)
25        {
26            int result = x - y;
27            Console.WriteLine("x - y = {0}", result);
28            return result;
29        }

30
31        static void Main(string[] args)
32        {
33            //声明一个委托对象
34            OperationDelegate del = null;
35            del += new OperationDelegate(Add);
36            del += new OperationDelegate(Sub);
37
38            Operator op = new Operator(53);
39            op.Operate(del);
40            Console.ReadLine();
41        }

42    }

43}

44
   从上面的例子看,委托OperationDelegate代表了一组方法,他们的方法签名是:
   --返回值:int; 参数:int ,int ;
   只要符合该签名的方法,都可以赋给此委托:从上面不难看出,我要要创建一委托,则如下定义:
1OperationDelegate del += new OperationDelegate(方法名);
   从上面可以看到(+=)这个运算符,那是不是也有(-=)这个运算符呢?这就涉及到另外一个概念了--委托链。
   --委托链:实际上委托实例就是一个委托链,+=代表增加委托实例到委托链中,相反-=则代表去掉该委托实例。
1OperationDelegate del = null;
2del += new OperationDelegate(Add); //增加委托实例到委托链
3del -= new OperationDelegate(Add); //去掉委托实例到

委托的意义之一
  --委托可以使得程序的复用程度提高;
  --委托在一定程度上想当于接口;
  例如:前面例子中的方法Operate(),由于接受的是一个委托类型;那么,我们可以对委托类型赋予不同的方法,来改变Operate()的性质。

  我们在来看看另外一个示例:
  --我们想输出一串数字,从0-100;
  --对于输出的要求有三种;
   -1、输出到控制台
   -2、输出到窗体中的ListBox中;
   -3、输出到文本文件中;
  解决方案:
  --使用委托和接口, 代码如下:
 1namespace DelegateSample2
 2{
 3    //定义一委托
 4    public delegate void ShowNumberDel(object[] items);
 5    public class ProcessNumber
 6    {
 7        private object[] items;
 8        public ProcessNumber(int max)
 9        {
10            items = new object[max];
11            for (int i = 0; i < max; ++i)
12            {
13                items[i] = i;
14            }

15        }

16
17        public void ProcessItems(ShowNumberDel show)
18        {
19            show(items);
20        }

21    }

22}

23
在这里我们先把界面上的控件布局好并做好调用委托的准备工作,效果及代码如下:
                   
代码如下:
 1private ProcessNumber pn = null;
 2ShowNumberDel del = null;
 3
 4private void Form1_Load(object sender, EventArgs e)
 5{
 6     pn = new ProcessNumber(100);
 7}

 8
 9//到控制台
10private void ShowInConsole(object[] items)
11{
12    foreach (object item in items)
13    {
14        Console.WriteLine(item);
15    }

16}

17
18//到ListBox
19private void ShowInListBox(object[] items)
20{
21    listBox1.Items.Clear();
22    foreach (object item in items)
23    {
24        listBox1.Items.Add(item);
25    }

26}

27
28//到文本文件
29private void ShowInFile(object[] items)
30{
31   using (StreamWriter sw = new StreamWriter("Test.txt"true))
32   {
33       foreach (object item in items)
34       {
35           sw.WriteLine(item);
36       }

37   }

38}
使用委托:
 1private void button1_Click(object sender, EventArgs e)
 2{
 3    pn.ProcessItems(new ShowNumberDel(ShowInConsole));
 4}

 5
 6private void button2_Click(object sender, EventArgs e)
 7{
 8    pn.ProcessItems(new ShowNumberDel(ShowInListBox));
 9}

10
11private void button3_Click(object sender, EventArgs e)
12{
13    pn.ProcessItems(new ShowNumberDel(ShowInFile));
14}

15
16private void button4_Click(object sender, EventArgs e)
17{
18    del += new ShowNumberDel(this.ShowInListBox);
19    del += new ShowNumberDel(this.ShowInFile);
20
21    pn.ProcessItems(del);
22}
完整的测试代码如下:
使用委托的完整测试代码

委托的意义之二
   --在C#中使用线程需要用到委托
    - Thread thread = new Thread(new ThreadStart(target));
?     -这里的ThreadStart就是一个委托,他的定义是:
     -target既为符号ThreadStart委托的方法名;
  
   --函数回调
    -
 当我们定义了一个委托;
       public delegate void MyDelegate(int source);
    -对于异步调用来说,就有BeginInvoke()和EndInvoke()方法;  
    -del.BeginInvoke(source, new System.AsyncCallback(CallBack), "test");

    -private void CallBack(IAsyncResult asyncResult)
      {
            int result = del.EndInvoke(asyncResult);
            //......
      }
  这里需要理解的就是什么叫函数回调?这个话题留给大家讨论,在此不作详细解说。关于委托本文只是入门级的文章,要想更详细深入的学习委托请查看具体的书籍或资料,本文就简单介绍到这里。

文中示例代码下载:Delegate.Event.Thread(1).rar

      我在上一篇文章(C#编程之委托与事件(一) )中通过示例结合的方法介绍了委托,在本文中,我同样以代码示例的方式来介绍C#里的事件机制。

二、事件
  1.了解概念
     事件就是当对象或类状态发生改变时,对象或类发出的信息或通知。发出信息的对象或类称为"事件源",对事件进行处理的方法称为"接收者",通常事件源在发出状态改变信息时,它并不知道由哪个事件接收者来处理.这就需要一种管理机制来协调事件源和接收者,C++中通过函数指针来完成的.在C#中事件使用委托来为触发时将调用的方法提供类型安全的封装。

   在介绍事件之前我们先来了解几个事件的基本概念和几个重要素:
   --事件的本质
    -事件是特殊的委托实例
    -事件关键字:event

   --事件的四个要素:
    -定义事件
    -激发事件
    -监听事件
    -执行事件

  2.事件分析
    在.NET中,很多控件都有相关的事件,如Button的Click事件,它能响应鼠标的单击事件。
    --定义事件
public delegate void EventHandler(object sender,EventArgs e);
public event EventHandler Click;
  
   --激发事件:单击鼠标

   --监听事件
this.button1.Click+=new EventHandler(this.button1_Click);
   
  --执行事件
public void button1_Click(object sender,EventArgs e)

  
//实现略
}
    上面这个button的Click事件是我们最常见的,这里展示出了整个事件过程。接下来我们来看看一个简单的事件实例。

  3.简单实例--怎样定义一个完整的事件机制
    一.定义委托
//定义事件委托
public delegate void ChangedEventHandler(object sender, EventArgs e);
    二.定义事件
//定义一个委托类型事件
public event ChangedEventHandler Changed;
   三.触发事件
//用于触发Changed事件
protected virtual void OnChanged(EventArgs e)
{
    
if (this.Changed != null)
    
{
         
this.Changed(this, e);
    }

}
   四.侦听事件
MyText myText = new MyText();
myText.Changed 
+= new MyText.ChangedEventHandler(myText_Chenged);
   五.事件处理程序
//事件处理程序
private static void myText_Chenged(object sender, EventArgs e)
{
    Console.WriteLine(
"Text属性的值改变:{0}", ((MyText)sender).Text);
}
     这就完成了一个完整的事件机制,详细代码如下:
MyText
Program

  4.实例解说
     现在我们需要设计一个电子邮件程序,当收到电子邮件时,希望将该消息转发到传真机(Fax)和手机(CallPhone);
     一.我们需要传递消息则需要定义事件传递的消息类吧,定义如下:
 1namespace EventEmail
 2{
 3    //事件传递的消息定义
 4    public class MailMsgEventArgs:EventArgs
 5    {
 6        public readonly string from, to, subject, body;
 7
 8        public MailMsgEventArgs(string from, string to, string subject, string body)
 9        {
10            this.from = from;
11            this.to = to;
12            this.subject = subject;
13            this.body = body;
14        }

15    }

16}
  
    二.定义委托及事件
public delegate void MailMsgEventHandler(object sender,MailMsgEventArgs e);
public event MailMsgEventHandler MailMsg;
      完整代码定义如下:
 1namespace EventEmail
 2{
 3    //定义一委托
 4    public delegate void MailMsgEventHandler(object sender,MailMsgEventArgs e);
 5
 6    public class MailManager
 7    {
 8        public event MailMsgEventHandler MailMsg;  //委托类型的事件
 9
10        protected virtual void OnMailMsg(MailMsgEventArgs e)
11        {
12            if (this.MailMsg != null)
13            {
14                MailMsg(this, e);
15            }

16        }

17
18        //通过事件传递消息
19        public void SimulateArrivingMsg(string from, string to, string subject, string body)
20        {
21            MailMsgEventArgs e = new MailMsgEventArgs(from, to, subject, body);
22            OnMailMsg(e);
23        }

24    }

25}

      三 .传真和手机的定义:
 1namespace EventEmail
 2{
 3    /// <summary>
 4    /// 传真机
 5    /// </summary>

 6    public class Fax
 7    {
 8        private TextBox _tBox;
 9        public Fax(MailManager mm, TextBox tBox)
10        {
11            //监听事件 
12            //这里的FaxMsg,指的是符合MailMsgEventHandler委托的方法,也就是激发事件后所执行的方法
13            mm.MailMsg += new MailMsgEventHandler(FaxMsg);
14            _tBox = tBox;
15        }

16
17        private void FaxMsg(Object sender, MailMsgEventArgs e)
18        {
19            _tBox.Text += string.Format("消息到传真:{4}来自:{0}{4}发到:{1}{4}主题:{2}{4}内容:{3}{4}{4}", e.from, e.to, e.subject, e.body, Environment.NewLine);
20        }

21
22        public void Register(MailManager mm)
23        {
24            mm.MailMsg += new MailMsgEventHandler(FaxMsg);
25        }

26
27        public void UnRegister(MailManager mm)
28        {
29            //注销事件
30            mm.MailMsg -= new MailMsgEventHandler(FaxMsg);
31        }

32    }

33}
-----------------------------------------------------------------------------------------------------------
 1namespace EventEmail
 2{
 3    /// <summary>
 4    /// 手机
 5    /// </summary>

 6    public class CallPhone
 7    {
 8        private TextBox _tBox;
 9        public CallPhone(MailManager mm, TextBox tBox)
10        {
11            mm.MailMsg += new MailMsgEventHandler(CellPhoneMsg);
12            _tBox = tBox;
13        }

14
15        private void CellPhoneMsg(Object sender, MailMsgEventArgs e)
16        {
17            _tBox.Text += string.Format("消息到手机:{4}来自:{0}{4}发到:{1}{4}主题:{2}{4}内容:{3}{4}{4}", e.from, e.to, e.subject, e.body,Environment.NewLine);
18        }

19
20        public void Register(MailManager mm)
21        {
22            mm.MailMsg += new MailMsgEventHandler(CellPhoneMsg);
23        }

24        public void UnRegister(MailManager mm)
25        {
26            mm.MailMsg -= new MailMsgEventHandler(CellPhoneMsg);
27        }

28    }

29}
    四.客户端调用
        上面的逻辑处理完毕,下面来看看调用情况:
 1namespace EventEmail
 2{
 3    public partial class Form1 : Form
 4    {
 5        private Fax fax = null;
 6        private CallPhone cell = null;
 7        private MailManager mm = null;
 8        public Form1()
 9        {
10            InitializeComponent();
11            mm = new MailManager();
12            fax = new Fax(mm, txtReceiver);
13            cell = new CallPhone(mm, txtReceiver);
14        }

15
16        private void Form1_Load(object sender, EventArgs e)
17        {
18
19        }

20
21        private void btnSend_Click(object sender, EventArgs e)
22        {
23            mm.SimulateArrivingMsg(txtFrom.Text, txtTo.Text, txtSubject.Text, txtBody.Text);
24        }

25
26        private void btnClear_Click(object sender, EventArgs e)
27        {
28            this.txtReceiver.Text = "";
29        }

30    }

31}


5 .事件的意义
   --有利于消息的传播
   --有利于模块之间的松散耦合

   注:什么是松散耦合?
   就以上面电子邮件程序为例。
   --如果没有事件机制,在发送邮件时,就需要去调用Fax,CellPhone的相关方法;
   --采用事件机制,在发送邮件时,仅许激发邮件管理器的事件既可,与Fax和CellPhone无关;
 
   也就是说,邮件管理器和Fax、CellPhone之间的依赖关系被解除了。
 
   本文就简单的介绍于此,上面看不太明白的可下示例程序了解;

   示例程序在载:EventDemo.rar

发表于 2009-4-29 2:03:52 | By 阅读(538)

文章评论列表

#1楼 2009-11-6 12:02:57 | By wcgao1020
支持邓老师

发表评论

验证码(必填)  
姓名
内容  

Top