协程与迭代器
自己实现unity的协程功能_c#实现类似unity的协程-CSDN博客
前天和同事聊天,聊起来协程,聊起原理,什么迭代器,什么MoveNext
几句话带过之后就算完了,事后再次想起,发现自己已经忘了具体细节,于是也打算写成博客,供自己以后回应
一句话概括
(yield外部的)(会运行的)代码行,会被放到MoveNext()中
(写在yield return后面的类或者参数)会变成Current,Update每帧去调Current(判断是否能MoveNext),倘若返回了false,就不做任何事,否则就MoveNext()+新的初始化
具体代码+注释
查看代码
using System; using System.Collections; using System.Collections.Generic; using System.Threading; internal class Program { static void Main(string[] args) { MyMonoBehaviour objMyMonoBehaviour = new MyMonoBehaviour(); Console.WriteLine("Create MyMonoBehaviour" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); objMyMonoBehaviour.StartCoroutine(CoroutineDetail()); while (true) { objMyMonoBehaviour.Update(); Thread.Sleep(100); } } static IEnumerator CoroutineDetail() { Console.WriteLine("yield return null start:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); yield return null; Console.WriteLine("yield return null end:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); Console.WriteLine("wait 1.0 seconds start:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); yield return new MyWaitForSeconds(1.0f); Console.WriteLine("wait 1.0 seconds end:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); Console.WriteLine("wait 2.0 seconds start:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); yield return new MyWaitForSeconds(2.0f); Console.WriteLine("wait 2.0 seconds end:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); } } public class MyMonoBehaviour { public class RoutineInfo { //我的迭代器 public IEnumerator routine; //迭代器当前需要比较的对象 public MyYieldInstruction current; public bool IsCanMoveNext() { return current == null || current.IsCanMoveNext(); } } //迭代器数据类List List<RoutineInfo> lstRoutine = new List<RoutineInfo>(); public void StartCoroutine(IEnumerator routine) { //如果是空迭代器或者只能迭代一次的,直接返回 if (routine == null || !routine.MoveNext()) return; //新建迭代数据类,管理该迭代器 RoutineInfo objRoutineInfo = new RoutineInfo(); lstRoutine.Add(objRoutineInfo); //初始化迭代数据类 objRoutineInfo.routine = routine; SetRoutineInfo(ref objRoutineInfo); } //设置目标迭代器当前的迭代参数 public void SetRoutineInfo(ref RoutineInfo objRoutineInfo) { //yield后面不是new了一个类嘛,存到迭代器的Current里了,要拿到类,就在这设置一下 objRoutineInfo.current = objRoutineInfo.routine.Current as MyYieldInstruction; } public void Update() { //从后往前遍历,便于lstRoutine.RemoveAt(i) for (int i = lstRoutine.Count - 1; i >= 0; i--) { RoutineInfo item = lstRoutine[i]; if (item == null) continue; if (!item.IsCanMoveNext()) continue; if (item.routine.MoveNext()) SetRoutineInfo(ref item); else lstRoutine.RemoveAt(i);//清除迭代完的迭代器 } } } //抽象类+抽象方法,有其他类型的迭代器就继承这个,判断条件由自己去实现 public abstract class MyYieldInstruction { public abstract bool IsCanMoveNext(); } public class MyWaitForSeconds : MyYieldInstruction { //在yield return时,记录等待时间,并用于后续的每次比较 public float seconds; private DateTime beginTime; public MyWaitForSeconds(float seconds) { this.seconds = seconds; beginTime = DateTime.Now; } //MyWaitForSeconds的比较就是 //1.在初始化时记录开始时间和等待时间 //2.使用当前时间减去开始时间,得到时间差 //3.使用时间差去和等待时间比较,如果时间差>等待时间,就表示可以MoveNext public override bool IsCanMoveNext() { TimeSpan deltaSeconds = DateTime.Now - beginTime; bool res = deltaSeconds.TotalSeconds > seconds; Console.WriteLine("MyWaitForSeconds类内部比较一次,结果为" + res); return res; } }
输出:
一个比较好的数据loop办法:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!