上海内部培训总结之 事件

事件

说到委托不得不说的另一个概念就是事件。

为什么会用到事件

为了能更好的理解为什么要使用事件,下面我们将换一个示例来说明(这个示例来源于网络),事件与委托的关系,以及为何要使用事件。

 

示例:现在,我们有一个热水器。这个热水器有热水的功能,同时,当温度高于96度后会发出警报,并显示当前水温。由OO的思想我们可以拆分为三个对象。

1、 热水器:具有烧水的功能,和水温属性

2、 报警器:是热水器的组件之一,具有发出警报的功能

3、 显示器:是热水器的组件之一,具有显示当前温度功能

 

下面我们用代码来实现一下三个类的基本功能

 

using System;

usingSystem.Collections.Generic;

using System.Text;

namespaceDelegate

{

    // 热水器

    public class Heater

    {

        privateint temperature;

      

        // 烧水

        public void BoilWater()

        {

        }

    }

    // 警报器

    public class Alarm

    {

        public void MakeAlert(intparam)

        {

            Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);

        }

    }

    // 显示器

    public class Display

    {

        public static void ShowMsg(int param)

        { //静态方法

            Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);

        }

    }

这个时候我们可以看到,显示器和警报器都提供了一个无返回值,参数个数为1且类型为int的变量。

 

按要求,这时我们可能会有这种实现,即在Heater的BoilWater方法中,当温度达到一定条件时,就调用其它两个类的方法,代码如下。

public void BoilWater()

        {

            for(int i = 0; i <= 100; i++)

            {

                temperature = i;

                if(temperature > 95)

                {

                    Alarmalm = new Alarm();

                    alm.MakeAlert(temperature);

                    Display.ShowMsg(temperature);

                }

            }

        }

但是前面我们才学过委托,也知道如果这样做不利于扩展对吧。所以我们可以像如下这样做修改。

// 热水器

    public class Heater

    {

        privateint temperature;

        public delegate void BoilHandler(intparam); //声明委托

        // 烧水

        public void BoilWater()

        {

            for(int i = 0; i <= 100; i++)

            {

                temperature = i;

                if(temperature > 95)

                {

                    if(BoilHandler!= null)

                    { //如果有对象注册

                        BoilHandler (temperature); //调用所有注册对象的方法

                    }

                }

            }

        }

    }

这时,我们会发现BoilHandler会报错。本来我们是希望在Heater被实例化的时候再决定有那些事件需要在温度高于95度的时候触发的。但委托确需要我们在使用的时候就要实例化,而不能等到调用的时候才做这样的事情。所以我们就必须用到事件这一概念了,下面先看下事件如何申明

public event BoilHandlerBoilEvent; //声明事件

可以看到事件申明只不过是在委托前加了一个event关键字并且取了一个别名而已。不过正是因为这个别名,才使得可以从类的外部为委托BoilHandler增加方法绑定,具体代码如下

public class Heater

    {

        privateint temperature;

        public delegate void BoilHandler(intparam); //声明委托

        public event BoilHandlerBoilEvent; //声明事件

        // 烧水

        public void BoilWater()

        {

            for(int i = 0; i <= 100; i++)

            {

                temperature = i;

                if(temperature > 95)

                {

                    if(BoilEvent != null)

                    { //如果有对象注册

                        BoilEvent(temperature);//调用所有注册对象的方法

                    }

                }

            }

        }

    }

所以,可以理解事件为委托的别名,然后公开到类的外部供其调用。有人会说,那委托BoilHandler是public的,也可以调用呀。其实不是的,如果你在外部实例后你会发现,调用不了这个委托,因为委托不管你是public还是什么,在编译的时候都会变成privite这是出于安全考虑,所以只有借助事件才能公开出来。下面我们看看在客户端如何使用。

class Program

    {

        static void Main(string[]args)

        {

            Heaterheater = new Heater();

            Alarmalarm = new Alarm();

            heater.BoilEvent +=alarm.MakeAlert; //注册方法

            heater.BoilEvent += (new Alarm()).MakeAlert;//给匿名对象注册方法

            heater.BoilEvent += Display.ShowMsg; //注册静态方法

            heater.BoilWater(); //烧水,会自动调用注册过对象的方法

 

            Console.ReadKey();

        }

}

输出为:

