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

C# 擴充方法實戰 (一):DataTable 與 List<T> 互轉通用元件 (反射/運算式樹兩版)

     編輯:關於C#

動機

專案從 DataSet/DataTable 導向轉型為 POCO/List<T>,頻繁需要雙向轉換。利用 C# 3.0 擴充方法,分別擴充 DataTable 與 IEnumerable<T>,實現 dt.ToList<T>() 與 list.ToDataTable()。

版本一:反射實作 (簡潔、效能可接受)

public static class DataTableExtensions {
    // DataTable → List
    public static List ToList(this DataTable dt) where T : class, new() {
        var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => dt.Columns.Contains(p.Name)).ToArray();

        var list = new List(dt.Rows.Count);
        foreach (DataRow row in dt.Rows) {
            T obj = new T();
            foreach (var p in props) {
                var val = row[p.Name];
                if (val != DBNull.Value) p.SetValue(obj, val);
            }
            list.Add(obj);
        }
        return list;
    }

    // IEnumerable → DataTable
    public static DataTable ToDataTable(this IEnumerable source) where T : class {
        var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToArray();
        var dt = new DataTable();
        foreach (var p in props) dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);

        foreach (T item in source) {
            var row = dt.NewRow();
            foreach (var p in props) row[p.Name] = p.GetValue(item) ?? DBNull.Value;
            dt.Rows.Add(row);
        }
        return dt;
    }
}

版本二:運算式樹編譯委派 (高效能、適合熱路徑)

public static class FastConverter {
    // 快取編譯後的轉換委派
    static readonly ConcurrentDictionary> _rowToObjCache = new();
    static readonly ConcurrentDictionary> _objToRowCache = new();

    public static List ToListFast(this DataTable dt) where T : class, new() {
        var converter = (Func)_rowToObjCache.GetOrAdd(typeof(T), t => {
            var rowParam = Expression.Parameter(typeof(DataRow), "row");
            var newExpr = Expression.New(t);
            var bindings = t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => dt.Columns.Contains(p.Name))
                .Select(p => Expression.Bind(p, Expression.Convert(
                    Expression.Property(rowParam, "Item", Expression.Constant(p.Name)), p.PropertyType)));
            var memberInit = Expression.MemberInit(newExpr, bindings);
            var lambda = Expression.Lambda>(memberInit, rowParam);
            return lambda.Compile();
        });

        return dt.Rows.Cast().Select(converter).ToList();
    }

    public static DataTable ToDataTableFast(this IEnumerable source) where T : class {
        var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToArray();
        var dt = new DataTable();
        foreach (var p in props) dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);

        var converter = _objToRowCache.GetOrAdd(typeof(T), t => {
            var objParam = Expression.Parameter(typeof(object), "obj");
            var rowParam = Expression.Parameter(typeof(DataRow), "row");
            var assigns = props.Select(p => Expression.Assign(
                Expression.Property(rowParam, "Item", Expression.Constant(p.Name)),
                Expression.Convert(Expression.Property(Expression.Convert(objParam, t), p.Name), typeof(object))));
            var block = Expression.Block(assigns);
            return Expression.Lambda>(block, objParam, rowParam).Compile();
        });

        foreach (var item in source) {
            var row = dt.NewRow();
            converter(item, row);
            dt.Rows.Add(row);
        }
        return dt;
    }
}

效能比較 (10 萬筆、10 欄位測試)

方案DataTable→List (ms)List→DataTable (ms)
反射版~1200~900
運算式樹版~180~150

運算式樹版首次呼叫支付編譯成本,後續呼叫極快,適合高頻轉換場景。

使用建議

  • 一般業務邏輯、低頻轉換:直接用反射版,程式碼可讀性高、維護容易。
  • 批次匯入/匯出、ETL、即時資料流:採用運算式樹版或成熟庫 (如 FastMember, AutoMapper)。
  • 注意 DBNull.Value 處理、Nullable 型別欄位型別對應、大型物件圖遞迴轉換深度。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved