先说说观察者模式
简单来说,就是存在一对多模式的时候。如果主题(subject)的内容发生改变,那么应当通知订阅了本主题(subject)的观察者(Observer)收到更新的通知,简单来说就是这么回事。
具体的设计模式嘛,再谈咯。
首先确定一件事情,U3D里的SendMessage与BroadcastMessage是有严重的缺陷的
首先是性能损耗。这个系统是非常依赖反射机制的。过度依赖反射机制非常影响性能。
当然这还不是最严重的问题。最严重的问题是,它的识别是依靠函数名字符串来识别的。如果重构中不小心重构了用于消息的方法名字,那就尴尬了。
还有就是因为使用了反射机制,这套消息系统对于似有方法也是可以调用的。但是这一个在类内部没有被调用过的私有方法谁会想到有人用反射调用过呢!如果删掉了,尴尬同上。
首先介绍委托delegate,其实比较类似于C++里的函数指针。
委托是支持重载的。就是说在给委托赋值的时候,如果有同名重载函数,委托会根据自己的类型选择合适的函数。
委托的回调中,判断委托为不为空是非常关键和必要的
委托的调用,其实是invoke的过程
比如md(value);(md为委托)其实相当于md.Invoke(value);
委托的内部实现暂时先不关注了,有兴趣可以看看书
事件
一个定义了时间成员的类型需要提供以下功能来实现这种交互的机制:
①方法能够订阅它对事件的关注
②方法能够取消它对事件的关注
③事件发生时,订阅了该事件的方法会收到通知
订阅者订阅事情的本质就是把委托类型的实例添加到委托列表中。
给个我自己写的例子吧
public enum NumType { _hp,_mp,_exp } public class BaseUnit { private float _hp; private float _mp; private float _exp; public string _name; public float hp { get { return _hp; } } public float mp { get { return _mp; } } public float exp { get { return _exp; } } public BaseUnit(string name) { _name = name; _hp = 100; _mp = 100; _exp = 0; } public delegate void changeNum(BaseUnit source, float amount, NumType numtype); public event changeNum OnNumChange; public void ChangeNum(float amount,NumType numtype) { switch (numtype) { case NumType._hp: _hp += amount; break; case NumType._mp: _mp += amount; break; case NumType._exp: _exp += amount; break; } if (OnNumChange != null) { OnNumChange(this, amount, numtype); } } }
这个算是对于原版的简化,这个是主题(subject)部分
接下来是观察者部分
public class BattleInforamtion : MonoBehaviour { BaseUnit hero; // Use this for initialization void Start () { hero = new BaseUnit("hiro"); hero.OnNumChange += Hero_OnNumChange; } private void Hero_OnNumChange(BaseUnit source, float amount, NumType numtype) { print(source._name + "の" + numtype.ToString() + " " + "增加了:" + amount); } // Update is called once per frame void Update () { } void OnGUI() { if(GUI.Button(new Rect(0, 0, 100, 30), "加血量")) { hero.ChangeNum(100, NumType._hp); } if (GUI.Button(new Rect(0, 30, 100, 30), "加魔量")) { hero.ChangeNum(100, NumType._mp); } if (GUI.Button(new Rect(0, 60, 100, 30), "加经验")) { hero.ChangeNum(100, NumType._exp); } } }
嘛,大概就是这个样子,形式其实是蛮简单的。
其实我很想知道怎么用枚举的名字直接来调用同名的字段啊!超想知道!!!!!!!!
委托的简化语法
匿名方法有点类似于js里面那一套function 机制,就是作为常量的函数。
不用声明返回类型,自己return类型然后判断。
Action<T>是返回void的泛型委托,Func<T>将类型参数的最后一个作为返回类型的泛型委托(注意要引入system命名空间)
Predicate<T>为返回bool类型的,Comparison<T>接受两个T类型的参数返回一个int值
comparison结合sort非常好用,二者结合可以用来做一些有趣的排序的效果出来
举个例子
public class Adelegate : MonoBehaviour { List<BaseUnit> _listunits; Comparison<BaseUnit> baseunitcompa; // Use this for initialization void Start() { _listunits = new List<BaseUnit>(); for (int i = 0; i <= 5; i++) { _listunits.Add(new BaseUnit(true)); } } // Update is called once per frame void Update() { } void OnGUI() { GUILayout.BeginArea(new Rect(0, 0, Screen.width, Screen.height)); if (_listunits != null) { foreach (BaseUnit baseunit in _listunits) { string content; content = "Name:" + baseunit._name + "n" + "HP:" + baseunit.hp + "n" + "MP:" + baseunit.mp + "n" + "EXP:" + baseunit.exp; GUILayout.Box(content); } if (GUILayout.Button("按hp排序")) { SortListByNumType(BaseUnit.NumType._hp, _listunits); } if (GUILayout.Button("按mp排序")) { SortListByNumType(BaseUnit.NumType._mp, _listunits); } if (GUILayout.Button("按exp排序")) { SortListByNumType(BaseUnit.NumType._exp, _listunits); } } GUILayout.EndArea(); } void SortListByNumType(BaseUnit.NumType numtype,List<BaseUnit> _list) { switch (numtype) { case BaseUnit.NumType._hp: baseunitcompa = delegate (BaseUnit b1, BaseUnit b2) { return b1.hp.CompareTo(b2.hp); }; break; case BaseUnit.NumType._mp: baseunitcompa = delegate (BaseUnit b1, BaseUnit b2) { return b1.mp.CompareTo(b2.mp); }; break; case BaseUnit.NumType._exp: baseunitcompa = delegate (BaseUnit b1, BaseUnit b2) { return b1.exp.CompareTo(b2.exp); }; break; } _list.Sort(baseunitcompa); } }
嘛,算是用了书上的好办法了吧。
闭包,这是使用匿名方法时另一个需要注意的点。
外部变量指的是,定义了匿名方法的作用域内,匿名方法之外的局部变量或者参数
捕获的外部变量:在匿名方法中使用了的外部变量
这个闭包特性,使得本来的变量的生命周期超过了他本来应该有的生命周期。
并且因此,所有的局部变量并非全都被分配在栈上,也有可能分配在托管堆上
举一个闭包的例子吧:
void Start() { Action<int> printint = testbibao(); printint(10); printint(100); printint(1000); printint(10000); } private Action<int> testbibao() { int n = 0; Action<int> testint = num => { n += num; print(n); }; testint(n); return testint; }
对应的输出也非常有趣
输出依次是0,10,110,1110,11110
最后说一下Lambda表达式,其实就是匿名方法的一种简写
书上规则总结得很好,这里就按照它的总结来说一下吧:
情景 | 例子 |
如果委托实例不需要获取任何参数,那么可以直接使用() | Func<string>Name=()=>"U3D"; |
如果委托实例需要获取一个或者一个以上的参数,可以显示指定参数类型,也可以不指定 |
Comparsion<int>compare = ( b1, b2)=> b1.exp.CompareTo(b2.exp); Comparsion<int>compare = (int b1, int b2)=> b1.exp.CompareTo(b2.exp); |
如果委托实例获取一个参数,则可以省略括号 | Func<int,string>name=n=>n.ToString(); |
如果委托实例有ref/out类型,必须指定其类型与参数 |
delegate void DelTest(out int number); DelTest deltest=(out int number)=>number=0; |
表达式的语句有两句以及以上时,必须要和匿名方法一个写法了,return不能省略,大括号得加 |
略 |
- 还没有人评论,欢迎说说您的想法!