之前写了一个狮子捉动物的例子,传送门:C# 发布-订阅模式
在那个例子中,用到了委托和事件,这里对例子里的代码进行详细解说,加深自己的理解。
首先,在公共部分定义一个委托:
public delegate void FindEventHandler(string animals); // 可以看出这个委托是带有一个 string 类型参数的
然后我们分析一下狮子类:
public class 狮子 { // 基于上面定义的委托定义了一个事件 public event FindEventHandler FindEvent; public void Find(string animals) { Console.WriteLine("狮子发现了{0}", animals); if (FindEvent != null) { FindEvent(animals); } } }
在狮子类中,定义了一个事件 FindEvent,并且在 Find 方法中触发了该事件。在触发事件之前,为什么要判断事件是否为不空呢?这个后面解释。
接下来我们看一下动物类,以 A 为例:
public class A { public A(狮子 狮子) //A 的构造函数 { 狮子.FindEvent += new FindEventHandler(a_FindEvent);//订阅发现事件 } void a_FindEvent(string animals) { if(animals.Contains("A")) { Run(); } } public void Run() { Console.WriteLine("我是 A,被狮子发现了,我要跑了"); } }
在 A类 的构造函数中,接收了一个狮子类参数,没错,该形参的数据类型就是类。然后在构造函数中把方法 a_FindEvent 注册到了该类的事件上,如果没人向 FindEvent 注册,则 FindEvent 事件就为 null,执行就会报错,这就是为什么狮子类中触发事件的时候先判断了一下 FindEvent 事件是否为空。
注册了之后,只要 FindEvent 事件触发,就会自动执行 a_FindEvent 方法。
然后我们看一下主函数:
static void Main(string[] args) { 狮子 狮子 = new 狮子(); A a = new A(狮子); B b = new B(狮子); 狮子.Find("A"); 狮子.Find("B"); 狮子.Find("AB"); Console.ReadLine(); // 由于A和B订阅了狮子的事件,所以无需任何代码,A和B均会按照约定进行动作。 }
先实例化了一个狮子,然后把狮子丢在了大草原,动物 A 和 B 感受到了狮子的杀气(接收了狮子类),狮子发现 A ,A 跑;狮子发现 B ,B 跑;狮子发现 A、B ,A、B 都跑。
至此整个流程已经走通了,下面,再看看 类、委托、事件、方法到底是怎么联动的。
目前的理解如下图所示,委托就是一个指针,存储着事件的地址,方法注册到事件就是把方法的地址添加到了事件地址的后面,所以事件触发之后,所有注册到事件的方法就按照地址顺序依次执行了。
肯定是按照注册顺序(地址顺序)执行的,因为我调换过 A、B 实例化的顺序,执行顺序也就相应的调换了。
不使用 if…else,而使用这种模式,目的就是实现代码解耦,提高复用性、扩展性,这才是面向对象编程嘛!