foreach
foreach無需要知道集合中元素個數就可以迭代集合中的元素它其實是迭代器模式的一個包裝就語言層面來說是while的另一種形式
using System;
using SystemCollections;
public class People : IEnumerable//版本{ string[] names; public People(string[] names) { thisnames = names; } public IEnumerator GetEnumerator() { return new Enumerator(thisnames); } private class Enumerator : IEnumerator { private int state = ; string[] names; internal Enumerator(string[] names) { thisnames = names; } public bool MoveNext() { state++; return (state < namesLength); } public void Reset() { state = ; } public object Current { get { try { return names[state]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } }}public class Test{ public static void Main() { People people = new People(new string[]{aaabbbcccddd}); foreach (var person in people) { ConsoleWriteLine(person); } IEnumerator Enumerator = peopleGetEnumerator(); while (EnumeratorMoveNext()) { ConsoleWriteLine(EnumeratorCurrent); } }}查看IL代碼先看foreach代碼塊的IL代碼
IL_f: callvirt instance class [mscorlib]SystemCollectionsIEnumerator People::GetEnumerator()
IL_: stlocs CS$$
try
{
IL_: brs IL_
IL_: ldlocs CS$$
IL_a: callvirt instance object [mscorlib]SystemCollectionsIEnumerator::get_Current()
IL_f: stloc
IL_: ldloc
IL_: call void [mscorlib]SystemConsole::WriteLine(object)
IL_: ldlocs CS$$
IL_: callvirt instance bool [mscorlib]SystemCollectionsIEnumerator::MoveNext()
IL_d: brtrues IL_
IL_f: leaves IL_
} // end try
finally
{
IL_: ldlocs CS$$
IL_: isinst [mscorlib]SystemIDisposable
IL_: stlocs CS$$
IL_a: ldlocs CS$$
IL_c: brfalses IL_
IL_e: ldlocs CS$$
IL_: callvirt instance void [mscorlib]SystemIDisposable::Dispose()
IL_: endfinally
} // end handler再看while代碼塊的IL代碼
IL_: callvirt instance class [mscorlib]SystemCollectionsIEnumerator People::GetEnumerator()
IL_: stloc
IL_: brs IL_
IL_: ldloc
IL_a: callvirt instance object [mscorlib]SystemCollectionsIEnumerator::get_Current()
IL_f: call void [mscorlib]SystemConsole::WriteLine(object)
IL_: ldloc
IL_: callvirt instance bool [mscorlib]SystemCollectionsIEnumerator::MoveNext()
IL_a: brtrues IL_可以看出foreach語句塊IL代碼與while語句塊的IL代碼相比只多了finally部分的資源處理但循環部分的代碼基本沒有任何分別
如果讓private class Enumerator 增加IDisposable接口將while語句塊改寫為如下
using((IDisposable)Enumerator)
{
while (EnumeratorMoveNext())
{
ConsoleWriteLine(EnumeratorCurrent);
}
}得到的IL代碼將與foreach基本沒有區別
由此可見foreach是編譯器給我們的一個語法糖它包裝了while
yield
在上面的代碼中枚舉器private class Enumerator 是自己實現了IEnumerator接口中的方法使用yield語句可以讓編譯器自動實現省去了自己寫的麻煩(包含yield語句的方法或屬性必須聲明為返回IEnumerable 或IEnumerator )所以上面的public class People 可以這樣寫
public class People : IEnumerable//版本
{
string[] names;
public People(string[] names)
{
thisnames = names;
}
public IEnumerator GetEnumerator()
{
for (int i = ; i < namesLength; i++)
{
yield return names[i];
}
}
}
反編譯之後看IL代碼
這是版本people類
這是版本的people類
自己寫的迭代器類Enumerator和編譯器生成的迭代器類<GetEnumerator>d_相比較<GetEnumerator>d_增加了IDisposable接口另外就是增加了IEnumerator接口的泛型版本的繼承如果願意Enumerator也可以實現這兩個接口
用yield還可以返回IEnumerable接口類型
using System;
using SystemCollections;
public class People
{
string[] names;
public People(string[] names)
{
thisnames = names;
}
public IEnumerable PeopleEven()
{
for (int i = ; i < namesLength; i += )
{
yield return names[i];
}
}
}
public class Test
{
public static void Main()
{
People people = new People(new string[] { aaa bbb ccc ddd });
foreach (var person in peoplePeopleEven())
{
ConsoleWriteLine(person);
}
}
}
該PeopleEven()返回的IEnumerable類<PeopleEven>d_會自動實現GetEnumerator()方法和IEnumerator接口中的方法看IL代碼
PeopleEven()返回的類<PeopleEven>d_實現了IEnumerable和IEnumerator接口以及它們的泛型版本還實現了IDisposable接口因此可以將這個接口都實現將類改寫
using System;
using SystemCollections;
public class People : IEnumerable IEnumerator IDisposable
{
string[] names = { bbbb dddd };
int state = ;
public IEnumerator GetEnumerator()
{
return new People();
}
public bool MoveNext()
{
state++;
return (state < namesLength);
}
public void Reset()
{
state = ;
}
public object Current
{
get
{
try
{
return names[state];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Dispose()
{
}
}
public class Test
{
public static void Main()
{
People people = new People();
foreach (var person in people)
{
ConsoleWriteLine(person);
}
}
}
看IL代碼
類People與PeopleEven()返回的類<PeopleEven>d_基本相同只是少了兩個接口的泛型版本的實現
由此可見yield是一個更大的語法糖它可以自動實現IEnumerable和IEnumberator接口中的方法自動替我們實現迭代器
用foreach和yield可以非常方便地使用迭代模式
From:http://tw.wingwit.com/Article/program/net/201311/11643.html