首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 讓 IDataReader 支援 foreach 迭代的包裝類別實作

     編輯:關於C語言

動機

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) { /* ... */ }

優點總結

  • 語法簡潔,符合 C# 慣用模式。
  • 維持 IDataReader 串流特性,不一次性載入所有資料到記憶體。
  • 可搭配 yield return 實作投影,直接產生強型別實體。
  • 包裝類別實作 IDisposable,確保資源釋放。

注意事項

  • Current 回傳 IDataRecord(或 IDataReader),不可在迴圈外部存取,因下一次 MoveNext 會覆寫內部緩衝區。
  • 若需平行處理或多次迭代,請改用 DataTable 或 List<T> 物質化資料。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved