小强的故事
从前的从前,有一个村子,叫双空函数村,各个不同的函数相互帮衬,幸福而稳定的劳作着。
虽然村子里每个函数都是空参空返回,但每个函数脑子里都装着不一样却一生注定的指令。(即程序开始编译之后)
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 变化之法 第二新指令;//得到胃疼刺激的变化之法的再延伸
}
两个月后,小强终于出小黑屋了,这一次,小强带着新的指令内核,为了防止再次被清空,他又离开了村子,去吸取漂泊四海的双空函数的指令。
而这一次,一过就又是三年,小强带着成吨的指令,再次回到了村子,村里的人对小强如此之多的指令再次议论纷纷,觉得小强又用了妖术,向长老告状。
而现在,长老们也无能为力了,因为小强现在的指令都在第二新指令里,已经是升级版的伟拓秘法了,外部已经不能再随意修改小强的指令了,
而唯一能再次清空小强指令的办法,只有一个一个的找到散落在天涯的被小强吸取(+=)过的双空人,从他们取消(-=)自己被吸取的部分。
故事的最后,小强因为指令数的庞大,拥有强大力量,双空村从此改名三空村
小强升职加薪,月薪过万,迎娶白富美,走上函数巅峰。
完
现在再看下专业一点的说明
事件给委托增加了保护机制
委托明明有各种函数在里面,却灵活的像个变量,但如果不加保护,可能带来以下问题:
-
随意调用:类外代码可以在任何时候调用委托,这可能在错误的时间触发不应发生的行为。
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!" // 错误触发 } }
-
覆盖或替换:类外部代码可以替换委托的内容,导致原本绑定的回调被覆盖,影响程序的正确性和稳定性。如果程序中某个委托本应绑定特定方法,但外部代码替换了它,可能导致逻辑错误。
有了事件的保护后
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(); // 这行代码会报错,无法直接调用
}
}
事件的保护机制主要体现在以下两方面:
-
触发控制:事件所在的类外,代码不能触发事件,只有定义事件的类内部才能触发它,确保事件只能在合适的时机执行。
-
防止覆盖或修改:外部代码不能直接修改或替换事件,只能订阅(+=)或取消订阅(-=),从而防止外部对事件行为的随意更改。
-
还有,要是+= lambda表达式这些匿名函数,就赖里面了
这两点保证了事件的安全性和可控性。
在 C# 中,如果你希望一个委托成为事件,你必须在已经声明的委托类型前面加上 event
关键字。这是将委托提升为事件的必要步骤。
委托不仅可以在类内部声明,还可以在类外部声明。
以下是unity里的GUI相关
button会触发方法,这里也是为着更好的保护委托,把委托升级成事件了,
所以在这里 public event UnityAction clickEvent;
每个组件的都继承GUI的基类组件,如果有关事件触发,就额外增加参数,记录数值的变化