从多播委托到事件

一、多播委托

前文提到的委托只是在一个委托类型中存储了一个方法(函数),实际上一个委托变量可以同时绑定多个方法,这些委托形成了一个委托链,每一个委托(实际上是方法)都顺序指向下一个委托,这个委托链就是多播委托。

每一个绑定的方法就像是订阅者一样,等着发布者的消息,而触发委托变量的那个就像是发布者,将出发的信号传给所有的订阅者。

1、订阅者

考虑一个温度控制器的例子,这个控制器拥有两个调温器,一个加热器,当温度低于指定的值时,启动,一个冷却器,当温度高于指定的温度时,启动。二者的类设计如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 //订阅者
 6 namespace DuoBoEvent
 7 {
 8     class Heater
 9     {
10         public Heater(float temperature)//设定启动加热器的临界
11         {
12             Temperature = temperature;
13         }
14         private float _Temperature;
15
16         public float Temperature
17         {
18             get { return _Temperature; }
19             set { _Temperature = value; }
20         }
21         public void OnTemperatureChanged(float newTemperature)
22         {
23             if (newTemperature < Temperature)
24             {
25                 Console.WriteLine("Heater start");
26             }
27             else
28             {
29                 Console.WriteLine("Heater stop");
30             }
31         }
32     }
33 }

Heater

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 //订阅者
 6 namespace DuoBoEvent
 7 {
 8     class Cooler
 9     {
10         private float _Temperature;//启动Cooler的临界温度
11
12         public float Temperature
13         {
14             get { return _Temperature; }
15             set { _Temperature = value; }
16         }
17         public Cooler(float temperature)
18         {
19             Temperature = temperature;
20         }
21
22         public void OnTemperatureChanged(float newTemperature)//传入的为当前温度
23         {
24             if (newTemperature > Temperature)
25             {
26                 Console.WriteLine("Cooler start");
27             }
28             else
29             {
30                 Console.WriteLine("Cooler stop");
31             }
32         }
33     }
34 }

Cooler

可以看出,这两个类除了温度比较外,几乎完全一直,温度比较的那个函数,形式也是一致的。OnTemperatureChanged都属于订阅者方法,他们的参数类型与发布者的委托类型一致,这样才可以绑定到发布者的委托变量上。

2、发布者

发布者代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5
 6 namespace DuoBoEvent
 7 {//发布者
 8     class Thermostat
 9     {
10         public delegate void TemperatureChangedHandler(float newTemperature);//定义一个委托,他与订阅者的函数签名一致
11
12         //定义一个委托类型的成员,用来发布“消息”
13         private TemperatureChangedHandler _OnTemperatureChange;
14
15         public TemperatureChangedHandler OnTemperatureChange
16         {
17             get { return _OnTemperatureChange; }
18             set { _OnTemperatureChange = value; }
19         }
20         //定义这个属性,在其访问器内出发委托,作为发布
21         private float _CurrentTemperature;
22
23         public float CurrentTemperature
24         {
25             get { return _CurrentTemperature; }
26             set
27             {
28                 if (value != CurrentTemperature)
29                 {
30                     //其实,_CurrentTemperature已经是输入新的温度后的前温度了,用来与新的温度Value比较,下一句就是更新前温度为现温度
31                     _CurrentTemperature = value;//用作一个参数,类似 public void OnTemperatureChanged(float newTemperature)里面的参数
32                     //调用委托
33                     OnTemperatureChange(value);//如果温度不一致再调用
34                 }
35
36             }
37         }
38
39     }
40 }

Thermostat

可以看出,在_CurrentTemperature的访问器内调用了这个委托变量,如果我在主函数中将两个调节器的OnTemperatureChanged与Thermostat的委托变量绑定后,当执行到_CurrentTemperature的访问器后,通过判定,会调用委托。

3、主函数:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5
 6 namespace DuoBoEvent
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             Heater heater = new Heater(60);
13             Cooler cooler = new Cooler(80);
14             Thermostat thermostat = new Thermostat();
15             string temperature;
16             //订阅者与发布者绑定
17             thermostat.OnTemperatureChange += heater.OnTemperatureChanged;
18             thermostat.OnTemperatureChange += cooler.OnTemperatureChanged;
19             //读入当前温度,60及其以下会出发Heater,80及其以上,会出发Cooler
20             Console.WriteLine("Enter the current temperature");
21             temperature = Console.ReadLine();
22             thermostat.CurrentTemperature = int.Parse(temperature);
23         }
24     }
25 }

main

在最后一句,在赋值的同时(实际上是晚于复制),调用了委托,便可以看到两个调节器的状态。

二、对发布者调用委托变量的考虑

如果我在主函数中没有给Thermostat的委托变量指定任何方法呢?会提示委托对象未实例化,所以在调用前必须要进行空值检查,代码如下:

 1   if (value != CurrentTemperature)
 2                 {
 3                     //其实,_CurrentTemperature已经是输入新的温度后的前温度了,用来与新的温度Value比较,下一句就是更新前温度为现温度
 4                     _CurrentTemperature = value;//用作一个参数,类似 public void OnTemperatureChanged(float newTemperature)里面的参数
 5                     //调用委托
 6                     TemperatureChangedHandler localOnChange = OnTemperatureChange;
 7                     if (localOnChange != null)
 8                     {
 9                         OnTemperatureChange(value);//如果温度不一致再调用
10                     }
11                 } 

