今天要講的是列舉器,我們很常用到foreach
來幫我們把陣列裡面的東西全部拿出來。那麼,為什麼陣列可以使用 foreach ,我們操作其他物件到底能不能使用 foreach ?
答案是因為,陣列有實作列舉器,所以我們可以使用 foreach 把陣列裡面的東西全部拿出來。簡而言之,若我們的自訂義的類別有實作列舉器。那就可以使用 foreach 來取出裡面的東西。
class Program
{
static void Main(string[] args)
{
string[] 陣列 = { "小明", "小華", "小花" };
foreach(var item in 陣列)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
輸出結果:
小明
小華
小花
列舉器 enumerator
列舉器會將陣列裡面的東西一個一個的依照順序取出來,不過 C# 裡面陣列是一個有指定大小的容器,定義陣列的大小後,裡面能放東西的數量就是固定的。所以廣泛來說,列舉器會把序列(不限定長度大小)裡面的東西一個一個取出來。
列舉器的實作方式也非常簡單,要做三個功能,1.拿出一個,2.判斷有沒有下一個,3.重頭開始(從零開始)
在C#中有定義列舉器的interface IEnumerator,所以我們說陣列裡面有實作enumerator,就是擁有這三個功能
public interface IEnumerator
{
bool MoveNext();
Object Current { get; }
void Reset();
}
可列舉的enumerable
至於要怎麼知道這個物件有沒有列舉器,能不能使用foreach,就要看這個類別是不是可列舉的類別,類別有沒有繼承IEnumerable
這介面。(補充:C#習慣在介面前面加上i,所以IEnumerable
就是enumerable
的interface,指的是可列舉的介面。)
如果有繼承IEnumerable,就代表有實作GetEnumerator()
這個方法來讓我們拿到列舉器。
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
所以剛剛的 foreach 程式可以改成以下使用列舉器的版本
class Program
{
static void Main(string[] args)
{
string[] 陣列 = { "小明", "小華", "小花" };
var 列舉器 = 陣列.GetEnumerator();
while(列舉器.MoveNext())
{
Console.WriteLine(列舉器.Current);
}
Console.ReadKey();
}
}
執行結果當然與剛剛的一樣
小明
小華
小花
讓自定義類別可列舉
前面講到繼承IEnumerable
介面的話,就要實作GetEnumerator()
來回傳列舉器。所以如果你的自定義類別裡面有個集合或陣列想要列舉出來,就可以實作GetEnumerator()把陣列的列舉器return出去。
例如我定義一個班級類別,實作GetEnumerator方法,裡面是回傳陣列的GetEnumerator()
class 班級 : IEnumerable
{
public string [] 學生們 = { "小明", "小華", "小花" };
public IEnumerator GetEnumerator()
{
return 學生們.GetEnumerator();
}
}
一般情況來說,我們會以這樣的方式來foreach取出學生
班級 A班 = new 班級();
foreach (var 學生 in A班.學生們)
{
Console.WriteLine(學生);
}
但是我們班級
這個類別實作了 GetEnumerator(),就可以直接對班級
跑迴圈取出學生
的資料
班級 A班 = new 班級();
foreach (var 學生 in A班)
{
Console.WriteLine(學生);
}
Console.Read();