Subject<T> 是UniRx 推荐的 事件实现方式 ( UniRx是AssetStore上的一款免费插件,但其实他的出身很地道,脱胎于微软的Rx框架,主要思想是像Linq那样对订阅的事件进行操作,并且他还能对操作进行 时间调度,线程调度,而UniRx就是Rx的Unity版) 。在开发中,这种数据发射工作(在使用UniRx时,可以把事件触发时调用观察者回调并传递参数的行为 形象的理解成发射,这样有助于理解万物皆Stream的这种编程思想),一般会让订阅方和发射方解耦,彼此不用了解对方的存在,SubjectManager的意义就在于此。 此系统中,用继承于SubjectArgs基类的一个一个子类来表示不同的事件,同时类本身作为事件传递的参数类型,你可以在里面随便定义任何你需要在这种事件里传递的数据
下面是实现
//主题参数基类
public abstract class SubjectArgs
{
public object sender = null;
public abstract int SubjectId { get; }
}
//主题管理器
public class Subject_Manager {
//主题字典,以主题传递的参数类型提供的hashCode为key
private Dictionary<int, Subject<SubjectArgs>> mSubjectDic = new Dictionary<int, Subject<SubjectArgs>>();
//通过id拿到主题
public IObservable<T> GetSubject<T>()where T : SubjectArgs
{
int subjectId = typeof(T).GetHashCode();
Subject<SubjectArgs> subject = null;
if (!mSubjectDic.TryGetValue(subjectId, out subject))
{
subject = new Subject<SubjectArgs>();
mSubjectDic.Add(subjectId, subject);
}
return subject.Select(_ => _ as T);
}
//数据,发射!
public void Fire<T>(T e)where T:SubjectArgs
{
Subject<SubjectArgs> subject = null;
if (!mSubjectDic.TryGetValue(e.SubjectId, out subject))
return;
subject.OnNext(e);
}
}
加一层静态封装,用起来稍微舒服点
public static class SubjectManager
{
private static Subject_Manager mInstance = new Subject_Manager();
public static IObservable<T> GetSubject<T>() where T : SubjectArgs
{
return mInstance.GetSubject<T>();
}
public static void Fire<T>(T e) where T : SubjectArgs
{
mInstance.Fire<T>(e);
}
}
然后进行测试
public class HappySubjectArgs : SubjectArgs //Todo用对象池缓存这些炮弹...
{
public static readonly int ID = typeof(HappySubjectArgs).GetHashCode();
public override int SubjectId { get { return ID; } }
public int HappyDegree = 0;
}
public class SMTest : MonoBehaviour {
//注销器集合,所有订阅的注销器都往这里塞
CompositeDisposable mSubjectUnRegister=new CompositeDisposable();
void Start () {
//只接收开心程度大于10的发射...
SubjectManager.GetSubject<HappySubjectArgs>().Where(e => e.HappyDegree > 10)
.Subscribe(e => print("开心程度:"+e.HappyDegree+"...看起来很开心")).AddTo(mSubjectUnRegister);
//开心程度会随着点击增长...
Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0))
.Select((_,count)=>count)
.Subscribe(count => SubjectManager.Fire(new HappySubjectArgs() { HappyDegree = count}));
//没有多余字段的类...看起来真的很舒爽...
}
void OnDestroy()
{
//此对象销毁时注销器注销 就算重复注销也没关系 这就是UniRx用起来舒服的点点滴滴...
mSubjectUnRegister.Dispose();
}
}
这样你可以在任何地方发射 在任何地方订阅
同时可以考虑增加一种轻量级的消息发射方式,使用string作为消息的key,object作为传递的参数(value是subject<object>)
这样不用每种消息都要去实现一个类,但是缺点是如果参数是struct的话,就要装箱和拆箱
总之,UniRx版的事件subject 的优点主要在于 可以对发射来的数据进行各种定制操作, 无缝对接UniRx所有的操作符,不多,也就七八十个把(滑稽脸),随便举个例子好把,比如First,只要在订阅前添加这个操作符就能优雅的实现只订阅一次的功能