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