• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#委托及事件处理机制浅析

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

   事件可以理解为某个对象所发出的消息,以通知特定动作(行为)的发生或状态的改变。行为的发生可能是来自用户交互,如鼠标点击;也可能源自其它的程序逻辑。在这里,触发事件的对象被称为事件(消息)发出者(sender),捕获和响应事件的对象被称作事件接收者。

    在事件(消息)通讯中,负责事件发起的类对象并不知道哪个对象或方法会接收和处理(handle)这一事件。这就需要一个中介者(类似指针处理的方式),在事件发起者与接收者之间建立关联。在.NET Framework中,定义了一个特殊的类型(delegate),来提供类似C++的函数指针的功能。本文通过一个定义和使用简单自定义事件的例子,对.NET的事件处理机制加以分析,以加深对事件处理原理的理解。


    如图所示,使用自定义事件,需要完成以下步骤:
    1、声明(定义)一个委托类(型),或使用.NET程序集提供的委托类(型);
    2、在一个类(事件定义和触发类,即事件发起者sender)中声明(定义)一个事件绑定到该委托,并定义一个用于触发自定义事件的方法;
    3、在事件响应类(当然发起和响应者也可以是同一个类,不过一般不会这样处理)中定义与委托类型匹配的事件处理方法;
    4、在主程序中订阅事件(创建委托实例,在事件发起者与响应者之间建立关联)。
    5、在主程序中触发事件。

    如按钮点击事件,就是用户在程序界面点击按钮控件时由按钮对象发出的消息,我们可以在界面程序中定义按钮点击事件处理方法来响应这一消息。这里就使用了委托处理机制。

    一、委托的定义和使用

    委托(委派)的声明(定义)格式如下所示:
    public delegate void MyDelegateClass(string message);
    其中delegate为委托类型关键字,MyDelegateClass是我们所定义的委托类的名称。委托类型类似C++的函数指针,而且是类型安全的函数指针,如同C++的回调函数(CALLBACK)。委托(委派)类型有一个签名(或称识别标志,signature),只有与签名特征匹配的方法才可以通过委托类型进行委派。

    从上面的定义中,可以看出我们定义的MyDelegateClass类的签名特征,即只要是输入参数为string,返回类型为void的方法都可以通过MyDelegateClass类进行指派。有了这一行定义语句,不需要我们再干什么,.NET编译环境就会自动为我们生成委托类MyDelegateClass,并允许我们通过类似MyDelegateClass delegateObj = new MyDelegateClass(对象名.方法名)的方式创建委托实例,添加与该实例关联的方法引用。.NET是如何做的呢?
    实际上,.NET在编译时,是根据我们的委托声明语句,为我们创建继承自System.MulticastDelegate(抽象类,其根类为System.Delegate)的委托类。Delegate类具有Target和Method两个类似指针的(引用)属性,分别指向所引用的对象及其方法的地址,这样,我们在使用委托类实例时实际上就是在调用对应的对象方法。而且,Delegate类可以引用多个对象方法,利用其“+=”操作符,通过类似delegateObj += new MyDelegateClass(对象名.方法名)的语句,可以为委托类对象实例delegateObj添加多个方法引用,这些方法引用被保存在委托类的委托列表中,在使用委托类实例时,这些方法都会被调用。



    如果需要,我们可以通过Delegate类的GetInvocationList()取出这些委托,并查看其Target和Method属性,获取所引用的方法名等信息。

    下面以一个简单例子来演示一下委托类型的定义和使用。

    1、创建一个目标类极其方法,提供给委托类型使用。

    //TargetClass.cs

    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace Delegatete_EventTest
    {
        //也可以创建单独的类文件
        public class TargetClass
        {
            public static void Method1(string message1)
            {
                Console.WriteLine("调用了目标方法1,参数:" + message1);
            }
            public void Method2(string message2)
            {
                Console.WriteLine("调用了目标方法2,参数:" + message2);
            }
        }
    }

    2、在主程序中定义并使用委托类型。如图所示为程序中定义的委托类(包括其基类)的类视图:


    //DeleGateExample.cs

    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace Delegatete_EventTest
    {
        class DeleGateExample
        {
            //定义委托类型

            public delegate void MyDelegateClass(string message);

            //主程序方法

            static void Main(string[] args)
            {
                //Test1();
                //Test2();
                Test3();
             }
             //测试1(仅为委托实例指派了一个目标方法)

             static void Test1()
             {
                 //定义委托实例,并指派(关联)目标方法(注意是目标类的静态方法)

                 MyDelegateClass delegateObj = new MyDelegateClass(TargetClass.Method1);

                 //运行委托实例(调用目标方法)

                 delegateObj("just a test");

                 //显示委托实例所关联的目标类极其方法

                 Console.WriteLine("目标对象及方法:" + delegateObj.Target + ","

                       + delegateObj.Method);

             }

             //测试2(如果不通过+=操作符而指派第二个目标方法,会覆盖掉第一个目标方法关联)
             static void Test2()
             {

                 //定义委托类对象实例,并指派第一个目标方法(目标类静态方法)
                 MyDelegateClass delegateObj = new MyDelegateClass(TargetClass.Method1);

                 //为委托实例指派第二个目标方法(目标类对象方法)

                 TargetClass targetobj = new TargetClass();
                 delegateObj = new MyDelegateClass(targetobj.Method2);

                 //运行委托实例(调用目标方法)

                 delegateObj("just a test");

                 //显示委托列表包含的目标方法个数

                 Console.WriteLine("该委托实例的目标方法个数:"

                         + delegateObj.GetInvocationList().Length);

                 //显示委托实例的目标类极其方法名称

                 Console.WriteLine("目标对象及方法:" + delegateObj.Target + ","

                         + delegateObj.Method);

             }

             //测试3(委托调用及委托列表显示)
             static void Test3()
             {

                 //定义委托对象实例,并关联第一个目标方法(目标类的静态方法)
                 MyDelegateClass delegateObj = new MyDelegateClass(TargetClass.Method1);

                 //使用+=操作符为委托实例添加第二个目标方法(目标类对象方法)
                 TargetClass targetobj = new TargetClass();
                 delegateObj += new MyDelegateClass(targetobj.Method2);
                 //运行委托实例(调用目标方法)

                 //delegateObj.Invoke("just a tets");
                 delegateObj("just a tets");

                 //调用委托列表显示方法

                 DisplayDeObjList(delegateObj);
             }

             //委托列表的显示方法(逐一显示委托列表所包含的目标类极其方法名称)

              static void DisplayDeObjList(MyDelegateClass delegateObj)
              {
                  //显示委托列表包含的目标方法个数

                  Console.WriteLine("该委托实例的目标方法列表中存在" +

                         delegateObj.GetInvocationList().Length+"个目标方法,分别是:");

                  //逐一显示委托列表中所指派的目标类极其方法名称

                  for (int i = 0; i < delegateObj.GetInvocationList().Length; i++)
                  {
                      MyDelegateClass deObj = (MyDelegateClass)delegateObj.GetInvocationList()[i];
                      Console.WriteLine("目标对象及方法:" + deObj.Target + "," + deObj.Method);
                  }
              }                                
          }   
    }
    二、自定义事件的定义与处理

    1、在事件发起者类中定义事件:

    //EventSenderClass.cs

    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace Self_DefinedEvent
    {
        //声明一个委托类(定义为公共类型,以便外部代码使用)
        public delegate void MyEventDelegate(string aMessage);//参数为提示信息

        class EventSenderClass
        {
            //定义一个事件属性
            public event MyEventDelegate selfEvent;
            //定义一个激发自定义事件的方法
            public void RaiseSelfDefinedEvent()
            {
                //事件是否被订阅(被实例化),如果未订阅,MessageArrived就是null,不会引发事件
                if (selfEvent != null)
                    selfEvent("Self-Defined event is raised.");
            }
        }
    }

    2、在事件接收与处理类中定义事件处理方法

    //EventHandlerClass.cs

    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace Self_DefinedEvent
    {
        public class EventHandlerClass
        {
            //定义接收消息的公共属性
            public string receivedMessage;
            //自定义事件的处理方法

            public void ReceiveAndDisplayMessage(string message)
            {
                receivedMessage = "自定义事件被响应,事件消息为:" + message;
            }
        }
    }

    3、本例基于窗口应用,把窗口(Form)类作为自定义事件处理的主程序。在初始化窗口对象时执行自定义事件的订阅,即为自定义事件添加负责事件接收和处理的对象方法(语法与前面例子中添加委托实例的目标方法相同);在窗口类中添加了一个按钮和一个标签控件,并把自定义事件的触发放在了按钮点击处理方法中。点击按钮,自定义事件被触发,并使用标签控件输出事件响应信息。

    //Form1.cs

    using System;
    ......

    using System.Windows.Forms;

    namespace Self_DefinedEvent
    {
        public partial class Form1 : Form
        {
            EventSenderClass myEventSender;
            EventHandlerClass myEventHandler;
       
            public Form1()
            {
                InitializeComponent();
                myEventSender = new EventSenderClass();
                myEventHandler = new EventHandlerClass();
                //订阅(实例化)自定义事件           
                myEventSender.selfEvent +=

                               new MyEventDelegate(myEventHandler.ReceiveAndDisplayMessage);
            }

            //按钮点击处理方法

            private void button1_Click(object sender, EventArgs e)
            {
                //触发自定义事件
                myEventSender.RaiseSelfDefinedEvent();
                label1.Text = label1.Text + myEventHandler.receivedMessage;
            }
        }
    }
    4、以按钮为例,理解.NET的事件处理方式

    实际上,.NET的控件事件处理方式正是采用了前面所讲的自定义事件的处理机制。以上例中的按钮事件处理为例,打开Form1.Designer.cs,可以找到按钮事件的订阅语句:

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

    解析一下这个语句,“Click”是System.Windows.Forms.Button按钮类的事件属性,button1_Click是处理按钮事件的目标方法名,System.EventHandler则是.NET已定义好的用于事件处理的委托类型。这是.NET事件订阅的典型语法。

    5、动态控件的定义和使用

    在实际项目中有时事先并不知道程序界面中需要哪些控件,需要几个,这时就需要根据不同的条件动态生成不同的控件并使用。这里我们仅以一个简单例子加以说明。

    在上面的Windows界面应用程序中添加一个界面类Form2.cs:

    ......

    namespace Self_DefinedEvent
    {
        public partial class Form2 : Form
        {
            public Form2()
            {
                InitializeComponent();
                Button but1 = new Button();
                but1.Text = "动态按钮";
                but1.Click += new EventHandler(this.but1_Click);
                this.Controls.Add(but1);
            }
            //动态按钮处理方法
            private void but1_Click(object sender, EventArgs e)
            {
                Label lb = new Label();
                //设置标签位置,实际应用中要涉及到界面布局,如利用动态表格设置控件位置等。
                lb.Location = new System.Drawing.Point(0, 30);
                lb.Size = new System.Drawing.Size(200,10);
                this.Controls.Add(lb);
                lb.Text = "Button is clicked.";
  


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
经过实际验证的C#调用Haskell的方法发布时间:2022-07-14
下一篇:
改善C#程序的建议9:使用Task代替ThreadPool和Thread发布时间:2022-07-14
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap