IEnumerable<T>转DataTable的几种方法

简介: 忘了为什么要把IEnumerable转成DataTable,不过这个需求应该挺常见,恰好今天看到以前的一段代码,有些想法就记录下来。 IEnumerable中的T是泛型,咱们就不能事先知道T都有哪些属性,因此创建出来的DataTable也就不能预先设置列。

忘了为什么要把IEnumerable<T>转成DataTable,不过这个需求应该挺常见,恰好今天看到以前的一段代码,有些想法就记录下来。

IEnumerable<T>中的T是泛型,咱们就不能事先知道T都有哪些属性,因此创建出来的DataTable也就不能预先设置列。遇到这种情况,首先就想到反射。

 1 public static DataTable ToDataTable<T>(IEnumerable<T> collection)
 2 {
 3     var props = typeof(T).GetProperties();
 4     var dt = new DataTable();
 5     dt.Columns.AddRange(props.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray());
 6     if (collection.Count() > 0)
 7     {
 8         for (int i = 0; i < collection.Count(); i++)
 9         {
10             ArrayList tempList = new ArrayList();
11             foreach (PropertyInfo pi in props)
12             {
13                 object obj = pi.GetValue(collection.ElementAt(i), null);
14                 tempList.Add(obj);
15             }
16             object[] array = tempList.ToArray();
17             dt.LoadDataRow(array, true);
18         }
19     }
20     return dt;
21 }

反射效率低,自然而然我们又想到了表达式树这个东西,表达式树的其中一个作用就是实现了反射的功能同时避免了反射带来的效率问题。

首先咱打算构造一个形如obj=>obj.Property的表达式树。

 1 //构造委托类似Func<User, int> getAge = u => u.Age; 
 2 static Func<T, object> GetGetDelegate<T>(PropertyInfo p)
 3 {
 4     var param_obj = Expression.Parameter(typeof(T), "obj");
 5     //lambda的方法体 u.Age
 6     var pGetter = Expression.Property(param_obj, p);
 7     //编译lambda
 8     return Expression.Lambda<Func<T, object>>(pGetter, param_obj).Compile();
 9 }
10 
11 static object FastGetValue<T>(this PropertyInfo property, T t)
12 {
13     return GetGetDelegate<T>(property)(t);
14 }

然后我们就可以将上述反射代码改成如下:

 1 public static DataTable ToTable<T>(this IEnumerable<T> collection)
 2 {
 3     var props = typeof(T).GetProperties();
 4 
 5     var dt = new DataTable();
 6     dt.Columns.AddRange(
 7         props.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray()
 8     );
 9 
10     collection.ToList().ForEach(
11         i => dt.Rows.Add(props.Select(p => p.FastGetValue(i)).ToArray())
12     );
13 
14     return dt;
15 }

很好,咱们没用到反射就把工作完成了。但是效率真的有提升吗?我看未必。后者只是将前者的pi.GetValue(collection.ElementAt(i), null)改成p => p.FastGetValue(i),而FastGetValue会每次都去构造表达式树然后编译Lambda,针对IEnumerable<T>中的每一条数据都重复构造相同的属性委托。两者效率我没测过,不过这个方式肯定不完美。改进的方式有很多,比如将属性和GetGetDelegate构造的委托作为键值对缓存起来供后续循环使用等等。下面是我想到的较好的一种解决方法: 

1 static Func<T, object[]> GetGetDelegate<T>(PropertyInfo[] ps)
2 {
3     var param_obj = Expression.Parameter(typeof(T), "obj");
4     Expression newArrayExpression = Expression.NewArrayInit(typeof(object), ps.Select(p => Expression.Property(param_obj, p)));
5     return Expression.Lambda<Func<T, object[]>>(newArrayExpression, param_obj).Compile();
6 }

