IEnumerable → GetEnumerator() → IEnumerator
│ ├── Current (T)
│ ├── MoveNext() (bool)
│ └── Dispose()
└── IEnumerable (非泛型) 為相容性保留
public class ListBoxTest : IEnumerable
{
private string[] _strings;
private int _count;
public ListBoxTest(string str1, string str2)
{
_strings = new string[8];
_strings[0] = str1;
_strings[1] = str2;
_count = 2;
}
public void Add(string s)
{
if (_count < _strings.Length)
_strings[_count++] = s;
}
// 泛型介面實作
public IEnumerator GetEnumerator()
{
for (int i = 0; i < _count; i++)
yield return _strings[i]; // 編譯器自動生成狀態機
}
// 非泛型介面顯式實作
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
// 原始碼
foreach (string s in listBox)
Console.WriteLine(s);
// 降級後 (簡化)
var enumerator = listBox.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
string s = enumerator.Current;
Console.WriteLine(s);
}
}
finally
{
(enumerator as IDisposable)?.Dispose();
}
IEnumerator 類別
public class ListBoxEnumerator : IEnumerator
{
private readonly ListBoxTest _collection;
private int _index = -1;
public ListBoxEnumerator(ListBoxTest collection) => _collection = collection;
public string Current => _index >= 0 && _index < _collection.Count
? _collection[_index]
: throw new InvalidOperationException();
object IEnumerator.Current => Current;
public bool MoveNext() => ++_index < _collection.Count;
public void Reset() => _index = -1;
public void Dispose() { }
}
IEnumerable<T>yield return 而非手寫列舉器IEnumerable.GetEnumerator() 避免命名衝突Current 在 MoveNext() 返回 false 前不可存取IDisposable 釋放非託管資源(檔案、資料庫連線等)
// .NET 4.5+ 建議暴露
public IReadOnlyList Items => _list.AsReadOnly();
// 或
public IReadOnlyCollection Items => _list;
防止呼叫端修改內部集合,語意更清晰。