我的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的實際數據測試,結果差別更大。
因個人水平有限,測試做的簡陋,如果有不當的地方,請大家指出。
完整測試代碼下載:https://docs.google.com/open?id=0B9pEVL9Vjb34dGFUX0RWQnZSd1dsd0d3MXRWZ29RQQ ,點擊File菜單中的Download即可。
摘自 天羽