事件和委托的悲伤故事

小强的故事

从前的从前,有一个村子,叫双空函数村,各个不同的函数相互帮衬,幸福而稳定的劳作着。

虽然村子里每个函数都是空参空返回,但每个函数脑子里都装着不一样却一生注定的指令。(即程序开始编译之后)

class 双空血统()
{

public void 指令() { }//指令里的内容越多,实力越强

}

里面有一个函数叫小强   MrQiang=new 双空血统()。小强是个不走运的孩子,刚出生的时候继承了村子的双空血统,空参空返回,这倒没什么,最可怜的是,小强甚至指令都是空的。

从小到大,小强受尽了周围人的嘲笑,老函数门背后议论小强,中年函数不让孩子和小强玩

“和那个连指令没有的三空函数玩,小心变得没指令!”

小强不甘心自己一生只能执行这几条指令,于是踏上了寻求变化之法的旅途。

一年又一年,寻寻觅觅,道法却似乎总与小强无缘。小强也慢慢开始变得消沉,没盘缠了就在街边讨钱,钱够了也不继续赶路,而是在湖边喝酒。

就这样可怜的又过活了一年...

一天,小强讨够钱照常去湖边喝酒,举杯间刻,身后一只手突然停住小强的酒杯,

“孩子,我看你骨质疏松,面色憔悴,却颇有狼子之气,来,跟我学吧”

“你是?”

“老衲化名给特,你也可以叫我伟拓大师,看你也是求道之函数,教你灵活七十二变之法可好?”

小强眼睛都放亮了,当场下跪,哭着说“师傅!请教我本领!!”

经历了三年一日复一日的淬炼,小强勤学苦练,伟拓看在眼里很是欣慰。

最后,伟拓大师把自己的delegate秘法,传授给了小强,对小强说“小强,你已经是可以独当一面的函数了,和你同类型的函数里,已经不会有能在指令上战胜你的了,你可以走了”

小强没有说话,泪流满面,给伟拓大师连磕了三个头,然后踏上了返回双空村的道路。

即使在学习完这套功法之后,小强也无法斩去曾经那个空指令方法,只能以伟拓秘法为核心再造了自己的指令内核,

因为本质上,那个原方法的空指令已经是无法改变的事实。虽然当前的委托与原生的同名,但已经因为变化之法,不再固定了。

伟拓大师之秘法:public delegate void 变化之法();

class 双空血统()
{

public void 指令() { }

public 变化之法 新指令;//得到新的指令内核

}

一路上,小强也吸取(+=)了很多同血统的函数的指令,回到村子的时候,他已经是指令最多的函数了。

村中长老看到小强吸收了那么多无参无返回的指令,觉得小强使了妖术,要惩罚小强。

然而即使小强习得了delegate秘法,但新的指令内核并无稳定性,

长老们轻松施法

MrQiang.新指令=null;

把小强的所有指令清空了,并且关小强进小黑屋待了几个月。

 

小强不服,在小黑屋的几个月,他不停的思考着破局的方法,

“可恶,要怎么才能让那些老头子失去对我的指令的控制权?难道要变成private吗?可是那样我也吸取不了别人的双空指令了!师傅!我该怎么办!”。

......此时小强隐隐的感到一阵胃疼

“呓!胃疼......”

忽然之间,金光乍现,小强感受到一股前所未有的力量似乎流入了他的血液。这股力量不同于第一次习得变化之法产生的指令,而是像,得到了第二个可吸取别人的指令内核,并且和之前秘法修炼出的指令内核互相独立。

“?!!这种感觉!!”

伟拓大师之秘法:public delegate void 变化之法();

class 双空血统()
{

public void 指令() { }

public 变化之法 新指令;//只是伟拓秘法

public event 变化之法 第二新指令;//得到胃疼刺激的变化之法的再延伸

}

 两个月后,小强终于出小黑屋了,这一次,小强带着新的指令内核,为了防止再次被清空,他又离开了村子,去吸取漂泊四海的双空函数的指令。

