15 C# 第十三章 事件和委托(二) 使用事件

本文介绍了C#中事件的使用步骤,包括事件如何解决委托链问题、异常处理、返回值处理,以及触发方式。通过对比委托链,强调事件在封装和参数传递上的优势,并讨论了事件为空时的处理。此外,还提到时序图有助于理解事件的工作机制。
摘要由CSDN通过智能技术生成
delegate 和 event 他们的定义和使用有点类似,会有点让人迷惑。
多播委托有几个问题 (委托链中的异常,委托链中的返回值,委托空值的检查)

这里可以通过分析事件怎样解决他们来了解事件与委托的区别,使用当然看具体问题来定未必事件就是比委托好的解决方式。



事件的使用大致分为这样几步:



1)  定义一个委托类型


2)  定义一个事件类型(在委托类型前加上关键字 event)


3)  为委托链中添加处理函数


4)  在运行时触发事件


Sample Code:  这段代码和多播委托的问题中的代码是一致的,只不过把委托用事件的方式实现了。


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

namespace event_delegate
{
    class Cooler
    {
        private float m_fTemperature;
        public Cooler(float fTemperature)
        {
            m_fTemperature = fTemperature;
        }

        public float Temperature
        {
            get { return m_fTemperature; }
            set { m_fTemperature = value; }
        }

        public void OnTemperatureChaged(float fNewTemperature)
        {
            if (fNewTemperature > m_fTemperature)
            {
                Console.WriteLine("Cooler : ON");
            }
            else
            {
                Console.WriteLine("Cooler : OFF");
            }
        }
    }

    class Heater
    {
        private float m_fTemperature;
        public Heater(float fTemperature)
        {
            m_fTemperature = fTemperature;
        }

        public float Temperature
        {
            get { return m_fTemperature; }
            set { m_fTemperature = value; }
        }

        public void OnTemperatureChaged(float fNewTemperature)
        {
            if (fNewTemperature < m_fTemperature)
            {
                Console.WriteLine("Heater : ON");
            }
            else
            {
                Console.WriteLine("Heater : OFF");
            }
        }
    }

    public class TemperatureHandler
    {
        public TemperatureHandler(Thermostat temperaturestat)
        {
            temperaturestat.eventOnTemperatureChanged += new Thermostat.TemperatureChangedHandler(HandlerTemperature);
        }

        void HandlerTemperature(object sender, Thermostat.TemperatureArgs fe)
        {
            Cooler cooler = new Cooler(60);
            Heater heater = new Heater(60);

            Console.WriteLine("TemperatureHandler  new value = {0}", fe.NewTemperature);
            if (fe.NewTemperature > 60)
            {
                Console.WriteLine("Cooler Start");
                cooler.OnTemperatureChaged(fe.NewTemperature);
            }
            else
            {
                Console.WriteLine("Heater Start");
                heater.OnTemperatureChaged(fe.NewTemperature);
            }
            ((Thermostat)sender).eventOnTemperatureChanged -= new Thermostat.TemperatureChangedHandler(HandlerTemperature);
        }
    }


    public class Thermostat
    {
        public class TemperatureArgs : System.EventArgs
        {
            public TemperatureArgs(float newTemperature)
            {
                _newTemperature = newTemperature;
            }

            public float NewTemperature 
            {
                get { return _newTemperature;}
                set { _newTemperature = value; }
            }
            private float  _newTemperature;
        }


        public delegate void TemperatureChangedHandler(object sender, TemperatureArgs newTemperature);
        public event TemperatureChangedHandler eventOnTemperatureChanged = delegate { };

        private float m_fCurrentTemperature;

        public float CurrentTemperature
        {
            get { return m_fCurrentTemperature; }
            set
            {
                if (m_fCurrentTemperature != value)
                {
                    m_fCurrentTemperature = value;
                    if (eventOnTemperatureChanged != null)
                    {
                        eventOnTemperatureChanged(this, new TemperatureArgs(value));
                    }
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Thermostat thermostat = new Thermostat();

            TemperatureHandler handler = new TemperatureHandler(thermostat);

            string strTemp = "";

            Console.WriteLine("Enter temperature: ");
            strTemp = Console.ReadLine();
            thermostat.CurrentTemperature = float.Parse(strTemp);

            // 在委托处理函数执行完成后调用 "-="后委托链为空了,这时在触发一下event来测试委托链为NULL的情况
            thermostat.CurrentTemperature = 10;

            Console.ReadKey();
        }
    }
}

代码中解决委托链问题的分析:

1)  委托链中的异常,委托链中的返回值

程序中还是存在着委托链,但这时的委托链中只有一个处理函数 += new Thermostat.TemperatureChangedHandler(HandlerTemperature)。

与原来在委托链中添加两个函数  (+= Heater.OnTemperatureChange()  和  += Cooler.OnTemperatureChange())  相比可以看作是一种封装。

这样在封装中可以任意调整实际的工作类(这里指的是Heater 和 Cooler) 可以是顺序的链,也可以根据条件执行。

处理异常和返回值时与单纯的委托链相比空间更大了。


2)  触发方式

在委托链方式中,触发方式就是通过调用委托来执行,在事件方式中变成了调用事件进行触发。这里最大的区别是它多了两个参数 SenderArguments

eventOnTemperatureChanged(this, new TemperatureArgs(value));

Sender 表示是那个对象触发了事件。

Arguments 表示执行委托需要的参数。

这个是委托链做不到的,事件又为我们解决问题增加了一条途径。



3)  委托空值的检查

在定义事件的代码中,同时还有一个赋值的操作。

public event TemperatureChangedHandler eventOnTemperatureChanged = delegate { };

它与NULL是不同的,它表示有委托但没有执行的实体。这也就解决了空引用的问题。


测试也很简单

在执行了事件处理函数后直接调用  “-=”,这样委托链就被清空了。

-= new Thermostat.TemperatureChangedHandler(HandlerTemperature);

然后在程序结束前再次触发事件,单步执行的话程序会运行到delegate { }中,但没有异常。

这可以看作是事件为委托加了一层保护吧。



如果代码不够直观,时序图也许能清楚点

这里红框部分更像是把委托链进行了封装,暴露在外的是event,大家可通过event来触发。

但触发时必须告诉 event 是谁触发的,参数是什么。


sequence_diam




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值