事件基于委托,为委托提供了一个发布/订阅机制,这个在我们编写代码中用到的地方还是比较多的,也是比较重要的内容。
事件的定义很简单,只需要在委托前面加上关键字event就可以了,返回值是委托类型。
public event 委托类型 事件名;
但是注意委托可以声明一个局部变量,而事件只能在类的成员里进行声明。
那么事件有什么用呢?事件主要用于观察者模式中,观察者模式中有观察者和被观察者,被观察者的状态发生了改变之后需要通知观察者做出相应的动作,比如在游戏中当我们点击了一个开始按钮,就需要加载音乐、场景等,这里的按钮就是被观察者,场景和音乐管理器就是观察者。
下面以一个猫捉老鼠的案例进行解析。
老鼠听到猫来了之后就要进行逃跑的动作,这里猫是被观察者,老鼠是观察者。
首先定义好猫类和老鼠类,猫类有一个猫来了的方法,老鼠类有一个逃跑方法。
猫类基本属性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise
{
class Cat
{
private string name;
private string color;
public Cat(string name, string color)
{
this.name = name;
this.color = color;
}
public void CatComing(Mouse mouse1,Mouse mouse2)
{
Console.WriteLine(color + "的猫" + name + "来了,喵呜 ...");
mouse1.RunAway();
mouse2.RunAway();
}
}
}
老鼠类基本属性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise
{
class Mouse
{
private string name;
private string color;
public Mouse(string name, string color)
{
this.name = name;
this.color = color;
}
public void RunAway()
{
Console.WriteLine(color + "的老鼠" + name + "说: 猫来啦, 赶紧跑");
}
}
}
然后回到主程序中
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat("加菲猫", "黄色");
Mouse mouse1 = new Mouse("舒克", "黑色");
Mouse mouse2 = new Mouse("贝塔", "白色");
cat.CatComing(mouse1,mouse2);
Console.ReadKey();
}
}
}
如果不用观察者模式的话就是上面写的代码,将RunAway方法在CatComing中调用,这样就达到了猫来老鼠跑的效果。但是这样写有一个弊端,如果此时又新添了一个老鼠,我们就需要修改CatComing方法的参数,还要修改传进来的参数,还要新添逃跑的调用,实在是麻烦。
那应该怎样修改呢?我们就需要使用到观察者模式了,我们把猫看成被观察者,老鼠看成观察者。当被观察者状态发生了改变会发布一个消息,观察者订阅这个消息,当收到消息之后做出相应的动作。
在猫类中定义一个委托,在CatComing方法中调用这个委托代表的方法,在主程序中将相应老鼠的RunAway方法赋值给该委托。
Cat类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise
{
class Cat
{
private string name;
private string color;
public Cat(string name, string color)
{
this.name = name;
this.color = color;
}
public void CatComing()
{
Console.WriteLine(color + "的猫" + name + "来啦,喵呜 ...");
if (catCome != null)
catCome();
}
public Action catCome;
}
}
老鼠类不需要做修改
主程序类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat("加菲猫", "黄色");
Mouse mouse1 = new Mouse("舒克", "黑色");
cat.catCome += mouse1.RunAway;
Mouse mouse2 = new Mouse("贝塔", "白色");
cat.catCome += mouse2.RunAway;
Mouse mouse3 = new Mouse("米奇", "红色");
cat.catCome += mouse3.RunAway;
cat.CatComing();
Console.ReadKey();
}
}
}
这样每当新添一个老鼠,我们就把它的RunAway赋值给委托就好了。但是我们还可以更简单一点,那就是将委托的注册放到观察者的构造方法中。这就是发布订阅机制。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exercise
{
class Mouse
{
private string name;
private string color;
public Mouse(string name, string color, Cat cat)
{
this.name = name;
this.color = color;
cat.catCome += this.RunAway;//把自身的逃跑方法注册进猫的委托里面 订阅消息
}
public void RunAway()
{
Console.WriteLine(color + "的老鼠" + name + "说: 猫来啦, 赶紧跑");
}
}
}
那么事件有什么用呢?上面这么多代码还没有用到事件。如果我们在主程序中加上一行代码
cat.catCome();
它的那么效果和cat.CatComing();是一样的。实际上,这是一种比较危险的行为,我们定义的委托不想在类的外部触发。这就需要事件了,我们将委托改为事件之后
public event Action catCome;
这个委托就不能在类的外部调用了。所以总结起来就是事件是受限制的委托,可以在类的外部注册,但是不可以在类的外部触发。二者本质上还是一个东西。