当前位置:首页 > Windows程序 > 正文

匹夫细说C#:庖丁解牛聊委托,那些编译器藏的和U3D给的

2021-03-27 Windows程序

标签:

0x00 前言

由于工作繁忙所以距离上一篇博客已经过去一个多月的时间了,因此决心这个周末无论如何也得写点东西出来,既是总结也是分享。那么本文主要的内容集中在了委托的使用以及内部结构(当然还有事件了,但是受制于篇幅故分为两篇文章)以及结合一部分Unity3D的设计思考。当然由于时间仓促,文中难免有一些疏漏和不准确,也欢迎各位指出,共同进步。

0x01 从观察者模式说起

在设计模式中,有一种我们常常会用到的设计模式——观察者模式。那么这种设计模式和我们的主题“如何在Unity3D中使用委托”有什么关系呢?别急,先让我们来聊一聊什么是观察者模式。

首先让我们来看看报纸和杂志的订阅是怎么一回事:

报社的任务便是出版报纸。

向某家报社订阅他们的报纸,只要他们有新的报纸出版便会向你发放。也就是说,只要你是他们的订阅客户,便可以一直收到新的报纸。

如果不再需要这份报纸,则可以取消订阅。取消之后,报社便不会再送新的报纸过来。

报社和订阅者是两个不同的主体,只要报社还一直存在着,不同的订阅者便可以来订阅或取消订阅。

如果各位读者能看明白我上面所说的报纸和杂志是如何订阅的,那么各位也就了解了观察者模式到底是怎么一回事。除了名称不大一样,在观察者模式中,报社或者说出版者被称为“主题”(Subject),而订阅者则被称为“观察者”(Observer)。将上面的报社和订阅者的关系移植到观察者模式中,就变成了如下这样:主题(Subject)对象管理某些数据,当主题内的数据改变时,便会通知已经订阅(注册)的观察者,而已经注册主题的观察者此时便会收到主题数据改变的通知并更新,而没有注册的对象则不会被通知。

当我们试图去勾勒观察者模式时,可以使用报纸订阅服务,或者出版者和订阅者来比拟。而在实际的开发中,观察者模式被定义为了如下这样:

观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

那么介绍了这么多观察者模式,是不是也该说一说委托了呢?是的,C#语言通过委托来实现回调函数的机制,而回调函数是一种很有用的编程机制,可以被广泛的用在观察者模式中。

那么Unity3D本身是否有提供这种机制呢?答案也是肯定的,那么和委托又有什么区别呢?下面就让我们来聊一聊这个话题。

0x02 向Unity3D中的SendMessage和BroadcastMessage说拜拜

当然,不可否认Unity3D游戏引擎的出现是游戏开发者的一大福音。但不得不说的是,Unity3D的游戏脚本的架构中是存在一些缺陷的。一个很好的例子就是本节要说的围绕SendMessage和BroadcastMessage而构建的消息系统。之所以说Unity3D的这套消息系统存在缺陷,主要是由于SendMessage和BroadcastMessage过于依赖反射机制(reflection)来查找消息对应的回调函数。频繁的使用反射自然会影响性能,但是性能的损耗还并非最为严重的问题,更加严重的问题是使用这种机制之后代码的维护成本。为什么说这样做是一个很糟糕的事情呢?因为使用字符串来标识一个方法可能会导致很多隐患的出现。举一个例子:假如开发团队中某个开发者决定要重构某些代码,很不巧,这部分代码便是那些可能要被这些消息调用的方法定义的代码,那么如果方法被重新命名甚至被删除,是否会导致很严重的隐患呢?答案是yes。这种隐患的可怕之处并不在于可能引发的编译时错误,恰恰相反,这种隐患的可怕之处在于编译器可能都不会报错来提醒开发者某些方法已经被改名甚至是不存在了,面对一个能够正常的运行程序而没有警觉是最可怕的,而什么时候这个隐患会爆发呢?就是触发了特定的消息而找不到对应的方法的时候 ,但这时候发现问题所在往往已经太迟了。

另一个潜在的问题是由于使用了反射机制因而Unity3D的这套消息系统也能够调用声明为私有的方法的。但是如果一个私有方法在声明的类的内部没有被使用,那么正常的想法肯定都认为这是一段废代码,因为在这个类的外部不可能有人会调用它。那么对待废代码的态度是什么呢?我想很多开发者都会选择消灭这段废代码,那么同样的隐患又会出现,可能在编译时并没有问题,甚至程序也能正常运行一段时间,但是只要触发了特定的消息而没有对应的方法,那便是这种隐患爆发的时候。因而,是时候向Unity3D中的SendMessage和BroadcastMessage说拜拜了,让我们选择C#的委托来实现自己的消息机制吧。

0x03 认识回调函数机制----委托

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/68679.html