Update

需要注意的是,并没有直接检查OnTemperatureChange,而是将其赋值给了localOnChange,因为可能在判断的时候,其他线程置空了OnTemperatureChange,造成错误。

既然委托是引用类型,那么为什么置空OnTemperatureChange不会影响到localOnChange呢?因为C#中,删除订阅者通过-=运算符,而调用-=来处理OnTemperatureChange-=<listener>,不会从OnTemperatureChange上删除,而是生成了一个全新的委托指向他,所以localOnChange是安全的。

时间: 2024-10-10 07:46:23

从多播委托到事件的相关文章

多播委托和事件

事件的创造规则是类型必须是一个委托的类型,也就是说是委托实例化了事件 事例代码如下: 这是两个以后要被委托的方法: public class EventManager { //第一个方法 public void Add(int i,int j) { int res = i + j; MessageBox.Show(res.ToString()); } //第二个方法 public void SayHello(int i,int j) { MessageBox.Show("hello word&q

委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托

在"委托.Lambda表达式.事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性"中,反编译委托,发现委托都是多播委托. 既然委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链, 它是怎样形成的?来看下面的例子: namespace ConsoleApplication3 { internal delegate void MySayDel(string msg); class Program { stati

C#学习总结 事件(一)深入多播委托

好久没写文章了,之前说要总结一下事件这个概念,今天趁着工作闲暇,谈谈我对C#事件事件的理解,如果有什么不对的地方,望各位大神指点. 真正的理解事件,还是要很好的理解委托,不然你永远不会明白很多教科书上的一句话:事件是一个特殊的委托,今天我们就来探寻一下:为什么说事件是一个特殊的委托,之前我谈论了委托的一些相关基础知识,其实还有一些关于委托更深层次的东西没有说,那么在今天谈事件这个知识之前还是继续从委托说起: 不知道有没有人了解C语言的函数指针这个概念,使用函数指针来创建回调函数,使用回调可以把函

C#高级知识点概要(1) - 委托和事件

作者:linybo 要成为大牛,必然要有扎实的基本功,不然时间再长项目再多也很难有大的提升.本系列讲的C# 高级知识点,是非常值得去撑握的,不仅可以让你写代码时游刃有余,而且去研究和学习一些开源项目时,也不会显得那么吃力了. 希望大家记住,这里讲的所有的知识点,不仅仅是了解了就可以了,还要会灵活用,一定要多思考,撑握其中的编程思想. 本文讲的是委托和事件,这两个词可能你早就耳熟能详,但你是否真正撑握了呢? 本系列讲的C#高级知识点都是要求开发时能达到可以徒手写出来的水平(不依赖搜索引擎.找笔记等

委托与事件

委托在底层就是一个函数的指针,委托是事件的基础. 你可以传递引用类型.值类型.但是你有没有需要传一个方法呢?传方法的过程就是委托. 消息类: public class Message { /// <summary> /// 传引用类型 /// </summary> /// <param name="msg"></param> public static void Send(string msg) { Console.WriteLine(&

委托和事件

一.委托:是一个能够引用方法的对象.创建委托时实际是创建一个方法引用的对象,创建的引用对象能够调用方法. 委托调用可以调用不同的方法,只需改变方法的引用即可.即委托调用方法是运行时确定,而非编译时确定. 就像声名一个object实例一样,声名的只是一个占位符,具体指向哪个对象在运行时可以动态指定.在委托中指定方法有限制:返回值,参数类型要相同. 委托声名:delegate ret-type delegatename(parameter-list) delegate 关键字声名委托 ret-typ

[转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)

原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委托是一个类型,是一个函数指针类型,这个类型将该委托的实例化对象所能指向的函数的细节封装起来了,即规定了所能指向的函数的签名,也就是限制了所能指向的函数的参数和返回值.当实例化委托的时候,委托对象会指向某一个匹配的函数,实质就是将函数的地址赋值给了该委托的对象,然后就可以通过该委托对象来调用所指向的函

C#语言-05.委托和事件

a. 委托:是一种定义方法签名的类型,可以与具有兼容签名的任何方法关联.所谓兼容的方法,是指这个方法和委托的方法签名具有相同的返回类型和参数 i. 语法:delegate 方法签名; 1. 方法签名是方法的名字,没有方法体 ii. 使用委托的过程 1. 定义委托和委托对象 2. 编写要委托的方法 3. 将要委托的方法传递给委托对象 4. 使用委托调用委托方法 iii. 多播委托:是将多个与委托有相同方法签名的方法绑定到同一个方法上 1. 使用多播委托要求方法返回类型为 void. 2. 用"+=

.NET基础拾遗(4)委托、事件、反射与特性

Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 一.委托基础 1.1 简述委托的基本原理 委托这个概念对C++程序员来说并不陌生,因为它和C++中的函数指针非常类似,很多码农也喜欢称委托为安全的函数指针.无论这一说法是否正确,委托的的确确实现了和函数指针类似的功能,那就是提供了程序回调指定方法的机制. 在委托内部,包含了一个指向某个方法的指针(这一点上委托实现机制和C++的函数指针一致),为何称其