总所周知,反射对于运行时确定对象类型十分方便,但是它最大的不足就是效率低下,比直接调用的效率慢了一百倍有余。
在3.5以前有codeDom或借助Emit直接编写IL来优化其效率,但是使用不便,借助3.5新增的Expression,让我们有了一种既简洁,在速度上又较反射有很大的提高。 示例如下
1 public static T GetEntityByDataRowSlow<T>(this DataRow data) where T : new()
2 {
3 T t = new T();
4 PropertyInfo[] properties = typeof(T).GetProperties();
5
6 foreach (PropertyInfo p in properties)
7 {
8 object value = data[p.Name] == DBNull.Value ? null : data[p.Name];
9 p.SetValue(t, value, null);
10 }
11 return t;
12 }
如上,整段代码慢就慢在p.SetValue(t,value,null)这段上。 也有可能有人会说 typeof(T).GetProperties()获取所有属性应该缓存,但实际测试结果看下来影响并不大,效率相差无几。
接下来,主角登场了
1 static Func<T, object, object> GetSetDelegate<T>(MethodInfo m,Type type)
2 {
3
4 var param_obj = Expression.Parameter(typeof(T), "obj");
5 var param_val = Expression.Parameter(typeof(object), "val");
7 var body_val = Expression.Convert(param_val, type);
8 var body = Expression.Call(param_obj, m, body_val);
9 Action<T, object> set = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
10 return (instance, v) =>
11 {
12 set(instance, v);
13 return null;
14 };
15 }
2 {
3
4 var param_obj = Expression.Parameter(typeof(T), "obj");
5 var param_val = Expression.Parameter(typeof(object), "val");
7 var body_val = Expression.Convert(param_val, type);
8 var body = Expression.Call(param_obj, m, body_val);
9 Action<T, object> set = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
10 return (instance, v) =>
11 {
12 set(instance, v);
13 return null;
14 };
15 }
1 static void FastSetValue<T>(this PropertyInfo property,T t, object value)
2 {
3 MethodInfo m = property.GetSetMethod();
4 GetSetDelegate<T>(m,property.PropertyType)(t, value);
5 }
2 {
3 MethodInfo m = property.GetSetMethod();
4 GetSetDelegate<T>(m,property.PropertyType)(t, value);
5 }
关于Expression和lambda的介绍可参看园里大牛赵哥的文章 方法的直接调用,反射调用与……Lambda表达式调用
经过改良的调用方法
public static T FastGetEntityByDataRow<T>(this DataRow data) where T : new()
{
T t = new T();
PropertyInfo[] properties = GetProperties(typeof(T));
foreach (PropertyInfo p in properties)
{
object value = data[p.Name] == DBNull.Value ? null : data[p.Name];
p.FastSetValue<T>(t, value);
}
return t;
}
{
T t = new T();
PropertyInfo[] properties = GetProperties(typeof(T));
foreach (PropertyInfo p in properties)
{
object value = data[p.Name] == DBNull.Value ? null : data[p.Name];
p.FastSetValue<T>(t, value);
}
return t;
}
经过测试下来 如果直接是Entity.Property = "somevalue"设置属性的速度比值是1的话,反射的速度比值是100多,而经过改良的上述方法比值在2-3之间。
尽管这样,常见Web应用的主要瓶颈还是在结构的设计,数据库的读取,上面的方法对于整个程序框架的影响也只是积跬步,单用这个地方用了也几乎白用,不用白不用。谨记录一下。