問題 1.C#疊代器原理 2.Coroutine的傳回類型為IEnumerator,使用yield return傳回,它與疊代器有什麼聯系 3.實作一個Coroutine
1.疊代器的原理: 微軟的官方文檔:疊代器是C#2.0中的新功能,疊代器是方法、get通路器或運算符,使你能在類或結構體中支援foreach疊代,而不必實作整個 IEnumerable接口 ,隻需提供一個疊代器,即可周遊類中的資料結構,當編譯器檢測到疊代器時,它将自動生成IEnumerable或IEnumerable<T>接口的 Current、MoveNext、Dispose 方法。疊代器使用 yield return 依次傳回每個元素, yield break 終止疊代。疊代器的傳回類型必須為 IEunmerable、IEnumerator 、IEnumerable<T>或IEnumerator<T>(前兩個接口與後兩個接口的差別是分别支援非泛型集合與泛型集合) 從中可以了解到: C#1.0集合類的疊代需實作IEnumerable接口,IEnumerable接口:公開枚舉數,該枚舉數支援在非泛型集合進行簡單疊代。它包含一個傳回IEnumerator的GetEnumerator方法。再來看IEnumerator接口:枚舉器,它提供了通過公開循環通路集合能力Current屬性和MoveNext和Reset方法。 在C#1.0中,手動實作簡單的foreach需要的做法如下,拿NGUI的BetterList<T>做實驗: 它是使用的疊代器foreach: T[] buffer;
改為使用IEnumerable<T>接口和IEnumerator<T>的實作方法: 将BetterList<T>繼承IEnumerable<T>接口,此接口又是繼承的IEnumerable接口,它增加了一個傳回IEnumerator<T>的GetEnumerator方法。
新增一個繼承IEnumerator<T>的類,IEnumerator<T>繼承自IEnumerator, IDisposable,同時又新增了T Current屬性。此類提供對泛型集合的循環通路的能力,每次疊代,執行過程如下 a.第一次疊代時遊标position為-1 b.執行MoveNext,遊标位置加1 c.傳回Current,一般來說,Current屬性都是隻讀的,是以這就是為什麼在foreach時,不能修改集合内的item值。( 疊代器模式 :提供一種方法通路一個容器對象中各個元素,而又不暴露該對象的内部細節)。 d.直到記錄的position為疊代集合的長度,MoveNext傳回false,執行Reset方法,将position重新設定為-1
在C#2.0時,通過yield語句簡化疊代,微軟官方對yield的解釋: yield return <expression>; yield break; yield 關鍵字向編譯器訓示它所在的方法是疊代器塊。 編譯器生成一個類 來實作疊代器塊中表示的行為。 在疊代器塊中,yield 關鍵字與 return 關鍵字結合使用,向枚舉器對象提供值。 這是一個傳回值,例如,在 foreach 語句的每一次循環中傳回的值。 yield return ,當疊代器遇到yield return時,會儲存目前位置(也就是上面枚舉器中維護遊标),下次疊代的調用時,将從此位置重新開始執行。将計算的expression的結果以值的形式傳回給疊代器對象。expression必須是可以隐式轉換為疊代器的yield類型。 yield break ,将控制權無條件傳回給疊代器的調用方,該調用方為枚舉器對象的MoveNext方法或Dispose方法。 也就是說,編譯器生成的那個類就是上面寫的那個實作IEnumerator<T>接口的那個類。 2.Coroutine與疊代器 現在知道了StartCoroutine方法的參數是一個IEnumerator類型,那麼在Unity協程中yield return傳回的方法是隐式轉換為疊代器類型,其實就是StartCoroutine對其中的IEnumerator進行MoveNext操作。 但即使如此,那麼Unity如何通過傳回WaitForSeconds實作延時呢,看Unity的繼承關系,StartCoroutine函數傳回一個Coroutine類型,而WaitForSeconds和Coroutine都是繼承自YieldInstruction(是一個空類),WaitForSeconds也沒有特殊的,它隻包含一個seconds屬性,真正的處理是在StartCoroutine中,如果發現其中的IEnumerator是個YieldInstruction類型,那麼就會特殊處理。 這樣如果擴充Coroutine可以通過繼承YieldInstruction類來實作。 自己模仿實作一個簡單Coroutine機制,也比較容易。 3.實作一個Coroutine a.首先寫一個Coroutine的空基類MYieldInstruction
b.寫一個自己的延時類MWaitForSeconds
c.實作自定義的MStartCoroutine方法
d.test,将MCoroutineTest腳本挂在場景中
結果如下,有一點誤差