IEnumerable<T>轉(zhuǎn)DataTable的幾種方法
忘了為什么要把IEnumerable<T>轉(zhuǎn)成DataTable,不過(guò)這個(gè)需求應(yīng)該挺常見,恰好今天看到以前的一段代碼,有些想法就記錄下來(lái)。
IEnumerable<T>中的T是泛型,咱們就不能事先知道T都有哪些屬性,因此創(chuàng)建出來(lái)的DataTable也就不能預(yù)先設(shè)置列。遇到這種情況,首先就想到反射。
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 }
反射效率低,自然而然我們又想到了表達(dá)式樹這個(gè)東西,表達(dá)式樹的其中一個(gè)作用就是實(shí)現(xiàn)了反射的功能同時(shí)避免了反射帶來(lái)的效率問(wèn)題。
首先咱打算構(gòu)造一個(gè)形如obj=>obj.Property的表達(dá)式樹。
1 //構(gòu)造委托類似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會(huì)每次都去構(gòu)造表達(dá)式樹然后編譯Lambda,針對(duì)IEnumerable<T>中的每一條數(shù)據(jù)都重復(fù)構(gòu)造相同的屬性委托。兩者效率我沒測(cè)過(guò),不過(guò)這個(gè)方式肯定不完美。改進(jìn)的方式有很多,比如將屬性和GetGetDelegate構(gòu)造的委托作為鍵值對(duì)緩存起來(lái)供后續(xù)循環(huán)使用等等。下面是我想到的較好的一種解決方法:
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 }
這里我將屬性委托從返回單個(gè)屬性值變?yōu)榉祷厮袑傩灾禂?shù)組,我們就可以這么使用。
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 }
上述代碼可知,使用時(shí)只需要構(gòu)造一次委托即可。另外,這個(gè)方法我沒使用過(guò),大家可以試試看,哇哈哈哈哈。
轉(zhuǎn)載請(qǐng)注明本文出處:http://www.rzrgm.cn/newton/archive/2013/01/09/2853083.html

浙公網(wǎng)安備 33010602011771號(hào)