接上一篇,我们了解了事件的一些基础用法和组成结构,这篇写一个完整的事件的例子
目前是这样,我是个玩家,然后我想要进入游戏大厅,进入大厅后出发开始游戏的事件,事件分发消息给订阅了事件的游戏启动器。
首先我们声明一个玩家类,里面包装了一个字段表明我们玩家的玩家账号名字。
public class Gamer
{
public string GamerName;
public Gamer(string gamername)
{
GamerName = gamername;
}
}
然后我们声明我们的事件参数EventArgs类来表明我们的事件参数有哪些内容,注意事件参数类要继承自EventArgs类型。
public class GameEventArgs : EventArgs
{
public string GameName { get; set; }
public int GameNumber
{
get
{
if (GameName == "彩虹六号")
{
return 1;
}
else if (GameName == "神界原罪")
{
return 2;
}
else
{
return 233;
}
}
}
}
这里我们使用了属性去设置游戏编号,根据游戏名字得来(虽然我觉得反着写合理一点点).
声明事件的委托,注意我们声明用事件包装的委托的时候,一般在委托命名后缀为EventHandler。
public delegate void GameLoadEventHandler(Gamer sender, GameEventArgs e);
里面是两个参数,第一个是我们的事件拥有者,我们这里使用了我们的玩家账户来表明是谁登录的游戏大厅。然后传递给游戏启动器我们需要启动的游戏事件参数。
然后我们建立一个事件所包装的类,即我们的游戏大厅:
public class GameCenter
{
private GameLoadEventHandler gameLoadEventHandler;
//声明委托实例
public event GameLoadEventHandler gameLoad
{//创建事件来包装我们的委托,使外部对委托的访问起到限制作用
add
{
gameLoadEventHandler += value;
}
remove
{
gameLoadEventHandler -= value;
}
}
private Gamer gamer;
//创建Gamer类型的字段
public void LoadingGame(Gamer gamerUser,string gameName)
{//加载游戏,我们输入游戏大厅的账户名和游戏名
Console.WriteLine("=========进入游戏中心=========");
gamer = gamerUser;
OngameLoad(gameName);
}
protected void OngameLoad(string gameName)
{
//触发委托,创建事件消息实例
GameEventArgs e = new GameEventArgs();
e.GameName = gameName;
//触发事件
gameLoadEventHandler.Invoke(gamer, e);
}
}
委托和包装它的事件都放在一起,使用起来类似于属性,来封装我们的委托,使用Add和Remove方法,使得委托在外面仅暴露出+=和-=。然后我们需要自己实现事件的订阅。再触发委托。
注意,触发事件(Invoke)所在的函数必须要为事件的拥有者来执行,写在所在的类中,一般为protected而不是public,起到维护事件安全的作用。
紧接着,我们游戏登录后要调用游戏开始,也就是指我们事件中的事件的响应者和事件处理器:
public class GamerStarter
{
public void StartGame(Object sender,GameEventArgs e)
{
Console.WriteLine("=========进入游戏启动器=========");
Console.WriteLine("用户识别中。。。。。。");
Console.WriteLine("登陆成功!您好!用户:"+((Gamer)sender).GamerName);
Console.WriteLine("您登录的游戏是"+e.GameName+",游戏编号为"+e.GameNumber);
}
}
你看,这里我们的事件接收器的形参列表和委托中是一样一样的。而且传进来的内容我都会一清二楚。不过使用Object类型的话,要进行一下类型转换才能看到Gamer类里的字段。
然后我们就可以调用Gamer来登录游戏了:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("请输入用户名");
string name = Console.ReadLine();
Console.WriteLine("请输入要开始的游戏");
string GameName = Console.ReadLine();
Gamer gamer = new Gamer(name);
GameCenter gameCenter = new GameCenter();
gameCenter.gameLoad += new GamerStarter().StartGame;
gameCenter.LoadingGame(gamer, GameName);
}
}
这样我们创建了Gamer实例,然后订阅了我们的游戏开始器,并传递参数到我们的触发委托之前的包装方法。
然后我们就实现了事件一整套流程,即,我打开游戏了游戏启动器做出反应这样一个事件模型。
效果还是刚刚的。
为了体现事件的牛逼,我们声明一个妈妈类,妈妈在我开电脑的时候就“偷偷”订阅了我们的事件,这样,我一旦打开了游戏登录大厅(创建GameCenter实例),我妈也接受到了事件的发生。妈妈是这样写她的类的:
public class Mother
{
public void MotherIsWatching(Object sender,GameEventArgs e)
{
string ChildName = ((Gamer)sender).GamerName;
if(ChildName=="xsy")
{
Console.WriteLine("妈妈:xsy还敢打游戏????");
Console.WriteLine("妈妈:以后不准玩"+e.GameName+"了!!!");
}
}
}
然后她也偷偷地订阅了我们的游戏大厅,随时随地查看我的上线情况。
gameCenter.gameLoad += new Mother().MotherIsWatching;
然后,我们再运行看看:
显而易见,我的妈妈订阅了我上游戏的事件,她对我发出了严厉的批评,这勉励我好好学习,天天向上。
照这样下去,任何人,我爸我哥我姐姐都能非常轻松的知道我上线了与否,并进行相应的逻辑,这就是事件的好处,实现的相应的功能,且两边的代码的耦合度非常的低,他知道了我的形参,一行代码就能知道我的上线情况了,效果拔群。
我们归纳一下上面的代码,再来带入一下事件模型:
GameCenter gameCenter = new GameCenter();
gameCenter.gameLoad += new GamerStarter().StartGame;
gameCenter.gameLoad += new Mother().MotherIsWatching;
这就是事件的简单应用。
我们明显的看到上面的逻辑,我们单纯的使用一些普通的委托也是可以完成的,无非就是包装函数形成一个委托链然后一起依次调用罢了,那么,为什么要有事件这个用法呢,我们来看看委托的缺点:
所以,“事件是以特殊方式声明的委托字段”这句话是个误解。它仅能放在“+=”或者“-=”的左边,不是一个字段。它和属性相似,
嗯,关于委托的想发到博客上的就这些。都是刘铁猛老师C#教程的一些笔记,原本想随便写个例子的,结果那个例子就整了俩小时,流下了不懂代码的泪水~