14 C# 第十三章 事件和委托(一) 多播委托的问题

一个简单的概述

这里有一个小小的例子描述了一些单独使用委托链的局限。

知道了使事件的优势,用委托链的不足,也就更清楚了使用这样便于在设计时灵活的选择。


多播委托(委托链)

一个简单的实例:(cooler and heater)

程序中有两个工作实体, Cooler 和 Heater,它们会根据输入的温度改变自己的状态(On 或 Off)  有一个管理者Thermostat,Cooler 和 Heater会注册到它的委托链上。Main直接操纵Thermostat,把温度的输入给它,Thermostat会根据委托链上的委托进行操作。

这个小程序的主要目的是为了解释委托在使用时的不足之处。尤其通过时序图可以看的更加的清楚。


时序图: 最有趣的是红色方框部分,当改变温度时委托链中的委托会被顺序的访问。

14 C# 第十三章 事件和委托(一) 多播委托的问题_第1张图片

注意红色方框的部分, 有下面几个问题是值得考虑的:


1)  委托链中的异常
这是能想到的最直接的问题。委托链中的委托是被顺序访问的,当其中一个出现了异常,就会打破这个链。例如上面的委托链中 heater 先被访问,然后是cooler。可能cooler是我们需要处理温度变化的对象,但在委托链在访问 heater时出了异常,cooler就无法再执行了,虽然cooler可能一点问题都没有,但它没办法执行。


2)  委托链中的返回值

假设程序需要扩展一下,在第11步和第13步需要返回值给控制端,当控制端收到第11步的返回值后,委托链并没有终止,而是继续了13步,这样最终的返回值将总是第13步的返回值。



3)  委托运算符,关于 “+=”  和  "-=" 操作
+=: 可以像委托队列中增加一个委托,它会取得委托链中的第一个委托,然后向后加入新的委托。在其内部使用的是System.Delegate.Combine()方法.

-+:  会从委托链中删除一个委托,但其实质并不是从委托链中删除一委托,使委托链比原来的少一个,而是建了一个新的委托链,这个新的委托链的基础是把原来的委托链减去目标的委托。其调用的是  System.Delegate.Remove() 方法。这里加了个MSDN的连接。



4)  委托空值的检查
无论是System.Delegate.Combine()还是System.Delegate.Remove()都是允许对空值操作的,在使用委托链时最好也判断一下。


实例程序:(对应上面的时序图)

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

namespace delegate_bad_point
{
    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 Thermostat
    {
        public delegate void TemperatureChangedHandler(float newTemperature);

        private TemperatureChangedHandler m_OnTemperatureChange;
        private float m_fCurrentTemperature;

        public TemperatureChangedHandler OnTemperatureChanged
        {
            get { return m_OnTemperatureChange; }
            set { m_OnTemperatureChange = value; }
        }

        public float CurrentTemperature
        {
            get { return m_fCurrentTemperature; }
            set 
            {
                if (m_fCurrentTemperature != value)
                {
                    m_fCurrentTemperature = value;

                    TemperatureChangedHandler localOnChange = m_OnTemperatureChange;
                    // check the null value, it's important
                    if (localOnChange != null)
                        localOnChange(value);
                }
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Cooler cool = new Cooler(80);
            Heater heat = new Heater(60);
            Thermostat thermostat = new Thermostat();

            string strTemp = "";

            thermostat.OnTemperatureChanged += heat.OnTemperatureChaged;
            thermostat.OnTemperatureChanged += cool.OnTemperatureChaged;

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

            Console.ReadKey();
        }
    }
}


下面将会使用事件方式来解决委托链中的问题。



你可能感兴趣的:(14 C# 第十三章 事件和委托(一) 多播委托的问题)