前言:
本来早就想写写和代码设计相关的东西了,以前做2DX的时候就有过写写观察者设计模式的想法,但是实践不多。现在转到U3D的怀抱中,倒是接触了不少委托事件的写法,那干脆就在此总结一下吧。
1.C#中的委托、事件引入
本想去找一些高端的介绍来开场,但是找来找去感觉还是用百度百科中的例子来开场也是蛮适合的。当然要具体到Unity3d,我们还是要按照U3d的格式来写。
首先我们来看百度百科这个例子写的是什么?
ok,原来是全球化形势下,不同语种的小伙伴们问候早安时候的一个情景。那么最直观的做法,无非是判断哪国人,然后说英语的调用说英语的方法,说中文的调用说中文的方法,之后再有说日语的,说法语的还要再调用说日语的,说法语的方法。这样做当然OK,但是拓展性很差。
首先是不考虑使用委托时的写法
using UnityEngine; using System.Collections; public class delegateFanyoy : MonoBehaviour { // Use this for initialization public UIButton testBtn; void Start () { EventDelegate.Add (this.testBtn.onClick, this.BtnClick); } public void BtnClick() { GoodMoring ("chenjd"); } public void GoodMoring(string name) { Debug.Log ("GoodMoring " + name); } // Update is called once per frame void Update () { } }
很简单,首先利用EventDelegate为按钮的OnClick事件绑定一个方法,用来测试我们上面提到的问早安的功能。
结果如下:
那么问题来了,小匹夫可是堂堂中国人啊,怎么能不说中文反而天天鼓捣英语呢?所以刚刚实现问早的方法GoodMoring()就不能用咯,还要新写一个方法,要输出中文早安,然后再和点击按钮的事件绑定。这样是不是很麻烦呢?
如果有小伙伴觉得不麻烦,那小匹夫只能演示一种小匹夫认为不使用委托的前提下最直接的一种写法了。这时候,GoodMoring就需要改一改了,肯定要根据不同的人来选择不同的问候语咯。这里为了方便,定义一个枚举Language作为判断的依据:
using UnityEngine; using System.Collections; public class delegateFanyoy : MonoBehaviour { // Use this for initialization public UIButton testBtn; void Start () { EventDelegate.Add (this.testBtn.onClick, this.BtnClick); } public void BtnClick() { GoodMoring ("chenjd", Language.Chinese); } public void GoodMoring(string name, Language l) { switch (l) { case Language.Chinese: MoringChinese(name); break; case Language.English: MoringEnglish(name); break; } } public void MoringChinese(string n) { Debug.Log ("早上好 " + n); } public void MoringEnglish(string n) { Debug.Log ("goodmoring" + n); } public enum Language { Chinese, English } // Update is called once per frame void Update () { } }
如果再来一个日语普通话的小伙伴,或者再来一个韩语思密达的小伙伴,那么不可避免我们需要去修改GoodMoring这个函数去实现判断并调用正确的语言输出方法。这样拓展性体现在哪里呢?
如果说能有一个方法A,它的参数也是一个方法B,那么我们保留那个方法A而只需要传入不同的参数(方法B),不就可以灵活的应对了吗?再来一个日本人,一个韩国人,无非只是将说日语和说韩语的方法B传入到那个无需改动的方法A中就可以了,点击按钮触发的事件只需要方法A响应就可以咯。这里方法A就是我们的GoodMoring方法,而方法B作为参数是不同语言的MoringXXXX方法。
那么我们就可以引入代理的概念了。嗯,是那句很老套的——方法的参数是方法。
2.方法的参数是方法
那么我们开始进行第一步分析,千里之行始于足下嘛。
既然方法A的某个参数是方法B,我们所要利用的无非就是使用传入的方法B。所以我们之前的方法A--GoodMoring(string name, language l)方法就变成了
GoodMoring(string name, XXX MoringLanguage) { MoringLanguage(name); }
直接调用作为参数传入的方法。
但是这里又有新的问题出现了,涉及到要将方法作为参数传入另一个方法,我们都知道,参数的传入是需要有类型的呀。你MoringLanguage到底是个啥?
这里我们似乎又想到了,区分方法,无非是它的返回值,和传入参数类型个数。想到这里,豁然开朗,只要我们规定了传入的MoringLanguage的返回类型和MoringLanguage的参数类型是不是就可以证明传入的MoringLanguage的身份了呢?对!这就是代理了delegate了。
那我们按照MoringChinese和MoringEnglish的返回类型和参数类型来定义这个委托。
public delegate void MoringDelegate(string name);
那么我们将GoodMoring的修改为
GoodMoring(string name, MoringDelegate MoringLanguage) { MoringLanguage(name); }
就可以了。下面上代码。
using UnityEngine; using System.Collections; public class delegateFanyoy : MonoBehaviour { // Use this for initialization public UIButton testBtn; void Start () { EventDelegate.Add (this.testBtn.onClick, this.BtnClick); } public void BtnClick() { GoodMoring ("chenjd", MoringEnglish); GoodMoring ("小匹夫", MoringChinese); } public void GoodMoring(string name, MoringDelegate MoringLanguage) { MoringLanguage (name); } public delegate void MoringDelegate(string n); public void MoringChinese(string n) { Debug.Log ("早上好 " + n); } public void MoringEnglish(string n) { Debug.Log ("goodmoring" + n); } public enum Language { Chinese, English } // Update is called once per frame void Update () { } }
最后,做个小结,也用一句在网上烂大街的话好了。所谓委托就是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性哟。