C# 委托与事件-事件篇

请按照顺序阅读

事件

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。

举一个生活中常见的例子。例如我们关注了某个博主,当他发布新动态的时候,所有关注者都会接受到消息推送,这个就是这个事件的例子----发布以及订阅。

例子

在下面这个例子中,我们先用常规的方式去实现一个,当玩家死亡时,其他对象要对此做出反应的行为需求。

using System;

/*
 * 我们想实现当玩家死亡的时候显示成就和游戏结束
 */

namespace Delegate
{
    /// <summary>
    /// 玩家
    /// </summary>
    class Play
    {
        public void Die()
        {
            UserInterface UI = new UserInterface();
            Achievements achievements = new Achievements();
            
            Console.WriteLine("玩家死亡!");
            UI.ShowUI();
            achievements.ShowScore();
        }
    }

    /// <summary>
    /// 用户界面
    /// </summary>
    class UserInterface
    {
        public void ShowUI()
        {
            Console.WriteLine("游戏结束!");
        }
    }

    /// <summary>
    /// 成就
    /// </summary>
    class Achievements
    {
        public void ShowScore()
        {
            Console.WriteLine("您当前的分数为 XX");
        }
    }
    
    public class EventSample
    {
        public static void Run()
        {
            Play play = new Play();
            play.Die();
        }
    }
}

你可以看到,这个非常容易实现而且很简便。但是这个里面有几点不足,对于后面的维护性很差,同时耦合也很大。

不足之处

  • 耦合很大:假如我一删除一个类,那么我的程序就挂掉了,运行直接报错。
  • 拓展困难:现在我要添加一个新的对象,敌人,他也需要对角色死亡时做出反应,那么我就需要到Play::Die这个函数中去修改,这样做肯定是没有问题。但是日后继续添加,又有新的需求,那么这个Die这个函数就会日益庞大,多了非常多奇奇怪怪的引用,对于后面维护的人或者是你会完全不知道这些对象是在干嘛的。同时问题一也会马上显现出来。
  • 依赖很强:依赖很强也是耦合大的原因之一,太多的依赖,太多的功能堆叠在一起,这个函数,这个类就会变的难以维护。
  • 维护性差:当你需求变动,例如我现在不需要你玩家死亡的显示游戏结束这个UI了,那么你肯定需要去Play::Die这个函数中去删除相关代码的,一段还好说,万一一手残删错了,就很尴尬了。

理想情况

我们只希望当角色死亡的时候,只需要简单的告诉我们就行了
举个不太恰当的例如。就好比,你在人群中,大喊一句,在场的各位都是**。那么在场的人想打你的肯定会自己过去揍你,而不用你自己去找他们。

using System;

/*
 * 我们想实现当玩家死亡的时候显示成就和游戏结束
 */

namespace EventPro
{
    public delegate void PlayerDeathDelegate();
    
    /// <summary>
    /// 玩家
    /// </summary>
    class Play
    {
        // 当玩家死亡时
        public PlayerDeathDelegate onPlayDeath;
        public void Die()
        {
            Console.WriteLine("玩家死亡!");
            if (onPlayDeath != null)
                onPlayDeath();
        }
    }

    /// <summary>
    /// 用户界面
    /// </summary>
    class UserInterface
    {
        public void ShowUI()
        {
            Console.WriteLine("游戏结束!");
        }
    }

    /// <summary>
    /// 成就
    /// </summary>
    class Achievements
    {
        public void ShowScore()
        {
            Console.WriteLine("您当前的成就为 XX");
        }
    }

    /// <summary>
    /// 敌人
    /// </summary>
    class Enemy
    {
        public void StopFire()
        {
            Console.WriteLine("停止攻击!");
        }
    }
    public class EventSample
    {
        public static void Run()
        {
            /*
             * UserInterface和Achievements已经和Play解耦,例如现在我们删除UserInterface也不会影响到其他的对象
             */
            Play play = new Play();
            UserInterface userInterface = new UserInterface();
            Achievements achievements = new Achievements();
            play.onPlayDeath += userInterface.ShowUI;
            play.onPlayDeath += achievements.ShowScore;
            
            // 新增需求,我们要玩家死亡时候,敌人停止攻击
            Enemy enemy = new Enemy();
            play.onPlayDeath += enemy.StopFire;
            
            play.Die();
        }
    }
}

但是有个问题,就是我们到现在用的还是委托啊,还没有用到事件啊。为了引出事件,我们必须要明白这段代码的几个问题。因为这个代码也是存在问题的。

理想情况之后的问题

首先,可以肯定的是,它是解耦了,维护性也提高了。问题就在于,在之前的委托篇中,我们的委托是可以被覆盖的啊(委托中的=操作符)。而且还可以在外部直接调用onPlayDeath(手贱党或者不熟悉的人都可能会出现的情况)。那么你可能会这样想。

  • 我谨慎点不就行了么
  • 我设置为private不就行了么

问题就在于在谨慎也会出错,墨菲定律听说过没有。设为private外部也就无法访问了,直接over。那么我们该如何避免这些问题?

是时候祭出事件大法Event

运用Event

事件(Event)它解决了两个问题,在某些场景委托无法避免的问题。

  • 防止委托(事件)被覆盖
  • 防止出现多个调用者,这个请结合上面的例子

把上面的代码添加一个字段就行

class Play
    {
        // 当玩家死亡时
        public event PlayerDeathDelegate onPlayDeath;
        public void Die()
        {
            Console.WriteLine("玩家死亡!");
            if (onPlayDeath != null)
                onPlayDeath();
        }
    }

这样不管你在怎么手残,你都无法对该委托进行破坏。其实从这里可以看出C#语言开发者的用心,只需简单的添加event字段就可以了。因此我们在了解了某些场景委托的弊端之后,再去了解事件,就会发现区分委托和事件也就简单。
添加之后可以试试看使用覆盖和调用,这个时候编译器就会直接报错。

猜你喜欢

转载自blog.csdn.net/qq448545478/article/details/106747553