这里我将属性委托从返回单个属性值变为返回所有属性值数组,我们就可以这么使用。

 1 public static DataTable ToTable<T>(this IEnumerable<T> collection)
 2 {
 3     var props = typeof(T).GetProperties();
 4     var func = GetGetDelegate<T>(props);
 5     var dt = new DataTable();
 6     dt.Columns.AddRange(
 7         props.Select(p => new DataColumn(p.Name, p.PropertyType)).ToArray()
 8     );
 9     collection.ToList().ForEach(i => dt.Rows.Add(func(i)));
10 
11     return dt;
12 }

上述代码可知,使用时只需要构造一次委托即可。另外,这个方法我没使用过,大家可以试试看,哇哈哈哈哈。

转载请注明本文出处:http://www.cnblogs.com/newton/archive/2013/01/09/2853083.html

目录
相关文章
|
SQL
SQL Server判断某个字段是否包含中文/英文字符/数字
原文:SQL Server判断某个字段是否包含中文/英文字符/数字 因最近在清理系统中的脏数据,需要查询某个字段是否包含中文/英文字符/数字的数据, 比较简单,仅以此篇博客做一个简单总结,方便以后查阅。
5412 0
|
存储 SQL 数据库连接
C#程序调用Sql Server存储过程异常处理:调用存储过程后不返回、不抛异常的解决方案
本文分析了C#程序操作Sql Server数据库时偶发的不返回、不抛异常问题,并提出了解决思路。首先解析了一个执行存储过程的函数`ExecuteProcedure`,其功能是调用存储过程并返回影响行数。针对代码执行被阻塞但无异常的情况,文章总结了可能原因,如死锁、无限循环或网络问题等。随后提供了多种解决方案:1) 增加日志定位问题;2) 使用异步操作提升响应性;3) 设置超时机制避免阻塞;4) 利用线程池分离主线程;5) 通过信号量同步线程;6) 监控数据库连接状态确保可用性。这些方法可有效应对数据库操作中的潜在问题,保障程序稳定性。
877 11
|
JSON API 数据格式
如何用 Python 的 requests 库发送 JSON 数据的 POST 请求
使用 requests 库发送 JSON 数据的 POST 请求是一个非常简单且实用的操作。通过将目标 URL 和 JSON 数据传递给 requests.post 方法,你可以轻松发送请求并处理响应。本篇文章介绍了从安装 requests 库,到发送 JSON 数据的 POST 请求,再到处理响应的整个流程。希望这篇文章能帮助你更好地理解并应用这个强大的 HTTP 请求库。
微信接口报错 "errcode":40163,"errmsg":"code been used, 如何处理?
【10月更文挑战第11天】微信接口报错 "errcode":40163,"errmsg":"code been used, 如何处理?
7959 1
|
设计模式 开发框架 前端开发
在Winform界面中使用DevExpress的TreeList实现节点过滤查询的两种方式
在Winform界面中使用DevExpress的TreeList实现节点过滤查询的两种方式
|
前端开发 JavaScript C#
C#开发者的新天地:Blazor如何颠覆传统Web开发,打造下一代交互式UI?
【8月更文挑战第28天】Blazor 是 .NET 生态中的革命性框架,允许使用 C# 和 .NET 构建交互式 Web UI,替代传统 JavaScript。本文通过问答形式深入探讨 Blazor 的基本概念、优势及应用场景,并指导如何开始使用 Blazor。Blazor 支持代码共享、强类型检查和丰富的生态系统,简化 Web 开发流程。通过简单的命令即可创建 Blazor 应用,并利用其组件化和数据绑定特性快速搭建界面。无论对于 .NET 还是 Web 开发者,Blazor 都是一个值得尝试的新选择。
1198 1
|
开发框架 前端开发 JavaScript
在DevExpress的GridView的列中,使用RepositoryItemSearchLookUpEdit控件实现产品列表信息的展示和选择
在DevExpress的GridView的列中,使用RepositoryItemSearchLookUpEdit控件实现产品列表信息的展示和选择
|
存储 SQL 安全
函数(Function)和存储过程(Stored Procedure)的区别(小白情感版)
函数(Function)和存储过程(Stored Procedure)的区别(小白情感版)
1109 0