1、委托Delegate
首先说一下delegate委托,委托是将方法作为参数进行传递。
// 定义了一个委托类型public delegate void MyDelegate(int num);// 定义了一个啥也不干的委托实例public MyDelegate m_delegate = _ => {};// 定义了一个和委托相同格式的方法public void MyFun(int num){Console.WriteLine(num);}; // 添加新的委托m_delegate += MyFun;// 执行任务m_delegate(2);
2、基于委托的发布/订阅模式
基于上例,对于如下的操作:
m_delegate += MyFun1;
m_delegate += MyFun2;
....
m_delegate(1);
这是一个委托的多播操作,一连串的任务会一起执行。
此时换一个思维,假如把+=操作看成是订阅操作。m_delegate(1)是发布,1可以看做是一个message,可以是int,也可以是string。这样就构成了一个发布/订阅模式。
当m_delegate(1)发布执行时,委托的多播会让所有订阅函数执行。
所以订阅者只需要把委托格式的函数告诉发布者(+=注册进去),当发布者发布时,所有的订阅者就相当于收到了消息。
下面写一个基于委托的发布/订阅模式:
#define DEBUG3using System;
using System.Diagnostics;namespace ConsoleApp1
{public delegate void SayGate(int num);public class Publisher{// 定义委托public SayGate SayGateHandler;// 触发任务public void OnTrigger(int num){// 触发任务SayGateHandler(num);}}public class Subscriber{public void Subscribe(Publisher publisher){// 订阅publisher.SayGateHandler += SayHello;}public void SayHello(int num){Console.WriteLine("subscribe1: " + num);}}public class Subscriber2{public void Subscribe(Publisher publisher){// 订阅publisher.SayGateHandler += SayHello;}public void SayHello(int num){Console.WriteLine("subscribe2: " + num);}}internal class Program{static void Main(string[] args) {Publisher publisher = new Publisher();Subscriber subscriber = new Subscriber();subscriber.Subscribe(publisher);Subscriber2 subscriber2 = new Subscriber2();subscriber2.Subscribe(publisher);Console.ReadLine();publisher.OnTrigger(1);Console.ReadLine();}}
}
执行后会显示:
subscribe1: 1
subscribe2: 1
3、存在的缺陷
假如有个订阅者干了坏事,把"+="写成了"=",比如:
此时执行后会显示:
subscribe2: 1
即只显示了一条。
4、事件Event
针对上面的缺陷,需要引入一种机制限制订阅者的行为。
在C#中,event是一种特殊的委托,特殊之处为:订阅者只能通过"+="和"-="订阅或取消。发布者仍然可以进行"="操作。
当将SayGateHandler定义为event之后,订阅者2"="方式的订阅提示失败。
错误信息为:事件"Publisher.SayGateEvent"只能出现在+=或-=的左边(从类型“Publisher“中使用时除外)
新程序如下:
namespace ConsoleApp1
{public delegate void SayGate(int num);public class Publisher{// 定义委托public event SayGate SayGateHandler;// 触发任务public void OnTrigger(int num){// 触发任务SayGateHandler(num);}}public class Subscriber{public void Subscribe(Publisher publisher){// 订阅publisher.SayGateHandler += SayHello;}public void SayHello(int num){Console.WriteLine("subscribe1: " + num);}}public class Subscriber2{public void Subscribe(Publisher publisher){// 订阅publisher.SayGateHandler += SayHello;}public void SayHello(int num){Console.WriteLine("subscribe2: " + num);}}internal class Program{static void Main(string[] args) {Publisher publisher = new Publisher();Subscriber subscriber = new Subscriber();subscriber.Subscribe(publisher);Subscriber2 subscriber2 = new Subscriber2();subscriber2.Subscribe(publisher);Console.ReadLine();publisher.OnTrigger(1);Console.ReadLine();}}
}
5、存在的缺陷2
以上程序有2个订阅者,但是假如没有订阅者,此时发布后会怎么样?
将以上代码注释后做测试,报错信息如下:
此处报错的意思是:SayGameHandler没有实例化。
有两种解决方案:
1)发布者实例化一个空的委托,
public event SayGate SayGateHandler = _ => { };
2)触发事件时检测下是否实例化,
// 触发任务
SayGateHandler?.Invoke(num);
两种方法选择一种即可。
最后一个完整的Event示例如下:
namespace ConsoleApp1
{public delegate void SayGate(int num);public class Publisher{// 定义委托public event SayGate SayGateHandler;// 触发任务public void OnTrigger(int num){// 触发任务SayGateHandler?.Invoke(num);}}public class Subscriber{public void Subscribe(Publisher publisher){// 订阅publisher.SayGateHandler += SayHello;}public void SayHello(int num){Console.WriteLine("subscribe1: " + num);}}public class Subscriber2{public void Subscribe(Publisher publisher){// 订阅publisher.SayGateHandler += SayHello;}public void SayHello(int num){Console.WriteLine("subscribe2: " + num);}}internal class Program{static void Main(string[] args) {Publisher publisher = new Publisher();Subscriber subscriber = new Subscriber();subscriber.Subscribe(publisher);Subscriber2 subscriber2 = new Subscriber2();subscriber2.Subscribe(publisher);Console.ReadLine();publisher.OnTrigger(1);Console.ReadLine();}}
}