我有几个方法返回不同的泛型列表。
在。net中存在任何类静态方法或将任何列表转换为数据表?我唯一能想到的是使用反射来做到这一点。
如果我有这个:
List<Whatever> whatever = new List<Whatever>();
(这下代码当然不工作,但我想有这样的可能性:
DataTable dt = (DataTable) whatever;
我有几个方法返回不同的泛型列表。
在。net中存在任何类静态方法或将任何列表转换为数据表?我唯一能想到的是使用反射来做到这一点。
如果我有这个:
List<Whatever> whatever = new List<Whatever>();
(这下代码当然不工作,但我想有这样的可能性:
DataTable dt = (DataTable) whatever;
当前回答
如果你的类中有属性,这行代码是OK的!!
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
但如果你有所有的公共字段,那么使用这个:
public static DataTable ToDataTable<T>( IList<T> data)
{
FieldInfo[] myFieldInfo;
Type myType = typeof(T);
// Get the type and fields of FieldInfoClass.
myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.Public);
DataTable dt = new DataTable();
for (int i = 0; i < myFieldInfo.Length; i++)
{
FieldInfo property = myFieldInfo[i];
dt.Columns.Add(property.Name, property.FieldType);
}
object[] values = new object[myFieldInfo.Length];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = myFieldInfo[i].GetValue(item);
}
dt.Rows.Add(values);
}
return dt;
}
原来的答案是从上面,我只是编辑使用字段而不是属性
然后这样使用它
DataTable dt = new DataTable();
dt = ToDataTable(myBriefs);
gridData.DataSource = dt;
gridData.DataBind();
其他回答
下面是NuGet使用FastMember的2013年更新:
IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data)) {
table.Load(reader);
}
它使用FastMember的元编程API来获得最大的性能。如果你想将它限制到特定的成员(或强制执行顺序),那么你也可以这样做:
IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) {
table.Load(reader);
}
FastMember是Marc Gravell的一个项目。它是金色的,完全是苍蝇!
是的,这个和这个完全相反;反射就足够了——或者如果你需要更快,2.0中的HyperDescriptor,或者3.5中的Expression。实际上,HyperDescriptor应该绰绰有余。
例如:
// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for(int i = 0 ; i < props.Count ; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}
现在,只需一行代码,你就可以让它比反射快很多很多倍(通过为对象类型T启用HyperDescriptor)。
编辑重新性能查询;下面是测试结果:
Vanilla 27179
Hyper 6997
我怀疑瓶颈已经从成员访问转移到数据表性能…我怀疑你在这方面还能提高多少……
代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
public class MyData
{
public int A { get; set; }
public string B { get; set; }
public DateTime C { get; set; }
public decimal D { get; set; }
public string E { get; set; }
public int F { get; set; }
}
static class Program
{
static void RunTest(List<MyData> data, string caption)
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.WaitForFullGCComplete();
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < 500; i++)
{
data.ToDataTable();
}
watch.Stop();
Console.WriteLine(caption + "\t" + watch.ElapsedMilliseconds);
}
static void Main()
{
List<MyData> foos = new List<MyData>();
for (int i = 0 ; i < 5000 ; i++ ){
foos.Add(new MyData
{ // just gibberish...
A = i,
B = i.ToString(),
C = DateTime.Now.AddSeconds(i),
D = i,
E = "hello",
F = i * 2
});
}
RunTest(foos, "Vanilla");
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(
typeof(MyData));
RunTest(foos, "Hyper");
Console.ReadLine(); // return to exit
}
}
试试这个
public static DataTable ListToDataTable<T>(IList<T> lst)
{
currentDT = CreateTable<T>();
Type entType = typeof(T);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
foreach (T item in lst)
{
DataRow row = currentDT.NewRow();
foreach (PropertyDescriptor prop in properties)
{
if (prop.PropertyType == typeof(Nullable<decimal>) || prop.PropertyType == typeof(Nullable<int>) || prop.PropertyType == typeof(Nullable<Int64>))
{
if (prop.GetValue(item) == null)
row[prop.Name] = 0;
else
row[prop.Name] = prop.GetValue(item);
}
else
row[prop.Name] = prop.GetValue(item);
}
currentDT.Rows.Add(row);
}
return currentDT;
}
public static DataTable CreateTable<T>()
{
Type entType = typeof(T);
DataTable tbl = new DataTable(DTName);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
foreach (PropertyDescriptor prop in properties)
{
if (prop.PropertyType == typeof(Nullable<decimal>))
tbl.Columns.Add(prop.Name, typeof(decimal));
else if (prop.PropertyType == typeof(Nullable<int>))
tbl.Columns.Add(prop.Name, typeof(int));
else if (prop.PropertyType == typeof(Nullable<Int64>))
tbl.Columns.Add(prop.Name, typeof(Int64));
else
tbl.Columns.Add(prop.Name, prop.PropertyType);
}
return tbl;
}
我自己编写了一个小型库来完成这个任务。它只在将对象类型转换为数据表时第一次使用反射。它发出一个方法,该方法将完成转换对象类型的所有工作。
它燃烧得很快。你可以在这里找到它:
ToDataTable()在MoreLinq上的GitHub 原:ModelShredder在GoogleCode,被合并到MoreLinq,从GoogleCode移动到GitHub
如果你的类中有属性,这行代码是OK的!!
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
但如果你有所有的公共字段,那么使用这个:
public static DataTable ToDataTable<T>( IList<T> data)
{
FieldInfo[] myFieldInfo;
Type myType = typeof(T);
// Get the type and fields of FieldInfoClass.
myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance
| BindingFlags.Public);
DataTable dt = new DataTable();
for (int i = 0; i < myFieldInfo.Length; i++)
{
FieldInfo property = myFieldInfo[i];
dt.Columns.Add(property.Name, property.FieldType);
}
object[] values = new object[myFieldInfo.Length];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = myFieldInfo[i].GetValue(item);
}
dt.Rows.Add(values);
}
return dt;
}
原来的答案是从上面,我只是编辑使用字段而不是属性
然后这样使用它
DataTable dt = new DataTable();
dt = ToDataTable(myBriefs);
gridData.DataSource = dt;
gridData.DataBind();
对Marc的答案做了一个小更改,使其与List<string> to data table等值类型一起工作:
public static DataTable ListToDataTable<T>(IList<T> data)
{
DataTable table = new DataTable();
//special handling for value types and string
if (typeof(T).IsValueType || typeof(T).Equals(typeof(string)))
{
DataColumn dc = new DataColumn("Value", typeof(T));
table.Columns.Add(dc);
foreach (T item in data)
{
DataRow dr = table.NewRow();
dr[0] = item;
table.Rows.Add(dr);
}
}
else
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
foreach (PropertyDescriptor prop in properties)
{
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
{
try
{
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
}
catch (Exception ex)
{
row[prop.Name] = DBNull.Value;
}
}
table.Rows.Add(row);
}
}
return table;
}