直接赋值,Reflection、Delegate、Expression,Emit 等几种方式生成实体类测试,3.18更新。
我的ORM一直是用反射来实现动态生成Entity的,最近想提高一下效率,就尝试了一下用其他的方法来生成实体类。平时看到的资料都说用Expression的速度已经接近于Emit了,效率非常高,但测试的结果却让我大跌眼镜。。。下面对直接赋值、反射、委托、Expression和Emit 动态方法五种方式来生成实体类进行测试。如果大家有其他更好的方法来生成实体类,请不吝赐教,谢谢。
----------------
Update1: 重新修改了一下测试用例,让测试更加合理化,原来的循环是不合理的,现在改为一个DataTable ,生成一个List<T> 的实体类List
Update2: 增加了Emit 的方法,DynamicMethod确实比较快。重写了Expression的测试方法,缓存了Func<>代理后,速度确实快多了。但Delegate的方法没想到怎么能不每次都CreateDelegate,谁有好办法可以讲一下,谢谢先。
----------------
先上测试结果:(环境:Windows 7 64 bit, I7 950,12G ram, VS2010)
Assign ,generate entity, total 10000 rows, time: 3
Emit ,generate entity, total 10000 rows, time: 7
Expression ,generate entity, total 10000 rows, time: 38
Reflection ,generate entity, total 10000 rows, time: 39
Delegate ,generate entity, total 10000 rows, time: 137
Press any key to continue...
从结果中可以,直接赋值最快(但写和维护太麻烦,实际项目中一般不考虑),然后就是Emit,IL确实比较快。Expression比反射快,但还是比Emit差不少,是不是测试用例还是不合理呢?
详细说明一下我的测试过程:
1、原始数据是一个DataTable, 一万行数据,三列。分别为Name,Age,Sex ,下面所有的转化是把整个DataTable转为一个List<T>
public static DataTable GetCustomer(int _rows)
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Name", typeof(System.String)));
dt.Columns.Add(new DataColumn("Age", typeof(System.String)));
dt.Columns.Add(new DataColumn("Sex", typeof(System.String)));
for (int i = 1; i <= _rows; i++)
{
DataRow dr = dt.NewRow();
dr["Name"] = "Andy" + i.ToString();
dr["Age"] = i.ToString();
dr["Sex"] = "Male" + i.ToString();
dt.Rows.Add(dr);
}
return dt;
}
2、要生成的实体类也很简单,只有三个property,为了避免类型转换等问题,用最简单的,都是string 类型。
public class Customer
{
public string Name { get; set; }
public string Age { get; set; }
public string Sex { get; set; }
}
3、直接赋值的方法:
public List<Customer> GetEntity(DataTable dt)
{
List<Customer> lst=new List<Customer>();
foreach (DataRow dr in dt.Rows)
{
Customer cus = new Customer();
cus.Name = dr["Name"].ToString();
cus.Age = dr["Age"].ToString();
cus.Sex = dr["Sex"].ToString();
lst.Add(cus);
}
return lst;
}
4、反射生成实体类的方法:
1 public class ToEntityByReflection<T>
2 {
3 public List<T> GetEntity(DataTable dt)
4 {
5 List<T> lst = new List<T>();
6 Type type = typeof(T);
7
8 foreach (DataRow dr in dt.Rows)
9 {
10 T model = (T)Activator.CreateInstance(typeof(T));
11 foreach (PropertyInfo pi in type.GetProperties())
12 {
13 pi.SetValue(model, Convert.ChangeType(dr[pi.Name], pi.PropertyType), null);
14 }
15 lst.Add(model);
16 }
17 return lst;
18 }
19 }
5、Delegate 生成实体类的方法:
1 public delegate void SetString(string value);
2 public class ToEntityByDelegate<T>
3 {
4 public List<T> GetEntity(DataTable dt)
5 {
6 List<T> lst = new List<T>();
7 Type type = typeof(T);
8 SetString setDelegateString;
9
10 foreach (DataRow dr in dt.Rows)
11 {
12 T model = (T)Activator.CreateInstance(typeof(T));
13 foreach (DataColumn dc in dt.Columns)
14 {
15 setDelegateString = CreateStringDelegate(model, dc.ColumnName); // 谁知道怎么缓存这个???
16 setDelegateString(dr[dc.ColumnName].ToString());
17 }
18 lst.Add(model);
19 }
20
21 return lst;
22 }
23
24 private static SetString CreateStringDelegate(object obj, string PropertyName)
25 {
26 MethodInfo mi = obj.GetType().GetProperty(PropertyName).GetSetMethod();
27 Type type = typeof(SetString);
28 return (SetString)Delegate.CreateDelegate(type, obj, mi);
29 }
30 }
6、Expression 生成实体的方法:
1 public static class ToEntityByExpression
2 {
3 public static List<T> GetEntity<T>(DataTable dt)
4 {
5 List<T> lst = new List<T>();
6 Dictionary<string, Func<T, object, object>> dic = new Dictionary<string, Func<T, object, object>>();
7 foreach (DataColumn dc in dt.Columns)
8 {
9 PropertyInfo pi = typeof(T).GetProperty(dc.ColumnName);
10 if (!dic.ContainsKey(dc.ColumnName))
11 {
12 Func<T, object, object> fc = SetDelegate<T>(pi.GetSetMethod(), pi.PropertyType);
13 dic.Add(dc.ColumnName, fc);
14 }
15 }
16
17 foreach (DataRow dr in dt.Rows)
18 {
19 T model = (T)Activator.CreateInstance(typeof(T));
20
21 foreach (DataColumn dc in dt.Columns)
22 {
23 Func<T, object, object> fc = dic[dc.ColumnName];
24 fc(model, dr[dc.ColumnName]);
25 }
26 lst.Add(model);
27 }
28
29 return lst;
30 }
31
32
33
34 static Func<T, object,object> SetDelegate<T>(MethodInfo m, Type type)
35 {
36 ParameterExpression param_obj = Expression.Parameter(typeof(T), "obj");
37 ParameterExpression param_val = Expression.Parameter(typeof(object), "val");
38 UnaryExpression body_val = Expression.Convert(param_val, type);
39 MethodCallExpression body = Expression.Call(param_obj, m, body_val);
40 Action<T, object> set = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
41
42 return (instance, v) =>
43 {
44 set(instance, v);
45 return null;
46 };
47 }
48 }
7、Emit 动态方法:
1 public static class ToEntityByEmit
2 {
3 public static List<T> GetList<T>(DataTable dt)
4 {
5 List<T> lst = new List<T>();
6 if (dt == null) return lst;
7 DataTableEntityBuilder<T> eblist = DataTableEntityBuilder<T>.CreateBuilder(dt.Rows[0]);
8 foreach (DataRow dr in dt.Rows)
9 {
10 lst.Add(eblist.Build(dr));
11 }
12 dt.Dispose();
13 dt = null;
14 return lst;
15 }
16 }
17
18 public class DataTableEntityBuilder<T>
19 {
20 private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
21 private static readonly MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
22 private delegate T Load(DataRow dr);
23
24 private Load handler;
25 private DataTableEntityBuilder() { }
26
27 public T Build(DataRow dr)
28 {
29 return handler(dr);
30 }
31
32 public static DataTableEntityBuilder<T> CreateBuilder(DataRow dr)
33 {
34 DataTableEntityBuilder<T> dynamicBuilder = new DataTableEntityBuilder<T>();
35 DynamicMethod method = new DynamicMethod("DynamicCreateEntity", typeof(T), new Type[] { typeof(DataRow) }, typeof(T), true);
36 ILGenerator generator = method.GetILGenerator();
37 LocalBuilder result = generator.DeclareLocal(typeof(T));
38 generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
39 generator.Emit(OpCodes.Stloc, result);
40
41 for (int i = 0; i < dr.ItemArray.Length; i++)
42 {
43 PropertyInfo pi = typeof(T).GetProperty(dr.Table.Columns[i].ColumnName);
44 Label endIfLabel = generator.DefineLabel();
45 if (pi != null && pi.GetSetMethod() != null)
46 {
47 generator.Emit(OpCodes.Ldarg_0);
48 generator.Emit(OpCodes.Ldc_I4, i);
49 generator.Emit(OpCodes.Callvirt, isDBNullMethod);
50 generator.Emit(OpCodes.Brtrue, endIfLabel);
51 generator.Emit(OpCodes.Ldloc, result);
52 generator.Emit(OpCodes.Ldarg_0);
53 generator.Emit(OpCodes.Ldc_I4, i);
54 generator.Emit(OpCodes.Callvirt, getValueMethod);
55 generator.Emit(OpCodes.Unbox_Any, pi.PropertyType);
56 generator.Emit(OpCodes.Callvirt, pi.GetSetMethod());
57 generator.MarkLabel(endIfLabel);
58 }
59 }
60 generator.Emit(OpCodes.Ldloc, result);
61 generator.Emit(OpCodes.Ret);
62 dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
63 return dynamicBuilder;
64 }
65 }
8、主程序:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Stopwatch sw = new Stopwatch();
6 int rows = 10000;
7 DataTable dt = DataTableData.GetCustomer(rows);
8
9 sw.Restart();
10 List<Customer> cusAssign = new ToEntityByAssign().GetEntity(dt);
11 sw.Stop();
12 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Assign".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
13 //Console.WriteLine("The fifth element is: {0}",cusAssign[4].Name+" , "+ cusAssign[4].Age+" , "+cusAssign[4].Sex);
14
15 sw.Restart();
16 List<Customer> cusEmit = ToEntityByEmit.GetList<Customer>(dt);
17 sw.Stop();
18 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Emit".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
19 //Console.WriteLine("The fifth element is: {0}", cusEmit[4].Name + " , " + cusEmit[4].Age + " , " + cusEmit[4].Sex);
20
21 sw.Restart();
22 List<Customer> cusExpress = ToEntityByExpression.GetEntity<Customer>(dt);
23 sw.Stop();
24 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Expression".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
25 //Console.WriteLine("The fifth element is: {0}", cusExpress[4].Name + " , " + cusExpress[4].Age + " , " + cusExpress[4].Sex);
26
27 sw.Restart();
28 List<Customer> cusReflection = new ToEntityByReflection<Customer>().GetEntity(dt);
29 sw.Stop();
30 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Reflection".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
31 //Console.WriteLine("The fifth element is: {0}", cusReflection[4].Name + " , " + cusReflection[4].Age + " , " + cusReflection[4].Sex);
32
33 sw.Restart();
34 List<Customer> cusDelegate = new ToEntityByDelegate<Customer>().GetEntity(dt);
35 sw.Stop();
36 Console.WriteLine("{0} ,generate entity, total {1} rows, time: {2}", "Delegate".PadRight(12, ' '), rows, sw.ElapsedMilliseconds.ToString());
37 //Console.WriteLine("The fifth element is: {0}", cusDelegate[4].Name + " , " + cusDelegate[4].Age + " , " + cusDelegate[4].Sex);
38
39
40 Console.WriteLine();
41 Console.WriteLine("Press any key to continue...");
42 Console.ReadKey();
43 }
44 }
这只一个用最简单的实例来测试,因个人水平有限,测试做的简陋,如果有不当的地方,请大家指出。
非常感谢回帖的各位同学,让我学到很多东西。
学习的过程中参考了Artech 和老赵等人的文章如下:
http://www.cnblogs.com/artech/archive/2011/03/26/1996057.html
http://www.cnblogs.com/JeffreyZhao/archive/2008/11/24/invoke-method-by-lambda-expression.html
http://hi.baidu.com/ysuhy/blog/item/5b993f003168ca0d728da540.html
完整测试代码下载:
Ver 3: https://files.cnblogs.com/andyliu/testGenerateEntity.rar