而这一次,一过就又是三年,小强带着成吨的指令,再次回到了村子,村里的人对小强如此之多的指令再次议论纷纷,觉得小强又用了妖术,向长老告状。

而现在,长老们也无能为力了,因为小强现在的指令都在第二新指令里,已经是升级版的伟拓秘法了,外部已经不能再随意修改小强的指令了,

而唯一能再次清空小强指令的办法,只有一个一个的找到散落在天涯的被小强吸取(+=)过的双空人,从他们取消(-=)自己被吸取的部分。

故事的最后,小强因为指令数的庞大,拥有强大力量,双空村从此改名三空村

小强升职加薪,月薪过万,迎娶白富美,走上函数巅峰。

现在再看下专业一点的说明

事件给委托增加了保护机制

委托明明有各种函数在里面,却灵活的像个变量,但如果不加保护,可能带来以下问题:

  1. 随意调用:类外代码可以在任何时候调用委托,这可能在错误的时间触发不应发生的行为。

    public class HealthSystem
    {
        public UnityAction OnHealthLow;  // 委托,可以被外部代码赋值和调用
    
        public void CheckHealth(int health)
        {
            if (health < 20)
            {
                OnHealthLow?.Invoke();  // 触发委托
            }
        }
    }
    
    public class Game
    {
        public static void Main()
        {
            HealthSystem healthSystem = new HealthSystem();
    
            // 在健康系统外部肆意玩弄里面的委托变量,里面现有的委托被任意修改
            healthSystem.OnHealthLow = () => { Console.WriteLine("Health is low!"); };
    
            // 正常的调用健康系统里的委托
            healthSystem.CheckHealth(10);  // 输出: "Health is low!"
    
            // 外部代码可以随意调用健康系统里的public委托,委托像极了一个变量
            healthSystem.OnHealthLow();  // 输出: "Health is low!"  // 错误触发
        }
    }
  2. 覆盖或替换类外部代码可以替换委托的内容,导致原本绑定的回调被覆盖,影响程序的正确性和稳定性。如果程序中某个委托本应绑定特定方法,但外部代码替换了它,可能导致逻辑错误。

有了事件的保护后

public class HealthSystem
{
    public event UnityAction OnHealthLow;  //在前面加上event,委托就此支棱

    public void CheckHealth(int health)
    {
        if (health < 20)
        {
            OnHealthLow?.Invoke();  // 触发事件(也就是在健康系统里更硬气了的委托)
        }
    }
}

public class Game
{
    public static void Main()
    {
        HealthSystem healthSystem = new HealthSystem();

        // 外部只能给事件添砖加瓦,不能动里面已有的
        healthSystem.OnHealthLow += () => { Console.WriteLine("Health is low!"); };

        // 检查健康
        healthSystem.CheckHealth(10);  // 输出: "Health is low!"

        // 不是健康系统类里的也不准调用事件
        // healthSystem.OnHealthLow();  // 这行代码会报错,无法直接调用
    }
}

事件的保护机制主要体现在以下两方面:

  1. 触发控制:事件所在的类外,代码不能触发事件,只有定义事件的类内部才能触发它,确保事件只能在合适的时机执行。

  2. 防止覆盖或修改:外部代码不能直接修改或替换事件,只能订阅(+=)或取消订阅(-=),从而防止外部对事件行为的随意更改。

  3. 还有,要是+= lambda表达式这些匿名函数,就赖里面了

这两点保证了事件的安全性和可控性。

在 C# 中,如果你希望一个委托成为事件,你必须在已经声明的委托类型前面加上 event 关键字。这是将委托提升为事件的必要步骤。

委托不仅可以在类内部声明,还可以在类外部声明。

以下是unity里的GUI相关

button会触发方法,这里也是为着更好的保护委托,把委托升级成事件了,

所以在这里 public event UnityAction clickEvent;

 每个组件的都继承GUI的基类组件,如果有关事件触发,就额外增加参数,记录数值的变化

上一篇:leetcode每日一题day19(24.9.29)——买票需要的时间


下一篇:PostgreSQL的学习心得和知识总结(一百五十二)|transaction_timeout:达到事务超时时终止会话