深入理解 C# 中的事件机制

前言:
对于搞.net的朋友来说,经常会遇到关于事件和委托的问题:事件与委托有什么关系?事件的本质是什么?委托的本质又是什么?由于.net 做了大量的封装,对于初学者,这两个概念确实不怎么好理解。事件是用户与应用程序交互的基础,它是回调机制的一种应用。举个例子,当用户点击按钮时,我们希望弹出一句“您好”;这里的【点击】就是一个事件;那么回调就是我们注册一个方法,当用户点击时,程序自动执行这个方法去响应这个操作,而不是我们时刻去监听用户有没有点击。

c# 中间的事件和委托是紧密联系在一起使用的。网上的很多博客都是用的窗体来介绍事件。但是窗体中事件基本都是微软提前构建好的,很多同学不能理解事件里面的参数也不太明白怎么去自定义时间。所以针对这些不足和自己学习的知识对事件的理解整理成一片文章,希望能够帮助到正在学习的同学。

一;事件的组成结构
事件:能够发生的什么,是一种类型的成员(属性,方法),它的功能就是通知
使用:用于对象或类间的动作协调与信息传递(消息推送)
原理:事件模型 发生----->响应的5个动作,-----------(1)我有一个事件—>(2)一个人或者一群人关心我这个事件 —>(3)我的这个事件发生了
—>(4)关心这个事件的人会被依次通知到—>(5)被通知到的人根据拿到的事件信息(又称“事件数据”,“事件参数”,“通知”)对事件进行响应(又称为"处理事件")

事件模型的五个组成部分:
1.事件的拥有着(event source,对象或类)
2.事件成员(event,成员),事件自己是不会主动的去通知别的对象,一定是被拥有着的某些内部逻辑触发以后他才会去通知别的对象。
3.事件的响应者(event subscriber,对象),订阅了事件的对象或者类,通俗的讲就是接收事件的类(这个类它需要接受到事件拥有着发出的事件中的参数来工作)
4.事件处理程序(eventHandler,成员) – 本质上是一个回调方法,他是属于事件的响应者的内部方法
5.事件订阅 — 把事件处理器(一个函数)与事件关联在一起,本质上是一种以委托类型为基础的约定(所以又称为事件是基于委托的)对象.事件 += new 委托(事件处理器)

以我们开篇的点击事件为例,对上面的五种组成部分做一下详细介绍
事件拥有着:按钮控件
事件:点击(点击这个动作就是一个事件,它不会自己触发,必须是我们用鼠标点击)
事件的响应者:拥有注册这个方法的类
事件处理程序:完成注册的程序(当事件发出后,被事件响应者所捕捉,捕捉到以后调用事件处理器)
订阅: 对象.事件 += new 委托(事件处理器)
按钮.clicked += new 委托(事件处理函数)

案例一:使用简单的定时器控件触发事件

using System;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Timers;

namespace ConsoleApp210_22
{
    // 定义委托 函数的类型 它的主要作用是 指定了事件处理方法必须拥有的返回类型和参数
    public delegate void MessageHandler(string messageText,int num);

    public class Connection
    {
        // 定义事件 指定要使用的委托类型。
        // 这种方式声明了事件后,就可以引发它(使用和委托触发的方法)。理解:他是一个其返回类型和参数是由委托指定的方法一样。通过方法的调用来引用事件
        public event MessageHandler MessageArrived;
        private Timer pollTimer;

        public Connection()
        {
            pollTimer = new Timer(1000);
            pollTimer.Elapsed += new ElapsedEventHandler(checkForMessage);
        }

        public void Connect()
        {
            pollTimer.Start();
        }
        public void Disconnect()
        {
            pollTimer.Stop();
        
        }
        private static Random random = new Random();
        private void checkForMessage(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("Checking for new message");
            //  这个表达式也使用了委托语法,其含义是 “事件是否有订阅者”,如果没有订阅者,也就不会触发事件
            if ( (random.Next(9) == 0) && ( MessageArrived != null ))
            {
                // 这里的作用是触发事件,触发事件以后,他会将参数传递给他绑定的事件处理器
                MessageArrived("Hello", random.Next(9));
            }
        }
    }

    // 订阅事件的额类
    public class Dispaly
    {
        public void DisplayMessage(string message,int num)
        {
            Console.WriteLine("Message arrievd:{0}:{1}",message,num);
        
        }
    }
    class Program
    {
        // 定义委托规则
        static void Main(string[] args)
        {
            Connection myconnection = new Connection();
            Dispaly mydissplay = new Dispaly();
            // 事件订阅  当这个事件被触发以后,执行事件处理器(一段执行逻辑代码) 那他到底是怎么被处罚的呢?(见上面注释)
            myconnection.MessageArrived += new MessageHandler(mydissplay.DisplayMessage);
            myconnection.Connect();
            Console.ReadKey(); 
        }

    }
}

