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

C#如何将IEnumerable匿名列表转换为数据表,c#,linq,datatable,ienumerable,C#,Linq,Datatable,Ienumerable,有许多解决方案可以将列表转换为使用反射的DataTable,并且可以用于转换匿名类型。但是,如果有很多匿名类型的列表,那么性能可能会成为一个问题 这是从列表创建数据表的唯一方法吗?有更快的方法吗?使用适当的命名POCO/DTO/etc类来实现这一点绝对更好,但仍然可以实现。反射成本可以通过使用元编程来消除,理想情况下可以使用预卷库,如下图所示 注意,匿名类型的使用迫使此处使用IList(而不是IList或List等)。最好使用泛型版本,使用命名类型。这将允许一些更改—特别是,itemType将是

有许多解决方案可以将列表转换为使用反射的
DataTable
,并且可以用于转换匿名类型。但是,如果有很多匿名类型的列表,那么性能可能会成为一个问题


这是从列表创建数据表的唯一方法吗?有更快的方法吗?

使用适当的命名POCO/DTO/etc类来实现这一点绝对更好,但仍然可以实现。反射成本可以通过使用元编程来消除,理想情况下可以使用预卷库,如下图所示

注意,匿名类型的使用迫使此处使用
IList
(而不是
IList
List
等)。最好使用泛型版本,使用命名类型。这将允许一些更改—特别是,
itemType
将是
typeof(T)
,并且甚至可以为空表创建正确的列。也许更重要的是,它将强制执行该列表是同质的,而不必对此做出假设

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},
        };
    }
}
使用FastMember;
使用制度;
使用系统集合;
使用System.Collections.Generic;
使用系统数据;
静态类程序
{
静态void Main()
{
var list=GetList();
var表=总表(列表);
}
静态数据表ToTable(IList源)
{
如果(source==null)抛出新ArgumentNullException();
var table=新数据表();
if(source.Count==0)返回表;
//明目张胆地假设列表是同质的
类型itemType=源[0]。GetType();
table.TableName=itemType.Name;
列表名称=新列表();
foreach(itemType.GetProperties()中的var prop)
{
if(prop.CanRead&&prop.GetIndexParameters().Length==0)
{
名称。添加(道具名称);
table.Columns.Add(prop.Name,prop.PropertyType);
}
}
name.trimposure();
var accessor=TypeAccessor.Create(itemType);
object[]value=新对象[names.Count];
foreach(源中的变量行)
{
for(int i=0;i
改进了支持答案:

  • 字段的可选列表,指定要保留哪些列及其顺序
  • 可为空的类型

    static public DataTable ToDataTable(this IList anonymousSource, List<string> keepOrderedFieldsOpt = null)
    {
        // https://stackoverflow.com/a/13153479/538763 - @MarcGravell
        // Added keepOrderedFieldsOpt, nullable types - @crokusek
    
        if (anonymousSource == null) throw new ArgumentNullException();
        DataTable table = new DataTable();
        if (anonymousSource.Count == 0) return table;
    
        // blatently assume the list is homogeneous
        Type itemType = anonymousSource[0].GetType();
        table.TableName = itemType.Name;            
    
        // Build up orderedColumns
        //
        List<PropertyInfo> orderedColumns;            
        if (keepOrderedFieldsOpt != null)
        {
            Dictionary<string, PropertyInfo> propertiesByName = itemType.GetProperties()
                .ToDictionary(p => p.Name, p => p);
    
            orderedColumns = new List<PropertyInfo>();
            List<string> missingFields = null;
    
            foreach (string field in keepOrderedFieldsOpt)
            {
                PropertyInfo tempPropertyInfo;
                if (propertiesByName.TryGetValue(field, out tempPropertyInfo))
                    orderedColumns.Add(tempPropertyInfo);
                else
                    (missingFields ?? (missingFields = new List<string>())).Add(field);
            }
    
            if (missingFields != null) 
                throw new ArgumentOutOfRangeException("keepOrderedFieldsOpt", "Argument keepOrderedFieldsOpt contains invalid field name(s): " + String.Join(", ", missingFields));
        }
        else
            orderedColumns = itemType.GetProperties().ToList();
    
        List<string> names = new List<string>();
        foreach (PropertyInfo prop in orderedColumns)
        {
            if (prop.CanRead && prop.GetIndexParameters().Length == 0)
            {
                names.Add(prop.Name);
    
                // Nullable support from stackoverflow.com/a/23233413/538763 - @Damith
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }
        }
        names.TrimExcess();
    
        TypeAccessor accessor = TypeAccessor.Create(itemType);
        object[] values = new object[names.Count];
        foreach (var row in anonymousSource)
        {
            for (int i = 0; i < values.Length; i++)
                values[i] = accessor[row, names[i]];
    
            table.Rows.Add(values);
        }
        return table;
    }
    
    静态公共数据表到数据表(此IList匿名源,列表keeporderedFeldsopt=null)
    {
    // https://stackoverflow.com/a/13153479/538763 -@MarcGravel
    //添加了keeporderedfeldsopt,可为null的类型-@crokusek
    如果(anonymousSource==null)抛出新的ArgumentNullException();
    DataTable=新的DataTable();
    if(anonymousSource.Count==0)返回表;
    //明目张胆地假设列表是同质的
    类型itemType=anonymousSource[0]。GetType();
    table.TableName=itemType.Name;
    //建立orderedColumns
    //
    列出有序列;
    if(keeporderedfeldsopt!=null)
    {
    Dictionary propertiesByName=itemType.GetProperties()
    .ToDictionary(p=>p.Name,p=>p);
    orderedColumns=新列表();
    List missingFields=null;
    foreach(keeporderedfeldsopt中的字符串字段)
    {
    PropertyInfo tempPropertyInfo;
    if(propertiesByName.TryGetValue(字段,out tempPropertyInfo))
    添加(tempPropertyInfo);
    其他的
    (missingFields???(missingFields=newlist())。添加(字段);
    }
    如果(缺少字段!=null)
    抛出新ArgumentOutOfRangeException(“KeeporDeredFeldSopt”,“参数KeeporDeredFeldSopt包含无效的字段名:”+String.Join(“,”,missingFields));
    }
    其他的
    orderedColumns=itemType.GetProperties().ToList();
    列表名称=新列表();
    foreach(orderedColumns中的PropertyInfo属性)
    {
    if(prop.CanRead&&prop.GetIndexParameters().Length==0)
    {
    名称。添加(道具名称);
    //stackoverflow.com/a/23233413/538763-@Damith提供的可为空的支持
    table.Columns.Add(prop.Name,Nullable.GetUnderlyingType(prop.PropertyType)??prop.PropertyType);
    }
    }
    name.trimposure();
    TypeAccessor accessor=TypeAccessor.Create(itemType);
    object[]value=新对象[names.Count];
    foreach(匿名源中的var行)
    {
    for(int i=0;i

  • 在处理数千个匿名列表之前,我会切换到声明的类型。健全性检查:为什么要将匿名类型列表转换为
    数据表
    ?这似乎是一种退步
    DataTable
    通常不是一个理想的API—在大多数情况下,正则类是首选的。命名类当然比匿名类型更可取,但就我个人而言,我会选择匿名类型而不是
    DataTable
    来支持nullable://添加了nullable支持(@crokusek)-stackoverflow.com/a/23233413/538763-@Damith table.Columns.Add(prop.Name,nullable.getunderyingtype(prop.PropertyType)??prop.PropertyType);这被拒绝作为直接编辑,说评论会更好。什么是TypeAccessor@ITGenius它是
    FastMember
    中的一个类(链接在文本中),通过元编程提供了对反射的优化访问级别