通过foreach,我们可以遍历List元素。
其中的原理便是使用了迭代器,迭代器里用current定义到当前元素,MoveNext()转移到下一元素并返回bool值
List<int> ages=new List<int>();
for (int i = 0; i < 5; i++)
{
ages.Add(i);
}
IEnumerator ie = ages.GetEnumerator();
while (ie.MoveNext())
{
print(ie.Current);
}
迭代器的接口是IEnumerator,IEnumerator接口的定义如下:
using System.Runtime.InteropServices;
namespace System.Collections
{
[ComVisible(true)]
[Guid("496B0ABF-CDEE-11D3-88E8-00902754C43A")]
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
}
成员说明:
Current 遍历当前类型时,存储当前元素。
MoveNext() 每调用一次,移动到下一个元素,返回下一个元素是否为空
Reset() 重置到列表最开始
枚举器就是实现IEnumerator接口,通过MoveNext()获取下一个元素来遍历每个元素的方法。
下方为List里面的枚举器Enumerator代码
public struct Enumerator : IEnumerator<T>, IEnumerator, IDisposable
{
public T Current { get; }
public void Dispose();
public bool MoveNext();
}
我们也可以自己定义返回的IEnumerator
public IEnumerator GetEnumerator()
{
print(1);
yield return 1;
print(2);
yield return 2;
print(3);
yield return "枚举器";
}
包含yield语句的方法或属性称为迭代块
而yield return则是实现了IEnumerator接口和IDisposable接口的属性和方法,相当于迭代器的实例。
当以后每次迭代器实例调用MoveNext()时,将逐一实现迭代块里的行为。比如上方就有3个迭代块。
每次MoveNext()方法后,改变当前迭代位置为下一个元素位置。
public class update2Coroutine : MonoBehaviour {
Update2CoroutineTest update2CoroutineTest = new Update2CoroutineTest();
IEnumerator e;
public update2Coroutine()
{
e = update2CoroutineTest.GetEnumerator();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (e.MoveNext())
{
}
}
}
public class Update2CoroutineTest
{
public IEnumerator GetEnumerator()
{
Debug.Log("协程:"+1);
yield return 0;
Debug.Log("协程:" + 2);
yield return 0;
Debug.Log("协程:" + "枚举器");
yield return 0;
}
}
每隔一段时间输出为:
协程:1
协程:2
协程:枚举器
void Start()
{
StartCoroutine(TestIt());
}
IEnumerator TestIt()
{
print("开始走路");
yield return 0;
print("走进商店");
yield return 0;
print("开始购物");
yield return 0;
//yield return null;
}
从而实现了某段代码的等待,一步步来,而不是全部输出。
从上面可以看出 ,在update()方法模拟协程和使用unity自带StartCoroutine()方法启动协程效果差不多。
但是唯一确定的一点就是unity也是通过枚举一步步运行程序块的。类似update()模拟的协程,每次遇到yield return ,就执行yield 类型的
MoveNext()方法,改变当前迭代位置为下一个元素位置。等待下一次MoveNext()方法调用。StartCoroutine()方法会不停的调用MoveNext()方法方法(这样就类似于foreach)。直到枚举结束。
比如下面这段代码,便相当于自己写了一个Update语句
IEnumerator MyEnumerator()
{
while (true)
{
MyUpdate();
yield return 0;
}
}
void MyUpdate()
{
i++;
print(i);
}
// Update is called once per frame
void Update()
{
// i++;
// print("Update:"+i);
}
值得注意的是,yield return 后面跟的值除了unity自带的类(如:new WaitForSeconds(0.2f)、YieldInstruction和协程语句块(返回值为 IEnumerator的方法 ) ,其他值没有意义(yield return 0和yield return null其实都是一样的,只是遇到yield return就做相同处理,不会去处理后面跟的值了)。
yield return new WaitForSeconds(1f);就能让协程每隔一秒再执行。
对比协程和Update的等待时间:
你会发现不管你设置的协程时间间隔多小(前提小于update的时间间隔),打印的时间和update的时间非常接近,
这说明你的协程时间间隔最小就是update的时间间隔。不可能再短了。即使你设置的比update时间间隔小。协程也只会执行update 的时间间隔了。
正好也验证了上面所说的:
在update()方法模拟协程和使用unity自带StartCoroutine()方法启动协程效果差不多。看来unity实现的StartCoroutine()启动协程和我们 update()模拟是一样的。但是也不确定到底是不是通过类似update()方法实现的。