.订阅一个事件的含义:提供代码,并在事件发生时执行这些代码,这段代码称为事件处理程序。
2.会有三个对象参与整个活动:1.事件发生者 3.事件处理器 3.他们活动的活动平台
3.整个流程:事件发生者触发一个事件,但它并不知道哪个对象或者方法会接收并处理它触发的时间,
它对事件处理的唯一要求就是:通过委托指明处理事件的方法(即是订阅的事件处理器)必须有的返回类型和参数

EventArgs是包含事件数据的类的基类,此类不包含事件数据,在事件触发时不向事件处理程序传递状态信息的事件会使用此类。
如果事件处理程序需要状态信息,则必须从此类派生一个类来保存数据。这个类的实例(即对象) 会作为参数传递给事件

案例二:捕获键盘按键的程序

namespace ConsoleApp210_22
{
    class Program
    {
        static void Main(string[] args)
        {
            // 实例化一个事件发送器
            KeyInputMonitor monitor = new KeyInputMonitor();
            // 实例化一个事件接收器
            EventReceiver eventReceiver = new EventReceiver(monitor);
            // 运行
            monitor.Run();

        }

    }

    //EventArgs是包含事件数据的类的基类
    //  重写此类的作用是:它能够在事件引发时向事件处理程序传递状态信息,(事件处理程序需要状态信息)
    //  因为在我们键盘按键事件中要包含按键信息,所以要派生一个KeyEventArgs类,来保存按键信息,好让后面知道按了哪个键
    internal class KeyEventArgs : EventArgs
    {
        private char keyChar;
        public KeyEventArgs(char keyChar) : base()
        {
            this.keyChar = keyChar;
        }
        public char KeyChar
        {
            get
            {
                return keyChar;
            }
        }
    }

    //再创建一个事件发生的类KeyInputMonitor,这个类用于监控键盘按键的输入并触发一个事件:
    internal class KeyInputMonitor
    {
        // 创建一个委托,返回类型为void,两个参数
        public delegate void KeyDownHandler(object sender, KeyEventArgs e);

        // 将创建的委托和特定事件关联,在这里特定的事件为KeyDown
        public event KeyDownHandler KeyDown;

        public void Run()
        {
            bool finished = false;
            do
            {
                Console.WriteLine("Input a char");
                string response = Console.ReadLine();

                char responseChar = (response == "") ? ' ' : char.ToUpper(response[0]);
                switch (responseChar)
                {
                    case 'X':
                        finished = true;
                        break;
                    default:
                        // 得到按键信息的参数,实例化   
                        KeyEventArgs keyEventArgs = new KeyEventArgs(responseChar);
                        // 触发事件,传递的参数必须和代理的类型一致  将事件交由KeyDownHandler这个委托来处理,
                        //委托指定事件处理方法去处理事件,这就是事件接收方的类的事情了
                        //参数this是指触发事件的对象就是本身这个对象,keyEventArgs包含了按键信息。
                        KeyDown(this, keyEventArgs);
                        break;
                }
            } while (!finished);
        }
    }

    //最后创建一个事件接收方的类,
    //这个类先产生一个委托实例,再把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件。然后提供一个方法回显按键信息。
    internal class EventReceiver
    {
        // 析构函数,将上面的事件拥有的作为参数传递进来
        public EventReceiver(KeyInputMonitor monitor)
        {
            // 产生一个委托实例并添加到KeyInputMonitor产生的事件列表中(这一步就是订阅的过程, 对象.事件 += 委托(事件处理函数) )
            monitor.KeyDown += new KeyInputMonitor.KeyDownHandler(this.OnKeyDown);
        }

           // 事件处理函数
        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            // 真正的事件处理函数
            Console.WriteLine("Capture key: {0}", e.KeyChar);
        }
    }

}

总结:
C#中使用事件需要的步骤:
1.创建一个委托
2.将创建的委托与特定事件关联 public event 委托 事件
3.编写触发事件的程序(什么时候触发事件)
4.利用编写的事件处理程序生成一个委托实例。
5.把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件(对象.事件 += new 委托(事件处理器))

C#中事件产生和实现的流程:
1.定义A为产生事件的实例,a为A产生的一个事件
2.定义B为接收事件的实例,b为处理事件的方法
3.A由于用户(程序编写者或程序使用者)或者系统产生一个a事件(例如点击一个Button,产生一个Click事件)
4.A通过事件列表中的委托对象将这个事件通知给B
5.B接到一个事件通知(实际是B.b利用委托来实现事件的接收)
6.调用B.b方法完成事件处理
public class A
{
public delegate void EventHandler(object sender);
public event EventHandler a;
public void Run()
{
Console.WriteLine(“Trigger an event.”);
a(this);
}
}
class B
{
public B(A a)
{
a.a += new A.EventHandler(this.b);
}
private void b(object sender)
{
Console.WriteLine(“Received and handled an event.” );
Console.Read();
}
}


版权声明:本文为qq_39143594原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。