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

有许多解决方案可以将列表转换为使用reflection的DataTable ,这可用于转换匿名类型。 但是,它有很多匿名类型的列表,然后性能可能是一个问题。

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

使用适当的命名POCO / DTO / etc类完成此操作绝对会更好,但它仍然可以完成。 可以通过使用元编程来消除reflection成本,理想情况是使用预卷库(如FastMember) ,如下所示。

请注意,匿名类型的使用强制在这里使用IList (而不是IListList等)。 使用命名类型时,最好使用通用版本。 这将允许一些更改 – 特别是, 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 names = new List(); 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}, }; } } 

改进了@ MarcGravell的支持答案:

  1. 一个可选的字段列表,指定要保留的列及其顺序。
  2. 可空类型。

     static public DataTable ToDataTable(this IList anonymousSource, List 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 orderedColumns; if (keepOrderedFieldsOpt != null) { Dictionary propertiesByName = itemType.GetProperties() .ToDictionary(p => p.Name, p => p); orderedColumns = new List(); List missingFields = null; foreach (string field in keepOrderedFieldsOpt) { PropertyInfo tempPropertyInfo; if (propertiesByName.TryGetValue(field, out tempPropertyInfo)) orderedColumns.Add(tempPropertyInfo); else (missingFields ?? (missingFields = new List())).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 names = new List(); 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; }