关于委托,事件和类的设计准则
我们必须保持类型设计满足“高内聚,低耦合”,如此才能做到更好的代码重用。将应用拆解成组件类型可以实现高可维护性,并利于编码调试。
按钮点击、鼠标移动、键盘按键通常都是观察者模式的典型应用。封闭的类型对外发布事件,外部用用订阅类型的事件并编码实现在事件触发通知到系统后的操作,想想按钮Button类型来源于Windows.Forms名称空间,通过Dll提供,你没有Button类型的源代码不是一样注册Button的Click事件,从而让Button唤起你的Method,这个Dll就是一个有效封装。
委托必须实例化,或者指向一个实例化的方法才能被调用,我们无法直接用委托的名字加委托的参数定义一次函数Invoke,而事件充当此角色。事件的作用就是像委托的“样式”那样Invoke满足“样式”的方法,将任意多个满足“样式”的方法注册到事件上。“样式”本质是类型约束,所以委托就是C++的指针,但比C++多了类型检查,所以可以人为是强类型的指针,是在C++指针基础上的扩展类!
热水器带加热组件用于加热Heat(),热水器含有温度报警器设置预加热温度和报警温度,好的温度传感器甚至能够输出控制热水器的加热组件从而保持恒温。以热水器WaterHeater和温度传感报警器TemperatureAlarmer为例,下面展示其应用。可以直接拷贝代码到Vistual Studio,逐行调试并观察“即时窗口”的变量的变化情况,以深入理解。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Demo { /// <summary> /// 报警处理器:用于热水器及其关联的报警器 /// </summary> /// <param name="heater">热水器</param> /// <param name="alarmer">报警器</param> public delegate void AlarmHandler(WaterHeater heater, TemperatureAlarmer alarmer); /// <summary> /// 温度报警器:设定当前温度和报警温度; /// 在超过报警温度后会触发事件 TemperatureOverflow,交给挂接报警器的设备处理 /// </summary> public class TemperatureAlarmer { private float temperature_current, temperature_alarm; /// <summary> /// 在超过报警温度后会触发事件 TemperatureOverflow,交给挂接报警器的设备处理,如热水器 /// </summary> public event Action TemperatureOverflow; //温度检查:超过报警温度后启动报警引擎 private void TemperatureCheck() { if (temperature_current > temperature_alarm) TemperatureOverflow(); } public TemperatureAlarmer() { temperature_current = 0f; temperature_alarm = 95f; } /// <summary> /// 属性:加热温度,范围35~100 /// </summary> public float TemperaturCurrent { get { return temperature_current; } set { temperature_current = value > 100 ? 100 : value; temperature_current = value < 35 ? 35 : value; TemperatureCheck(); } } /// <summary> /// 属性:报警温度,范围35~100 /// </summary> public float TemperaturAlarm { get { return temperature_alarm; } set { temperature_alarm = value > 100 ? 100 : value; temperature_alarm = value < 35 ? 35 : value; TemperatureCheck(); } } } /// <summary> /// 热水器,属于加热器,需要温度报警器 /// </summary> public class WaterHeater { public string TradeMark { get; set; } public string Company { get; set; } public TemperatureAlarmer heaterAlarmer; /// 热水器报警事件,将处理权交给用户处理 public event AlarmHandler TemperatureOverflow; /// 可以绑定更换掉默认的温度报警器 public void BindAlarmer(TemperatureAlarmer alarmer) { this.heaterAlarmer = alarmer; } public WaterHeater() { heaterAlarmer = new TemperatureAlarmer(); heaterAlarmer.TemperatureOverflow += () => { TemperatureOverflow(this, heaterAlarmer); }; } /// 对加热器进行加热,最高到100度,将温度增加到指定值 public void Heat(float temperature) { heaterAlarmer.TemperaturCurrent = temperature; } public override string ToString() { return $"{this.Company},{this.TradeMark},{heaterAlarmer.TemperaturCurrent}/{heaterAlarmer.TemperaturAlarm}"; } } /* 海信,?新婚之夜,0/95 Overflow:海信,?新婚之夜,88/80 海信,?新婚之夜,88/80 海信,?新婚之夜,95/95 海信,?新婚之夜,95/95 海信,?新婚之夜,90/95 */ public class RunMyApp { static void Main(string[] args) { WaterHeater HisenseWarmer = new WaterHeater(); HisenseWarmer.Company = "海信"; HisenseWarmer.TradeMark = "©新婚之夜"; HisenseWarmer.TemperatureOverflow += (heater, e) => Console.WriteLine("Overflow:" + HisenseWarmer); Console.WriteLine(HisenseWarmer);// HisenseWarmer.Heat(88);// HisenseWarmer.heaterAlarmer.TemperaturAlarm = 80;// Console.WriteLine(HisenseWarmer); //更换第三方温度报警器,具有更高级的自我调节温度功能,也就是保证不超过报警温度 TemperatureAlarmer OmronAlarmer = new TemperatureAlarmer(); OmronAlarmer.TemperatureOverflow += () => { OmronAlarmer.TemperaturCurrent = OmronAlarmer.TemperaturAlarm; }; HisenseWarmer.BindAlarmer(OmronAlarmer); HisenseWarmer.Heat(96); Console.WriteLine(HisenseWarmer); OmronAlarmer.TemperaturCurrent = 100; Console.WriteLine(HisenseWarmer); HisenseWarmer.Heat(90); Console.WriteLine(HisenseWarmer); Console.ReadKey(); } } }