Alarm:嘀嘀嘀,水已经 96 度了:

Alarm:嘀嘀嘀,水已经 96 度了:

Display:水快烧开了,当前温度:96度。

 

这样即使以后有其它组件加入进来,也只需要在main方法中使用+=的方法把需要执行的新方法注册一下就可以了。

 

看到这里,大家可能会想,这与我们在.NetFramework中看到的事件不一样呢,那里面的事件一般都有两个参数一个是object类型,另一个是EventArgs或其子类型。那我们下面就来讲讲.NetFramework中的事件模型是怎么样的。

.Net Framework中的事件模型

在.NetFramework中有以下特点需要我们注意

1、委托类型的名称都应该以 EventHandler结束

2、委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object类型,一个EventArgs类型或其子类。

3、事件的命名为 委托名去掉EventHandler之后剩余的部分

4、继承于EventArgs的类型应该以EventArgs结尾

 

特别说明:Object一般是指触发事件的对象,而EventArgs则是触发事件后需要传递的参数。

 

为了让我们的代码和.NetFramework的事件模型一致,我们可以做如下修改。

1、  自定义参数类型

2、 按.NetFramework中的事件模型实现事件触发

 

using System;

usingSystem.Collections.Generic;

using System.Text;

namespaceDelegate

{

    // 热水器

    public class Heater

    {

        privateint temperature;

        public string type = "RealFire001"; // 添加型号作为演示

        public string area = "ChinaXian"; // 添加产地作为演示

        //声明委托

        public delegate void BoiledEventHandler(Objectsender, BoliedEventArgs e);

        public event BoiledEventHandlerBoiled; //声明事件

        // 定义BoliedEventArgs类,传递给Observer所感兴趣的信息

        public class BoliedEventArgs: EventArgs

        {

            publicreadonly inttemperature;

            publicBoliedEventArgs(int temperature)

            {

                this.temperature= temperature;

            }

        }

        // 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视

        protectedvirtual voidOnBolied(BoliedEventArgs e)

        {

            if(Boiled != null)

            { // 如果有对象注册

                Boiled(this,e); // 调用所有注册对象的方法

            }

        }

        // 烧水。

        public void BoilWater()

        {

            for(int i = 0; i <= 100; i++)

            {

                temperature = i;

                if(temperature > 95)

                {

                    //建立BoliedEventArgs 对象。

                    BoliedEventArgse = new BoliedEventArgs(temperature);

                    OnBolied(e); // 调用 OnBolied方法

                }

            }

        }

    }

    // 警报器

    public class Alarm

    {

        public void MakeAlert(Objectsender, Heater.BoliedEventArgse)

        {

            Heaterheater = (Heater)sender; //这里是不是很熟悉呢?

            //访问 sender 中的公共字段

            Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);

            Console.WriteLine("Alarm: 嘀嘀嘀,水已经 {0} 度了:", e.temperature);

            Console.WriteLine();

        }

    }

    // 显示器

    public class Display

    {

        public static void ShowMsg(Object sender, Heater.BoliedEventArgs e)

        { //静态方法

            Heaterheater = (Heater)sender;

            Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);

            Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", e.temperature);

            Console.WriteLine();

        }

    }

    class Program

    {

        static void Main()

        {

            Heaterheater = new Heater();

            Alarmalarm = new Alarm();

            heater.Boiled += alarm.MakeAlert; //注册方法

            heater.Boiled += (new Alarm()).MakeAlert;//给匿名对象注册方法

            heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert); //也可以这么注册

            heater.Boiled += Display.ShowMsg; //注册静态方法

            heater.BoilWater(); //烧水,会自动调用注册过对象的方法

        }

    }

}

输出为:

Alarm:China Xian - RealFire 001:

Alarm: 嘀嘀嘀,水已经 96 度了:

Alarm:China Xian - RealFire 001:

Alarm: 嘀嘀嘀,水已经 96 度了:

Alarm:China Xian - RealFire 001:

Alarm: 嘀嘀嘀,水已经 96 度了:

Display:China Xian - RealFire 001:

Display:水快烧开了,当前温度:96度。

// 省略 ...

 

在上面的例子中,大家会发现,除了可以使用参数 e来获取所需要参数外,还可以能过 sender来获取触发事件对象本身的一些信息。是不是很爽呀。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值