Unity3d C#:从迭代器到协程

进程、线程、协程是几个很基础且重要的知识点。而在C#中要理解协程得先理解一下迭代器。接下来从一个初学者的角度浅要分析(如有误还恳请指出)。

一、C#当中的迭代器

先简单说明一下迭代器。迭代器通过去继承和实现IEnumerable跟IEnumerator这两接口,接下来便可以为foreach所使用。

整体可参考:C#迭代器 - 知乎

接下来从应用的角度介绍几种能够实现迭代器的办法。

1.方法一:标准迭代器的实现方法

这是最基础的方法,但是通过这种方法能很好滴能清楚背后的原理。

该部分及方法二参考自:C# 迭代器_Go_Accepted的博客-CSDN博客_c#迭代器

class CustomList : IEnumerable, IEnumerator {//继承自这两个接口
    private int[] list;
 
    // 从-1开始的光标用于表示数据得到了哪个位置
    private int position = -1;  
 
    public CustomList() {
        list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    }
        
 
    #region IEnumerable
    public IEnumerator GetEnumerator() {//实现IEnumerable接口中的GetEnumerator函数
        Reset();
        return this;
    }
    #endregion
 
    #region IEnumerator
    public object Current {//实现IEnumerator接口中的Current 函数
        get {
            return list[position];
        }
    }
 
    public bool MoveNext() {//实现IEnumerator接口中的MoveNext函数
        // 移动光标判断是否是否溢出,溢出则不合法
        return ++position < list.Length;
    }
 
    // reset是重置光标位置,一般写在获取 IEnumerator 对象的函数中
    // 用于第一次重置光标位置
    public void Reset() {//实现IEnumerator接口中的Reset函数
        position = -1;            
    }
    #endregion
}

调用:

CustomList list = new CustomList();
 
// foreach执行过程:
// 1、先获取 in 后面这个对象的 IEnumerator
//    会调用对象其中的 GetEnumerator 方法来获取
//   (只要有这个方法就可以,即使没有继承IEnumerable接口)
// 2、执行得到的这个 IEnumerator 对象中的 MoveNext 方法
// 3、只要这个 MoveNext 方法的返回值是true,就会去得到 Current
//    然后赋值给 item 
foreach (int item in list) {
    Console.WriteLine(item);
}

简要来说就是这个foreach会循环调用MoveNext函数跟Current函数,并不断将Current函数的返回值赋予item,直至MoveNext函数的返回为false。如此一来便实现了对CustomList的遍历。

2.方法二:用yield return

class CustomList2 : IEnumerable {//只继承IEnumerable接口
    private int[] list;
 
    public CustomList2() {
        list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    }
 
    public IEnumerator GetEnumerator() {//但是实现了IEnumerator接口类型的函数
        for(int i = 0; i < list.Length; ++i) {
            // yield 关键字配合迭代器使用
            // 可以理解为暂时返回,保持当前的状态
            yield return list[i];
        }
    }
}

调用:

​
CustomList list = new CustomList();
 
foreach (int item in list) {
    Console.WriteLine(item);
}

采用yield return可以大大简化迭代器的编写效率,同时也让程序变得更好理解。foreach中不断返回的即是GetEnumerator函数中yield return的值。而yield return本质应该是对返回类型做了一个隐式强转,将返回值强转为IEnumerator类型。

3.方法三:在函数中使用yield return

该部分参考自:C#迭代器方法介绍

class Program
{
    public static IEnumerable<int> Fibs()
    {
        int f1 = 1, f2 = 2;
        while (true)
        {
            yield return f1;
            yield return f2;
            f1 += f2;
            f2 += f1;
        }
    }
    static void Main(string[] args)
    {
        foreach (int i in Fibs())
            if (i < 20)
                Console.WriteLine("{0}", i);
 
            else
                break;
        Console.ReadKey();
    } 
}

这种方法就更简单巧妙了,是直接在函数中实现迭代器,这种办法应该包含了一个隐式的强转,比较适合用在一些相对简单独立的模块。

