2012年3月17日星期六

颠覆常识??Reflection、Delegate、Expression 生成实体类,实测结果,反射最快。

我的ORM一直是用反射来实现动态生成Entity的,最近想提高一下效率,就尝试了一下用其他的方法来生成实体类。平时看到的资料都说用Expression的速度已经接近于Emit了,效率非常高,但测试的结果却让我大跌眼镜。。。下面对直接赋值、反射、委托、Expression四种方式来生成实体类进行测试。(暂时不考虑Emit),如果大家有其他更好的方法来生成实体类,请不吝赐教,谢谢。

先上测试结果:(环境:Windows 7 64 bit, I7 950,12G ram, VS2010)

Assign       ,generate entity 10000 times, time: 2
Reflection ,generate entity 10000 times, time: 54
Delegate ,generate entity 10000 times, time: 134
Expression ,generate entity 10000 times, time: 4323

Press any key to continue...

从结果中可以,直接赋值最快(但写和维护太麻烦,实际项目中一般不考虑),然后就是反射,到了用Expression的方式,效率是数量级的下降啊,比反射慢几十倍?

不知道是不是我的测试用例写的问题,下面详细说明一下我的测试过程:

1、原始数据是一个DataTable, 只有一行数据,三列。分别为Name,Age,Sex ,下面所有的转化只是把DataTable的第一行转为一个实体类。

public static DataTable GetCustomer()
{
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)));

DataRow dr = dt.NewRow();
dr["Name"] = "Andy";
dr["Age"] = "33";
dr["Sex"] = "Male";

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、直接赋值的方法:

 1 public class ToEntityByAssign
2 {
3 public Customer GetEntity(DataTable dt)
4 {
5 DataRow dr = dt.Rows[0];
6 Customer cus = new Customer();
7 cus.Name = dr["Name"].ToString();
8 cus.Age = dr["Age"].ToString();
9 cus.Sex = dr["Sex"].ToString();
10 return cus;
11 }
12 }

4、反射生成实体类的方法:

 1  public class ToEntityByReflection<T>
2 {
3 public T GetEntity(DataTable dt)
4 {
5 Type type = typeof(T);
6 T model = (T)Activator.CreateInstance(typeof(T));
7 DataRow dr = dt.Rows[0];
8
9 foreach (PropertyInfo pi in type.GetProperties())
10 {
11 pi.SetValue(model,Convert.ChangeType(dr[pi.Name],pi.PropertyType), null);
12 }
13
14 return model;
15 }
16 }

5、Delegate 生成实体类的方法:

 1 public delegate void SetString(string value);
2 public class ToEntityByDelegate<T>
3 {
4 public T GetEntity(DataTable dt)
5 {
6 Type type = typeof(T);
7 T model = (T)Activator.CreateInstance(typeof(T));
8 DataRow dr = dt.Rows[0];
9
10 foreach (DataColumn dc in dt.Columns)
11 {
12 var setDelegateString = CreateStringDelegate(model, dc.ColumnName);
13 setDelegateString(dr[dc.ColumnName].ToString());
14 }
15 return model;
16 }
17
18 private static SetString CreateStringDelegate(object obj, string PropertyName)
19 {
20 MethodInfo mi = obj.GetType().GetProperty(PropertyName).GetSetMethod();
21 Type type = typeof(SetString);
22 return (SetString)Delegate.CreateDelegate(type, obj, mi);
23 }
24 }

6、Expression 生成实体的方法:

public static class ToEntityByExpression
{
public static T GetEntity<T>(DataTable dt)
{
T t = (T)Activator.CreateInstance(typeof(T));
DataRow dr = dt.Rows[0];
foreach (PropertyInfo p in typeof(T).GetProperties())
{
object value = dr[p.Name] == DBNull.Value ? null : dr[p.Name];

p.FastSetValue<T>(t, value);
}
return t;
}

static Func<T, object, object> GetSetDelegate<T>(MethodInfo m, Type type)
{
ParameterExpression param_obj = Expression.Parameter(typeof(T), "obj");
ParameterExpression param_val = Expression.Parameter(typeof(object), "val");
UnaryExpression body_val = Expression.Convert(param_val, type);
MethodCallExpression body = Expression.Call(param_obj, m, body_val);
Action<T, object> set = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
return (instance, v) =>
{
set(instance, v);
return null;
};
}

static void FastSetValue<T>(this PropertyInfo property, T t, object value)
{
MethodInfo m = property.GetSetMethod();
GetSetDelegate<T>(m, property.PropertyType)(t, value);
}
}

7、主程序:

static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
DataTable dt = DataTableData.GetCustomer();
int times = 10000;

sw.Restart();
for (int i = 0; i < times; i++)
{
Customer cusReflection = new ToEntityByAssign().GetEntity(dt);
}
sw.Stop();
Console.WriteLine("{0} ,generate entity {1} times, time: {2}", "Assign".PadRight(12, ' '), times, sw.ElapsedMilliseconds.ToString());

sw.Restart();
for (int i = 0; i < times; i++)
{
Customer cusReflection = new ToEntityByReflection<Customer>().GetEntity(dt);
}
sw.Stop();
Console.WriteLine("{0} ,generate entity {1} times, time: {2}","Reflection".PadRight(12,' '), times, sw.ElapsedMilliseconds.ToString());

sw.Restart();
for (int i = 0; i < times; i++)
{
Customer cusDelegate = new ToEntityByDelegate<Customer>().GetEntity(dt);
}
sw.Stop();
Console.WriteLine("{0} ,generate entity {1} times, time: {2}", "Delegate".PadRight(12, ' '), times, sw.ElapsedMilliseconds.ToString());

sw.Restart();
for (int i = 0; i < times; i++)
{
Customer cusExpress = ToEntityByExpression.GetEntity<Customer>(dt);
}
sw.Stop();
Console.WriteLine("{0} ,generate entity {1} times, time: {2}", "Expression".PadRight(12, ' '), times, sw.ElapsedMilliseconds.ToString());


Console.WriteLine();
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}



这只一个用最简单的实例来测试,我在本地用数据库中的5万条数据,表中20几个Column的实际数据测试,结果差别更大。

因个人水平有限,测试做的简陋,如果有不当的地方,请大家指出。





颠覆常识??Reflection、Delegate、Expression 生成实体类,实测结果,反射最快。

TAG:C# Reflection Expression Delegate Entity