C# --- IEnumerable 和 IEnumerator
- IEnumerable
- IEnumerator
- IEnumerable 和 IEnumerator 的作用
- 手動實現 IEnumerable
- IEnumerable vs. IQueryable
- 為什么有了ienumerator還需要ienumerable
IEnumerable
- 在C#中,
IEnumerable
是一個核心接口,用于表示一個可迭代的集合。它是實現迭代模式的基礎,允許你通過 foreach 循環遍歷集合中的元素- IEnumerable 是一個接口,屬于 System.Collections 命名空間。它的泛型版本 IEnumerable 位于 System.Collections.Generic 中。
- 作用:標記一個對象可以被迭代(即支持 foreach 循環)。
- 核心方法:
GetEnumerator()
,返回一個IEnumerator
對象,用于逐個訪問集合中的元素。
public interface IEnumerable
{IEnumerator GetEnumerator();
}// 泛型版本(更常用):
public interface IEnumerable<out T> : IEnumerable
{IEnumerator<T> GetEnumerator();
}
IEnumerator
- IEnumerator 是實際執行迭代的接口,相當于Java里的
iterator
, 他定義了三個關鍵方法:
- MoveNext():移動到集合的下一個元素,返回 bool 表示是否成功。
- Current:獲取當前元素的值。
- Reset():重置迭代器到初始位置(通常不常用)
public interface IEnumerator
{bool MoveNext();object Current { get; }void Reset();
}// 泛型版本:
public interface IEnumerator<out T> : IEnumerator, IDisposable
{T Current { get; }
}
IEnumerable 和 IEnumerator 的作用
- 統一迭代方式:任何實現 IEnumerable 的類型都可以用
foreach
遍歷。- 支持 LINQ:LINQ 的查詢操作(如 Where、Select)基于 IEnumerable,實現延遲執行。
- 抽象集合類型:允許代碼操作抽象的集合(
IEnumerable<T>
),而非具體類型(如 List),提高靈活性。
手動實現 IEnumerable
using System;
using System.Collections;public class MyCollection : IEnumerable
{private int[] _data = { 1, 2, 3 };public IEnumerator GetEnumerator(){return new MyEnumerator(_data);}
}public class MyEnumerator : IEnumerator
{private int[] _data;private int _index = -1;public MyEnumerator(int[] data){_data = data;}public object Current => _data[_index];public bool MoveNext(){_index++;return _index < _data.Length;}public void Reset(){_index = -1;}
}// 使用:
var collection = new MyCollection();
foreach (var num in collection)
{Console.WriteLine(num); // 輸出 1, 2, 3
}
IEnumerable vs. IQueryable
- IEnumerable:
用于內存中集合(如 List、數組)。
LINQ 操作由 C# 編譯器處理(如 Where 轉換為委托)。
- IQueryable:
用于外部數據源(如數據庫)。
LINQ 操作轉換為表達式樹(如 SQL 查詢),由提供程序解析
為什么有了ienumerator還需要ienumerable
IEnumerable:
- 角色:表示一個集合是“可迭代的”。
核心功能:通過 GetEnumerator() 方法返回一個迭代器(IEnumerator 對象)。- 意義:提供統一的接口,讓所有集合類型(如數組、List、自定義集合)都能被 foreach 遍歷。
IEnumerator:
- 角色:實際控制迭代過程(移動指針、獲取當前元素)。
- 核心功能:通過 MoveNext() 和 Current 實現逐個元素的遍歷。
- 意義:封裝迭代狀態(如當前索引),
確保多次遍歷互不干擾
。
場景假設
- 假設一個集合類直接實現 IEnumerator, 如果兩個 foreach 同時遍歷同一個 BadList 實例,它們的 _index 會互相干擾:
無法支持并發迭代:同一實例的迭代狀態會被覆蓋
public class BadList : IEnumerator<int>
{private int[] _data = { 1, 2, 3 };private int _index = -1;public int Current => _data[_index];object IEnumerator.Current => Current;public bool MoveNext(){_index++;return _index < _data.Length;}public void Reset() => _index = -1;public void Dispose() { }
}
var badList = new BadList();
foreach (var num in badList) { /* 第一次遍歷 */ } // 輸出 1,2,3
foreach (var num in badList) { /* 第二次遍歷 */ } // 無輸出(索引已到末尾)
正確做法:IEnumerable + IEnumerator
public class GoodList : IEnumerable<int>
{private int[] _data = { 1, 2, 3 };public IEnumerator<int> GetEnumerator(){// 每次調用返回一個新的迭代器return new GoodListEnumerator(_data);}// 顯式實現非泛型接口IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}public class GoodListEnumerator : IEnumerator<int>
{private int[] _data;private int _index = -1;public GoodListEnumerator(int[] data) => _data = data;public int Current => _data[_index];object IEnumerator.Current => Current;public bool MoveNext(){_index++;return _index < _data.Length;}public void Reset() => _index = -1;public void Dispose() { }
}
- 狀態隔離:
每次 foreach 會調用 GetEnumerator() 生成新的 IEnumerator 實例,確保每次遍歷獨立。
- 并發安全:多個遍歷操作互不影響。
- 簡化代碼:foreach 自動管理迭代器生命周期(調用 Dispose())。