C#中事件详解

##1.委托与事件的关系
事件是一类特殊的委托,更像是委托的升级版,将委托中的方法封装起来,封装到触发事件的函数或类中,当调用某一函数时,函数触发该事件,执行事件中的委托方法。
##2.事件定义格式

修饰词 envent 委托名称 事件名称

例如:public event SaySomething come
事件传入参数类型与返回值类型跟绑定委托一摸一样,即事件只会跟绑定的委托打交道,当触发该事件时,事件会通知关注的委托。
##3.事件的使用实例
定义委托SaySomething和2个委托方法SayHello、SayNiceToMeetYou,定义事件OnCome,在方法test中,添加实例化委托方法sayhello、saynice到事件OnCome 中,再触发该事件OnCome,主函数Main中调用program.test();即触发事件

internal class Program
    {
        public delegate void SaySomething(string name);     //定义一个委托

        public void SayHello(string name)                   //委托实例方法
        {
            Console.WriteLine("Hello," + name + "!"); 
        }

        public void SayNiceToMeetYou(string name)           //委托实例方法
        { 
            Console.WriteLine("Nice to meet you," + name + "!"); 
        }

        public event SaySomething OnCome;   //定义事件OnCome,事件传入的方法与返回值类型同委托SaySomething一致

        public void test() 
        { 
            SaySomething sayhello = new SaySomething(SayHello);             //实例化委托并传入方法
            SaySomething saynice = new SaySomething(SayNiceToMeetYou);      //实例化委托并传入方法      
            OnCome += sayhello;             //将方法添加到事件OnCome中
            OnCome += saynice;              //将方法添加到事件OnCome中,多播委托
            OnCome("张三");                 //事件执行实例                            
        }

        static void Main(string[] args)
        {
            Program program = new Program(); 
            program.test();         //事件好处:如果代码升级,并不需要修改调用的地方,
                                    //即program.test();因为事件相当于已经被封装起来了。
                                    //只需要修改委托实例,C#语言代码核心:高内聚,低耦合
            Console.Read();
        }
 
    }

##3.事件的自定义模式
常见的窗体按钮单击事件、文本框聚焦事件、列表框选择事件等,此部分事件及背后的委托方法,已经被.net封装起来可直接使用,但我们可自定义事件的发生,事件例子代码如下:事件中的详细信息已经再代码中做了批注

public delegate void TimeEventHandler(object obj, TimeEventArgs args); //定义一个委托,
                                                                           //委托其实就是“方法模板”,就好像“类”是“对象”的模板一样。
                                                                           //如果某个类想在事件触发的时候收到通知,它必须有一个符合这种格式的方法,
                                                                           //在这个例子中,就是:返回类型为void,参数类型为object、TimeEventArgs
    //TimeEventArgs是我们自己定义的一个类,用于保存事件中的参数。这里我们分别保存时间的时分秒。
    public class TimeEventArgs : EventArgs      //继承至EventArgs类
    {
        private int hour;       //类中定义3个字段
        private int minute;
        private int second;
        public int Hour
        {
            get
            {
                return hour;
            }
        }       //字段属性
        public int Minute
        {
            get
            {
                return minute;
            }
        }
        public int Second

        {
            get
            {
                return second;
            }
        }

        public TimeEventArgs(int hour, int minute, int second)      //以类名定义的方法,构造函数,相当于重写或者重载形式
        {
            this.hour = hour;
            this.minute = minute;
            this.second = second;
        }     
    }

    //这是一个观察者类,它有一个符合我们上面定义的“委托”的方法,
    //也就是void ShowTime(object obj, TimeEventArgs args),
    //从这个方法的定义可以看到,我们只会关心返回类型和方法的参数,而方法名称则无所谓
    class MyTimeEventHandlerClass
    {
        public void ShowTime(object obj, TimeEventArgs args)  //TimeEventHandler委托的实例
        {
            Console.WriteLine("现在的时间是:" + args.Hour + ":" + args.Minute + ":" + args.Second);
        }
    }

    //时钟类
    class Clock
    {
        我们在这个类中定义了一个“TimeChanged”事件,
        注意其前面有两个关键字“event”和“TimeEventHandler”,
        其中event表示这是一个事件,而不是方法或属性;TimeEventHandler则指出,
        谁要监听TimeChanged事件,它就必须有一个符合TimeEventHandler(委托)的方法。  
        public event TimeEventHandler TimeChanged;
        public Clock()
        {
            TimeChanged = null; //注意,这里的null的含义是指TimeChanged事件当前还没有观察者关注它,
                                //如果某个观察者要关注TimeChanged事件,它必须要让这个事件知道,
                                //方法是使用操作符“+=”来借助委托将其加载到事件上。        
        }

        //时钟开始走动,我们的目标是每秒钟触发一次TimeChanged事件
        public void go()
        {
            DateTime initi = DateTime.Now;
            int h1 = initi.Hour;
            int m1 = initi.Minute;
            int s1 = initi.Second;
            while (true)
            {
                DateTime now = DateTime.Now;
                int h2 = now.Hour;
                int m2 = now.Minute;
                int s2 = now.Second;
                if (s2 != s1)
                {
                    h1 = h2;
                    m1 = m2;
                    s1 = s2;
                    TimeEventArgs args = new TimeEventArgs(h2, m2, s2);   //实例化类TimeEventArgs,并传入3个参数
                    //首先建立一个TimeEventArgs对象来保存相关参数,这里是时分秒。
                    TimeChanged(this, args);        //事件执行
                }
            }
        }
    }

    internal class Program
    {                  
        static void Main(string[] args)
        {
            Clock clock = new Clock(); //实例化一个时钟   
            MyTimeEventHandlerClass tehc = new MyTimeEventHandlerClass(); //实例化一个观察者类
            //委托事件TimeChanged与委托方法tehc.ShowTime绑定,即事件TimeChanged触发时,ShowTime方法响应
            clock.TimeChanged += new TimeEventHandler(tehc.ShowTime);   
            clock.go();     //clock.go方法中会触发TimeChanged事件
            Console.ReadLine();
        }
    }

##4事件的组成部分
综上所述一个完整的事件过程应该由以下部分组成:
在这里插入图片描述

##5.根据上面的例子总结事件的使用
事件使用:
delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它"只能持有与它的签名相匹配的方法的引用"。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数委托和事件在 .Net Framework中的应用非常广泛指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。

实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:
1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
2. 创建delegate对象,并"将你想要传递的函数作为参数传入"。
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。

C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。

就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义"事件处理方法,它应当与delegate对象具有相同的参数和返回值类型"。
4.用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值