4.进一步的深入

这就不多讲了,可参考官方文档:迭代器 | Microsoft Docs

这里主要还包含了:异步迭代器、泛型迭代器、foreach背后的原理、yield return与return。


二、Unity3d当中的协程

以下内容大多参考自Unity 协程(Coroutine)原理与用法详解_心之凌儿的博客-CSDN博客_unity 携程

1.协程的基本特征

该部分主要参考自:Unity 协程的原理_画个小圆儿的博客-CSDN博客_unity协程原理

1)进程有自己独立的堆和栈,即不共享堆也不共享栈,进程由操作系统调度

2)线程拥有自己独立的栈和共享的堆,共享堆不共享栈,线程亦有操作系统调度(标准线程是这样的)

3)协程和线程一样共享堆不共享栈,协程由程序员在协程的代码里面显示调度。并且协程的切换成本远远低于线程的切换成本。这个可参考:协程(coroutine)简介 - DoubleLi - 博客园

4)一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行的,在线程里面可以开启协程,让程序在特定的时间内运行。

5)协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失了标准线程使用多CPU的能力。(即协程并不是真的并行,其无法像线程一样并行去使用CPU的多核)

6)unity中的协程可以访问unity当中的对象,而线程是不可以的。

2.协程当中的yield

不同的yield 的方法处于生命周期的不同位置,可参考:

在这里插入图片描述

 简要介绍就是不同的yield返回值,其调用的时机为上图所示,这样讲不太好理解,举个例子:

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
 
public class Test : MonoBehaviour {
 
    private int num = 0;
 
    void Start()
    {
        StartCoroutine(Test1());
    }
 
    IEnumerator Test1()
    {
        while (num < 30)
        {
            num++;
            Debug.Log(num);
            yield return null;
            Debug.Log("-------------" + num);
        }
    }
 
    void Update()
    {
        Debug.Log("update" + num);
    }
    void LateUpdate()
    {
        Debug.Log("lateUpdate!" + num);
    }
}
  • yield return null; 暂停协程等待下一帧继续执行

  • yield return 0或其他数字; 暂停协程等待下一帧继续执行

 (备注:调试生命周期的时候一定记得把Collapse(折叠)这一栏给点开)

3.协程的迭代器本质

参考自:Unity协程与迭代器_whr12的博客-CSDN博客_unity迭代器

Unity协程(Coroutine)之yield和迭代原理分析-腾讯游戏学堂

用C# 模拟实现unity里的协程 - 吃斤欢乐豆 - 博客园

1)根据上面的分析和理解呢。协程其实就是一个IEnumerator(迭代器),IEnumerator 接口有两个方法 Current 和 MoveNext() ,只有当MoveNext()返回 true时才可以访问 Current,否则会报错。迭代器方法运行到 yield return 语句时,会返回一个expression表达式并保留当前在代码中的位置。 当下次调用迭代器函数时执行从该位置重新启动。Unity在每帧做的工作就是:调用协程(迭代器)MoveNext() 方法,如果返回 true ,就从当前位置继续往下执行。

2)协程跟Update()其实一样的,都是Unity每帧对会去处理的函数(如果有的话)。如果MonoBehaviour 是处于激活(active)状态的而且yield的条件满足(一定要注意是激活状态,否则会报错),就会协程方法的后面代码。

3)而比较特殊的就是unity中的协程返回的迭代器会根据返回的类型分配到不同的生命周期。尤其是上面的的用C# 模拟实现unity里的协程这篇博客,分析的相当不错

4.协程的应用场景

将一个复杂程序分帧执行、计时器、结合线程进行异步加载。但是要注意不要给协程放太大的运算预防将主线程给弄崩溃了。

其他可参考:Unity3D协程介绍 以及 使用_huang9012的博客-CSDN博客_unity协程

Unity(协程是什么,怎么用)_诡白丷的博客-CSDN博客_unity 携程

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值