链式编程与UniRX的基础使用
链式编程与UniRX的基础使用
第一部分:链式编程
链式方法在C#中为拥有返回值的扩展静态方法
- C#里面可以写一种特殊的工具方法,专门用来扩展某个类型的使用,任意此类型都可以直接呼出此方法,这种方法称作为扩展方法。
- 扩展方法必须放在一个非泛型静态类中,一般设置一个工具类来存放即可。
1 | public static class UtilityExtension//这是一个非泛型静态类,static就是静态修饰符 |
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
- 好了,已经知道了扩展类怎么使用了,那么什么样才是链式编程哪?
- 很简单,扩展类携带返回值即可。
1 | public static class UtilityExtension |
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
- 这些方法能像链条一样一个接一个地写下去,与节点式编程的逻辑很像。
- 它们都有一些相同的特征:
- 链式方法会从一种数据类型呼出,类似节点的输入。
- 链式方法会从一种数据类型返回,类似节点的输出。
- 下面还有一个例子,是关于加减法的,可能更好理解。
1 | public static class UtilityExtension |
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
- 为了对比与传统方法的区别,我特地附注了传统方法示例:
1 | public static class UtilityExtension//此处没有静态类的限制,图方便就这么写了 |
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
第二部分:UniRX的基础使用
- 最近终于有时间来了解这个代码集了,因为只自学了两天,所以可能有些条目存在理解问题。
- UniRX是什么我不会去介绍,因为我不知道这么去给它(Unity的响应式编程)下定义。但是它能实现一些很强的功能,这些功能的使用方式是基于链式编程的。
- 我将一些基础方法做了分类,先介绍基础使用,再介绍这些单独的方法。
怎么获取UniRX
〇:一些基础前置知识(可跳过)
[前置知识]C# Lambda表达式(匿名方法)
- 用途:用作一个匿名的方法,如果你不想在脚本底下再定义一个额外的方法的话。
- Lambda表达式一般写作:
()=>{}
1 | using UnityEngine.Events;//引入命名空间 |
- 有时候匿名方法里需要传参。
- 当需要传参时,写作:
(int i)=>{}
或(i)=>{}
或i=>{}
1 | using UnityEngine.Events;//引入命名空间 |
壹:最简单的UniRX结构
1 | //这些是可能需要的命名空间引入 |
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
- 第一行“
Observable.EveryUpdate()
”构成了一个观察器,此时返回值类型为IObservable
。 - 第二行“
.Subscribe(_ =>{print("TODO");})
”为此观察器订阅了一个onNext事件,构成了一个释放器,此时返回值类型为IDisposable
。 - 第三行“
.AddTo(this)
”将此释放器的生命周期绑定到本物体上,语句结束。 - 这个消息语句的目的是:在每帧游戏刷新时输出“TODO”,直到本物体的销毁。
- 这个消息语句替换为节点化编程类似于:
1 | graph LR |
贰:观察者、观察器与释放器
- 观察者是Observer,接口为IObserver
- 观察器是Observable,接口为IObservable
- 释放器是Disposable,接口为IDisposable
【观察者】
Subscribe
操作符主要干一个功能,将输入的观察器绑定到新建的观察者上,并向观察者输入三状态方法:onNext
,onComplete
,onError
。- 由于观察者一般是操作符自动生成的,所以一般不会有什么特殊操作。
【观察器】
- 有多种方法可以用来生成观察器,不仅仅只有
EveryUpdate
一种。 - 比如说从某游戏物体身上生成的观察器,这样的观察器转换的释放器可以不考虑绑定问题。
1 | [public GameObject GO_Sample;//外部获取一个游戏物体 ] |
- 以及由集合类型演变而成的观察器,将每一个数据转化为一个该类型的信号来输出(字典也可以)。
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
- UniRX有一些响应化的数值,也可以转换成观察器,仅当值变化时触发事件。
1 | public ReactiveProperty<int> RP_Int = new ReactiveProperty<int>();//响应化类型 |
- 有一种泛型拓展方法,可以记录某某类中的某某值是否发生了改变:
1 | [public CharacterController cc;//获取角色控制器 ] |
【释放器】
- 当你使用
Subscribe
方法将观察器转为释放器后,需要去考虑释放器如何从内存中释放
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
- 释放器有一个独立版本,类型为
CompositeDisposable
。多个释放器可以被绑定在一起释放。
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
叁:主体类Subject
Subject
类型既实现了IObserver
接口,又实现了IObservable
接口。- 可以把
Subject
想象成一个用户自定义的观察器,用户能够随时调用观察器的观察者,而像其他观察器就无法手动调用。
1 | public Subject<int> SUB_Int = new Subject<int>();//主体化类型 |
肆:事件类操作符(常用)
Subscribe
:注册事件AddTo
:绑定事件Do
:前置事件
1 | //示例用于演示Subscribe操作符的用法 |
伍:流程类操作符(常用)
Where
:条件判断
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
OfType
:类型判断
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Distinct
:当与之前输出不同时才输出
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
DistinctUntilChanged
:当与前一个输出不同时才输出
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
StartWith
:将信息放置在输出之前来执行
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
陆:断点类操作符(常用)
First
:当第一次触发时输出
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Last
:当最后一次触发时输出(需要使用其他语句来告诉程序何时为最后一次输出)
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Take
:输出次数限制(TakeWhile
TakeUntil
)
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Skip
:输出次数忽略(SkipWhile
SkipUntil
)
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
柒:延时类操作符(常用)
Delay
:延时(DelayFrame
)
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
NextFrame[观察器]
:等待下一帧
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Timer[观察器]
:计时器
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Interval[观察器]
:循环计时器
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
TimeInterval
:获取触发时间间隔
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
捌:采样类操作符(常用)
Sample
:输出采样
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Buffer
:输出缓存
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Throttle
:结束连续性计时判断
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
ThrottleFirst
:开始连续性计时判断
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Zip[观察器]
:打包等待
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Timeout
:输出间隔超时判断
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
玖:异常类操作符(常用)
Catch
:捕获异常
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
OnErrorRetry
:当异常出现时执行,然后终止余下操作
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Retry
:当异常出现时跳过,继续余下操作
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
拾:工具类操作符(常用)
Merge
:合并观察器
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Amb
:执行优先观察器
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Aggregate
:迭代器,在迭代完成后输出
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Scan
:迭代器,当每次迭代后输出
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Range[观察器]
:将某范围内的数作为输出来触发
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Repeat
:重复执行输入
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
Concat
:观察器前后相搭
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
SelectMany
:观察器置换器
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
WhenAll
:当所有触发满足条件时,在结束时触发
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
拾壹:UniRX与消息机制
- UniRX的消息机名为
MessageBroker
,使用也是比较简单的。 - 更为方便的是,消息接收者不需要知道消息发送者是谁。
1 | private void Start()//这是Unity某个继承自MonoBehaviour的类的启动方法 |
拾贰:自定义操作符模版
- 暂且记录以下实现的一个小功能:检测是否在编辑器环境的暂停状态,如果不在编辑器内,则直接通过
- 因为大量的UniRX的观察器都不依赖Unity的编辑器状态,致使在编辑器暂停时也能继续运行
- 这个问题会导致不方便测试,所以自制了一个操作符
EditorDebug
来规避这个情况
1 | //这是一个非泛型静态类,专门用来存放扩展方法 |
1 | //以下为特地设置的自定义返回观察器,是直接复制改名了Where操作符的定义 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Iamsleepingnow 世界!