從foreach 到yield 來聊聊協程

C#實現foreach  其實是通過對一個集合不斷的迭代輸出的過程 。  它需要滿足的條件是

 

 1 foreach的對象有 GetEnumarator方法 來返回迭代對象的集合

 2 需要有一個類繼承IEnumerator接口。該類有一個集合保存了所有需要迭代的對象,並且該類中必須有幾個方法:返回一個是否還有需要迭代的對象的bool MoveNext()方法,重新設置迭代索引的 Reset()方法,還有返回當前迭代對象的IEnumerator方法。以List的底層實現爲例子

 

下面我們自己實現一個功能來鞏固一下:

  public class IteratorTarget 
    {
        public IteratorTarget(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }


        public string firstName;
        public string lastName;

    }

 

public class ForeachObject
    {
        private IteratorTarget[] iteratorTargetArray;
        public ForeachObject(IteratorTarget[] pArray)
        {
            iteratorTargetArray = new IteratorTarget[pArray.Length];


            for (int i = 0; i < pArray.Length; i++)
            {
                iteratorTargetArray[i] = pArray[i];
            }
        }

      public IteratorObject GetEnumerator()
        {
            return new IteratorObject(iteratorTargetArray);
        }

    }

 

 // When you implement IEnumerable, you must also implement IEnumerator.

    public class IteratorObject : IEnumerator
    {
        public IteratorTarget[] IteratorTargets;


        // Enumerators are positioned before the first element
        // until the first MoveNext() call.
        int position = -1;


        public IteratorObject(IteratorTarget[] list)
        {
            IteratorTargets = list;
        }


        public bool MoveNext()
        {
            position++;
            return (position < IteratorTargets.Length);
        }


        public void Reset()
        {
            position = -1;
        }


        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }


        public IteratorTarget Current
        {
            get
            {
                try
                {
                    return IteratorTargets[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }

    }

上面我們定義了三個類一個迭代的對象IteratorTarget ,一個 foreach的對象 ForeachObject 最後一個 用來繼承IEnumerator的類IteratorObject。下面

     static void Main(string[] args)
        {
            IteratorTarget[] IteratorTargetArray = new IteratorTarget[3]
            {
                 new IteratorTarget("John", "Smith"),
                 new IteratorTarget("Jim", "Johnson"),
                 new IteratorTarget("Sue", "Rabon"),
            };
            ForeachObject foreachObject= new ForeachObject(IteratorTargetArray);
                 foreach (IteratorTarget p in foreachObject)
                     Console.WriteLine(p.firstName + " " + p.lastName);
                 Console.Read();

 

輸出了

 John Smith

 Jim Johnson

 Sue Rabon

自定義的foreach輸出成功!!!

下面該聊聊yield了 先上一個用 yield實現同上面一樣功能

 public class Test
    {
        static IteratorTarget[] iteratorTargetArray = new IteratorTarget[3]
        {
                new IteratorTarget("John", "Smith"),
                new IteratorTarget("Jim", "Johnson"),
                new IteratorTarget("Sue", "Rabon"),
        };
            public IEnumerator<IteratorTarget> GetEnumerator()
        {
            foreach (var item in iteratorTargetArray)
            {
                yield return item;
            }
        }

    }

 Main方法變成

  static void Main(string[] args)
        {
              Test test = new Test();
              foreach (var item in test)
              {
                  Console.WriteLine(item.firstName + " " + item.lastName);
              }
              Console.Read();
       }

同樣輸出了

 

 John Smith

 Jim Johnson

 Sue Rabon

到這裏我們應該明白了,其實yield就相當於爲我們省去了 創建上面第二個條件的繼承自IEnumarator的類。yield的作用就是不斷的執行 MoveNetxt(). GetCurrent() 等方法來一步步迭代,其實上面的Test類我們還可以這樣寫

public class Test
    {
        public IEnumerator<IteratorTarget> GetEnumerator()
        {
            yield return new IteratorTarget("John", "Smith");
            yield return new IteratorTarget"Jim", "Johnson");
            yield return new  IteratorTarget("Sue", "Rabon"),
        }

}

瞭解了 Yield的具體用法後也就不難理解unity 中協程的作用了,當unity開始一段協程時候,StartCroutine(SomeFunc) SomeFunc是一個返回值爲IEnumarator的方法。當迭代器方法運行到 yield return 語句時,會返回一個expression表達式並保留當前在代碼中的位置。 當下次調用迭代器函數時執行從該位置重新啓動。

        Unity在每幀的Update之後執行協程,做的工作就是:調用 協程(迭代器)MoveNext() 方法,如果返回 true ,就從當前位置繼續往下執行從而每幀迭代下去。(插播 unity獲得wake start update 協程等方法時候是通過在編譯階段可以是ilcpp或者mono把updae wake 等方法放到不同的list裏面然後再運行的時候依次執行,而不是通過運行時候反射拿到 。具體如下

當第一次訪問給定類型的單行爲時,將通過腳本運行時(Mono或IL2CPP)檢查底層腳本是否定義了任何神奇的方法(指的是wake update 等方法),並緩存這些信息。如果一個單值行爲有一個特定的方法,它將被添加到一個適當的列表中,例如,如果一個腳本定義了更新方法,那麼它將被添加到一個腳本列表中,這些腳本需要在每一幀中進行更新。在遊戲中,Unity只是遍歷這些列表並執行它的方法)

另外加上我實現的 unity中的協程的 yield return null 功能 。

地址https://github.com/paridas0813/Unity_My_Crotine

Reference: http://www.cnblogs.com/zhaopei/p/5769782.html 

                    http://dsqiu.iteye.com/blog/2029701

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章