且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

C#如何将IEnumerable匿名列表转换为数据表

更新时间:2022-11-27 08:23:12

使用适当的命名POCO / DTO / etc类来做这个更好,但仍然可以做到。通过使用元编程可以消除反思成本,理想情况是使用预先滚动的库,例如 FastMember ,如下所示。

It would absolutely be better to do this using proper named POCO/DTO/etc classes, but it can still be done. The cost of reflection can be removed by using meta-programming, ideally by using a pre-rolled library such as FastMember, as shown below.

请注意,使用匿名类型已经强制使用 IList IList< T> 列表< T> 等)。使用命名类型将使用通用版本。这将允许一些更改 - 特别是 itemType 将是 typeof(T),并且可以即使为空表创建正确的列。也许更重要的是,它将执行列表是同构的,而不是必须对此做出假设。

Note that the use of anonymous types has forced the use of IList here (rather than IList<T> or List<T> etc). The use of a generic version would be preferable, using named types. This would allow a few changes - in particular, itemType would be typeof(T), and it would be possible to create the correct columns even for an empty table. Perhaps more importantly, it would enforce that the list is homogeneous, rather than having to make an assumption about that.

using FastMember;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
static class Program
{
    static void Main()
    {
        var list = GetList();
        var table = ToTable(list);
    }
    static DataTable ToTable(IList source)
    {
        if (source == null) throw new ArgumentNullException();
        var table = new DataTable();
        if (source.Count == 0) return table;

        // blatently assume the list is homogeneous
        Type itemType = source[0].GetType();
        table.TableName = itemType.Name;
        List<string> names = new List<string>();
        foreach (var prop in itemType.GetProperties())
        {
            if (prop.CanRead && prop.GetIndexParameters().Length == 0)
            {
                names.Add(prop.Name);
                table.Columns.Add(prop.Name, prop.PropertyType);
            }
        }
        names.TrimExcess();

        var accessor = TypeAccessor.Create(itemType);
        object[] values = new object[names.Count];
        foreach (var row in source)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = accessor[row, names[i]];
            }
            table.Rows.Add(values);
        }
        return table;
    }
    static IList GetList()
    {
        return new[] {
            new { foo = "abc", bar = 123},
            new { foo = "def", bar = 456},
            new { foo = "ghi", bar = 789},
        };
    }
}