IDataReader 提供唯讀、順向的資料流存取,但未實作 IEnumerable,無法直接使用 foreach。本文示範建立一個實作 IEnumerable 的包裝類別,將 IDataReader 轉為可迭代物件,簡化程式碼並避免產生大量結構體物件佔用記憶體。
using System;
using System.Collections;
using System.Data;
public class DataReaderEnumerable : IEnumerable
{
private readonly IDataReader _reader;
private bool _disposed;
public DataReaderEnumerable(IDataReader reader)
{
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
}
public IEnumerator GetEnumerator()
{
if (_disposed) throw new ObjectDisposedException(nameof(DataReaderEnumerable));
return new DataReaderEnumerator(_reader);
}
private class DataReaderEnumerator : IEnumerator
{
private readonly IDataReader _reader;
public DataReaderEnumerator(IDataReader reader) => _reader = reader;
public object Current => _reader; // 回傳 DataReader 本身,呼叫端可直接存取欄位
public bool MoveNext() => _reader.Read();
public void Reset() => throw new NotSupportedException("DataReader 唯讀順向,不支援 Reset。");
public void Dispose() { /* 不關閉 Reader,由外部控制 */ }
}
public void Dispose()
{
if (!_disposed)
{
_reader?.Dispose();
_disposed = true;
}
}
}
using (var conn = new OleDbConnection(connStr))
using (var cmd = new OleDbCommand("SELECT Id, Name FROM Users", conn))
{
conn.Open();
using (var reader = cmd.ExecuteReader())
{
// 包裝後直接 foreach
foreach (IDataRecord row in new DataReaderEnumerable(reader))
{
Console.WriteLine($"{row["Id"]} - {row["Name"]}");
}
}
}
public class DataReaderEnumerable : IEnumerable
{
private readonly IDataReader _reader;
private readonly Func _projector;
public DataReaderEnumerable(IDataReader reader, Func projector)
{
_reader = reader;
_projector = projector;
}
public IEnumerator GetEnumerator()
{
while (_reader.Read())
yield return _projector(_reader);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
// 使用
var users = new DataReaderEnumerable(reader, r => new User
{
Id = r.GetInt32(0),
Name = r.GetString(1)
});
foreach (var u in users) { /* ... */ }
IDataReader 串流特性,不一次性載入所有資料到記憶體。yield return 實作投影,直接產生強型別實體。IDisposable,確保資源釋放。Current 回傳 IDataRecord(或 IDataReader),不可在迴圈外部存取,因下一次 MoveNext 會覆寫內部緩衝區。DataTable 或 List<T> 物質化資料。