一个简单的概述
这里有一个小小的例子描述了一些单独使用委托链的局限。
知道了使事件的优势,用委托链的不足,也就更清楚了使用这样便于在设计时灵活的选择。
多播委托(委托链)
一个简单的实例:(cooler and heater)
程序中有两个工作实体, Cooler 和 Heater,它们会根据输入的温度改变自己的状态(On 或 Off) 有一个管理者Thermostat,Cooler 和 Heater会注册到它的委托链上。Main直接操纵Thermostat,把温度的输入给它,Thermostat会根据委托链上的委托进行操作。
这个小程序的主要目的是为了解释委托在使用时的不足之处。尤其通过时序图可以看的更加的清楚。
时序图:
最有趣的是红色方框部分,当改变温度时委托链中的委托会被顺序的访问。
注意红色方框的部分, 有下面几个问题是值得考虑的:
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();
}
}
}
下面将会使用事件方式来解决委托链中的问题。