協(xié)程的關(guān)鍵字是 IEnumerator
這是迭代器的意思,那么什么是迭代器?
先請看下面的代碼:
定義一個Test類,實現(xiàn)IEnumerable接口:
class Test : IEnumerable<string>
{
public IEnumerator<string> GetEnumerator()
{
for (int i = 0; i < 5; i++)
{
yield return "test" + i;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
說一下 IEnumerable 和 IEnumerator 的關(guān)系:
- IEnumerable是一個接口,代表其是可枚舉的。
- IEnumerator對象就是一個迭代器(iterator:MoveNext(),Reset(),Current)。
用上面的類,寫一個測試程序:
Test test = new Test();
foreach (string s in test)
{
Console.WriteLine(s);
}
此時輸出結(jié)果為:

可以猜到,輸出的就是 yield return xxx; 后面的xxx。
那么換一種寫法試試:
Test test = new Test();
var e = test.GetEnumerator();
while (e.MoveNext())
{
Console.WriteLine(e.Current);
}
這種寫法就是基于迭代器(iterator)的方式。先來看一下迭代器的定義:
public interface IEnumerator
{
bool MoveNext();
void Reset();
Object Current { get; }
}
從定義可以理解,使用foreach這樣的枚舉操作的時候,枚舉數(shù)被定為在集合的第一個元素前面。
使用迭代器在執(zhí)行迭代,首先會執(zhí)行 MoveNext(), 如果返回true,說明下一個位置有對象,然后此時將Current設(shè)置為下一個對象,這時候的Current就指向了下一個對象;如果返回false,說明下一個位置沒有對象,此時結(jié)束遍歷。
雖然看起來和遍歷數(shù)組一樣,但本質(zhì)上是把其包裝成了一個迭代器執(zhí)行,每次執(zhí)行到y(tǒng)ield的時候,程序就把需要的東西傳遞出去,再停住,不會繼續(xù)往下執(zhí)行。
由于這里yield下面沒有什么其他語句,所以就會依舊 i++ ,繼續(xù)執(zhí)行下去,然后又返回一個值。
那么回到Unity3d中,協(xié)程的用法如下:
void Start()
{
StartCoroutine(DoSomething());
}
IEnumerator DoSomething()
{
DoBefore();
yield return new WaitForSeconds(1f);
DoAfter();
}
這里Unity3d的運行效果,在 Start() 方法開啟協(xié)程后,程序會執(zhí)行 DoBefore() 函數(shù),直到遇見第一個yield,就會暫時把協(xié)程掛起,直到下一幀的Update渲染完成之后再獲取到迭代器當(dāng)前的 Current 對象,此時就是 WaitForSeconds 這個對象。
通過此對象,判斷是否還需要進行等待,再進行 DoAfter() 的回調(diào)。
我們可以寫偽代碼來表述內(nèi)部的邏輯:
void StartCoroutine(IEnumerator e)
{
if (!e.MoveNext())
{
return;
}
var obj = routine.Current;
if (obj is WaitForSeconds)
{
//判斷是否到達時間 如果超過了設(shè)置時間 就繼續(xù)回調(diào)
}
}
在注釋處,會利用現(xiàn)有的定時器機制,注冊定時器,并繼續(xù)進行 DoAfter() 的回調(diào)。