C# 学习笔记:委托(4):事件(2)

接上一篇,我们了解了事件的一些基础用法和组成结构,这篇写一个完整的事件的例子

目前是这样,我是个玩家,然后我想要进入游戏大厅,进入大厅后出发开始游戏的事件,事件分发消息给订阅了事件的游戏启动器。

1.事件参数准备:

首先我们声明一个玩家类,里面包装了一个字段表明我们玩家的玩家账号名字。

    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;
                }
            }
        }
    }

这里我们使用了属性去设置游戏编号,根据游戏名字得来(虽然我觉得反着写合理一点点).

2.声明事件

声明事件的委托,注意我们声明用事件包装的委托的时候,一般在委托命名后缀为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,起到维护事件安全的作用。

3.声明事件处理器

紧接着,我们游戏登录后要调用游戏开始,也就是指我们事件中的事件的响应者和事件处理器:

    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实例,然后订阅了我们的游戏开始器,并传递参数到我们的触发委托之前的包装方法。

然后我们就实现了事件一整套流程,即,我打开游戏了游戏启动器做出反应这样一个事件模型。

效果还是刚刚的。

4.事件多播

为了体现事件的牛逼,我们声明一个妈妈类,妈妈在我开电脑的时候就“偷偷”订阅了我们的事件,这样,我一旦打开了游戏登录大厅(创建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;

然后,我们再运行看看:

显而易见,我的妈妈订阅了我上游戏的事件,她对我发出了严厉的批评,这勉励我好好学习,天天向上。

照这样下去,任何人,我爸我哥我姐姐都能非常轻松的知道我上线了与否,并进行相应的逻辑,这就是事件的好处,实现的相应的功能,且两边的代码的耦合度非常的低,他知道了我的形参,一行代码就能知道我的上线情况了,效果拔群。

5.归纳到事件模型

我们归纳一下上面的代码,再来带入一下事件模型:

            GameCenter gameCenter = new GameCenter();
            gameCenter.gameLoad += new GamerStarter().StartGame;
            gameCenter.gameLoad += new Mother().MotherIsWatching;
  • 事件的拥有者:GamerCenter类的实例gameCenter
  • 事件:gameLoad
  • 事件的订阅:如图
  • 事件的响应者:GamerStarter(游戏启动器)、Mother(我妈)。
  • 事件的处理器:StartGame(游戏开始,提示我的用户名游戏名)、MotherIsWatching(我妈的监控方法)。

这就是事件的简单应用。

6.总结事件

我们明显的看到上面的逻辑,我们单纯的使用一些普通的委托也是可以完成的,无非就是包装函数形成一个委托链然后一起依次调用罢了,那么,为什么要有事件这个用法呢,我们来看看委托的缺点:

  1. 委托是一种方法级别的耦合,如果我们把类对象比喻成一个房间,方法是房间里的一个个物体,那么委托将会把这些不同房间的物体串联起来,相当于打破了一个个类的墙壁。
  2. 委托一旦暴露给了类以外的接口,如果被滥用,就会大幅度的造成代码可读性的下降。委托暴露出去被别人实现了多个委托链,你整个方法挂在上面,他也整一个,搞下去不知道谁手贱触发了Invoke,整个代码就乱套了,Debug的时候就不知道天南地北了。

综上,委托会带来不必要的自由度。那么我的事件的功用就出来了:

事件是对委托类型的包装器,它并不是类中的字段。该包装器对委托字段的访问起到限制作用,相当于一个蒙版,它对外隐藏了委托实例的大部分功能(Invoke、MethodInfo、ObjectInfo等)。仅暴露出订阅和去除订阅的功能,保护了委托不被人滥用

所以,“事件是以特殊方式声明的委托字段”这句话是个误解。它仅能放在“+=”或者“-=”的左边,不是一个字段。它和属性相似,

  • 属性是字段的包装器,防止字段被污染。
  • 事件是委托的包装器,防止委托被滥用。
  • 这些包装器无法代表它被包装的东西。

嗯,关于委托的想发到博客上的就这些。都是刘铁猛老师C#教程的一些笔记,原本想随便写个例子的,结果那个例子就整了俩小时,流下了不懂代码的泪水~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值