表达式的应用:反射方法调用效率优化
反射通过操作元数据,一般使用场景:一个是晚期绑定,CLR运行时动态加载程序集,建立类型对象等操作(如加载插件);另一个是提供通用的模型,进行通用的功能操作,一般和泛型一起用(如ORM)。
反射方法调用效率慢,是因为反射当于黑盒操作,看一下MethodInfo的Invoke()方法的参数就知道了,参数个数、类型都是未知的,都需要和真正的方法签名的参数进行校验,还会遇到装箱的操作。性能优化就是要解决参数校验的问题(明确参数的个数、类型),方法如下:
- 建立强类型委托, 使用Delegate.CreateDelegate()将反射方法绑定到一个定义好委托中;
- 使用Expression.Call()方法调用表达式。
和数据实体打交道,写了个通用的实体帮助类,使用表达式进行优化,实现对某一实体属性的批量设值和读取。这里写的是静态扩展类,也可以以属性作为最小处理单元进行封装,将get和set构建的方法缓存到类型为委托的属性上(因为一个属性get,set只需构建一次),后续通过属性直接拿来调用即可。
代码如下:

1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Linq.Expressions; 6 using System.Reflection; 7 using System.Text; 8 using System.Threading.Tasks; 9 10 namespace Extensions 11 { 12 ///13 /// 实体辅助类 14 /// 15 public static class EntityHelperExtension 16 { 17 /// 18 /// 构建获取属性值方法 19 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 20 /// 21 /// 类型 22 /// 属性类型 23 /// 属性描述 24 /// 获取属性值方法 25 public static Func BuildGetPropertyValueDelegate (this PropertyInfo propertyInfo) 26 { 27 var invokeObjExpr = Expression.Parameter(typeof(T), "item"); 28 var getValueExpr = Expression.Call( 29 invokeObjExpr,//Invoke第一个参数,即调用方法的实例,静态方法为null 30 propertyInfo.GetGetMethod(),//反射调用的方法 31 null//Invoke第二个参数,即调用方法的参数 表达式 32 ); 33 34 var getValueReturnExpr = Expression.Convert(getValueExpr, typeof(TK)); 35 var lambdaExpr = Expression.Lambda >(getValueReturnExpr, invokeObjExpr); 36 return lambdaExpr.Compile(); 37 } 38 39 /// 40 /// 构建设置属性值方法 41 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 42 /// 43 /// 类型 44 /// 属性类型 45 /// 属性描述 46 /// 设置属性值方法 47 public static Action BuildSetPropertyValueDelegate (this PropertyInfo propertyInfo) 48 { 49 var invokeObjExpr = Expression.Parameter(typeof(T), "item"); 50 var propValExpr = Expression.Parameter(propertyInfo.PropertyType, "propertyValue"); 51 var setValueExpr = Expression.Call( 52 invokeObjExpr, 53 propertyInfo.GetSetMethod(), 54 propValExpr 55 ); 56 57 var lambdaExpr = Expression.Lambda >(setValueExpr, 58 invokeObjExpr, propValExpr); 59 return lambdaExpr.Compile(); 60 } 61 62 /// 63 /// 构建获取属性值方法 64 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 65 /// 66 /// 类型 67 /// 属性类型 68 /// 属性名 69 /// 获取属性值方法和属性描述 70 public static (Func getPropertyValueDelegate, PropertyInfo propertyInfo) BuildGetPropertyValueDelegate (string propertyName) 71 { 72 var propertyInfo = typeof(T).GetProperty(propertyName); 73 if (propertyInfo == null) throw new ArgumentNullException($"属性不存在,属性名:{propertyName}"); 74 if (!propertyInfo.CanRead) throw new NotSupportedException($"属性不支持读操作,属性名:{propertyName}"); 75 76 var getValue = BuildGetPropertyValueDelegate (propertyInfo); 77 78 return (getValue, propertyInfo); 79 } 80 81 /// 82 /// 创建设置属性值方法 83 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 84 /// 85 /// 类型 86 /// 属性类型 87 /// 属性名 88 /// 设置属性值方法和属性描述 89 public static (Action setPropertyValueDelegate, PropertyInfo propertyInfo) BuildSetPropertyValueDelegate (string propertyName) 90 { 91 var propertyInfo = typeof(T).GetProperty(propertyName); 92 if (propertyInfo == null) throw new ArgumentNullException($"属性不存在,属性名:{propertyName}"); 93 if (!propertyInfo.CanWrite) throw new NotSupportedException($"属性不支持写操作,属性名:{propertyName}"); 94 95 var setValue = BuildSetPropertyValueDelegate (propertyInfo); 96 97 return (setValue, propertyInfo); 98 } 99 100 /// 101 /// 获取属性值 102 /// 103 /// 集合元素类型 104 /// 属性值类型 105 /// 集合 106 /// 属性名 107 /// 属性值集合 108 public static (IEnumerable propertyValues, PropertyInfo propertyInfo) GetAllProperty (this IEnumerable data, 109 string propertyName) where T : class 110 { 111 if (!data.Any()) throw new ArgumentException($"集合无元素,元素类型:{nameof(T)}"); 112 113 var (getValue, propertyInfo) = BuildGetPropertyValueDelegate (propertyName); 114 115 var propertyValueList = new List (); 116 foreach (var item in data) 117 { 118 propertyValueList.Add(getValue(item)); 119 } 120 121 return (propertyValueList, propertyInfo); 122 } 123 124 /// 125 /// 设置属性值 126 /// 127 /// 集合元素类型 128 /// 集合元素中属性类型 129 /// 集合 130 /// 属性名 131 /// 属性值 132 public static PropertyInfo SetAllProperty (this IEnumerable data, 133 string propertyName, TK propertyValue) where T : class 134 { 135 if (!data.Any()) throw new ArgumentException($"集合无元素,元素类型:{nameof(T)}"); 136 137 var (setValue, propertyInfo) = BuildSetPropertyValueDelegate (propertyName); 138 139 foreach (var item in data) 140 { 141 setValue(item, propertyValue); 142 } 143 144 return propertyInfo; 145 } 146 } 147 }
测试下性能和结果:

1 class EntityHelperExtensionTest 2 { 3 static void Main(string[] args) 4 { 5 EfficiencyTest(); 6 7 Console.ReadKey(); 8 } 9 10 static void EfficiencyTest() 11 { 12 var person = new Person(); 13 var namePropertyInfo = typeof(Person).GetProperty(nameof(Person.Name)); 14 15 var sw = new Stopwatch(); 16 17 sw.Start(); 18 for (int i = 0; i < 1000000; i++) 19 { 20 person.Name = "X"; 21 } 22 sw.Stop(); 23 Console.WriteLine($"属性赋值Name={ person.Name}:"+sw.ElapsedMilliseconds); 24 25 sw.Restart(); 26 for (int i = 0; i < 1000000; i++) 27 { 28 namePropertyInfo.SetValue(person, "XX"); 29 } 30 sw.Stop(); 31 Console.WriteLine($"反射Name={ person.Name}:" + sw.ElapsedMilliseconds); 32 33 sw.Restart(); 34 var setValue = namePropertyInfo.BuildSetPropertyValueDelegatestring>(); 35 for (int i = 0; i < 1000000; i++) 36 { 37 setValue.Invoke(person, "XXX"); 38 } 39 sw.Stop(); 40 Console.WriteLine($"表达式Name={ person.Name}:" + sw.ElapsedMilliseconds); 41 42 Console.WriteLine("-------------------"); 43 var people = GetData(); 44 sw.Restart(); 45 foreach (var person1 in people) 46 { 47 namePropertyInfo.SetValue(person1, "X"); 48 } 49 sw.Stop(); 50 Console.WriteLine($"反射Name={ people[0].Name}:" + sw.ElapsedMilliseconds); 51 52 sw.Restart(); 53 people.SetAllProperty("Name", "XX"); 54 sw.Stop(); 55 Console.WriteLine($"表达式Name={ people[0].Name}:" + sw.ElapsedMilliseconds); 56 } 57 58 public static List GetData() 59 { 60 List people = new (); 61 for (int i = 0; i < 1000000; i++) 62 { 63 Person person = new(); 64 person.Name = "张三"; 65 people.Add(person); 66 } 67 return people; 68 } 69 }
结果: