表达式的应用:反射方法调用效率优化
Posted SHao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了表达式的应用:反射方法调用效率优化相关的知识,希望对你有一定的参考价值。
反射通过操作元数据,一般使用场景:一个是晚期绑定,CLR运行时动态加载程序集,建立类型对象等操作(如加载插件);另一个是提供通用的模型,进行通用的功能操作,一般和泛型一起用(如ORM)。
反射方法调用效率慢,是因为反射当于黑盒操作,看一下MethodInfo的Invoke()方法的参数就知道了,参数个数、类型都是未知的,都需要和真正的方法签名的参数进行校验,还会遇到装箱的操作。性能优化就是要解决参数校验的问题(明确参数的个数、类型),方法如下:
- 建立强类型委托, 使用Delegate.CreateDelegate()将反射方法绑定到一个定义好委托中;
- 使用Expression.Call()方法调用表达式。
和数据实体打交道,写了个通用的实体帮助类,使用表达式进行优化,实现对某一实体属性的批量设值和读取。这里写的是静态扩展类,也可以以属性作为最小处理单元进行封装,将get和set构建的方法缓存到类型为委托的属性上(因为一个属性get,set只需构建一次),后续通过属性直接拿来调用即可。
代码如下:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Extensions /// <summary> /// 实体辅助类 /// </summary> public static class EntityHelperExtension /// <summary> /// 构建获取属性值方法 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 /// </summary> /// <typeparam name="T">类型</typeparam> /// <typeparam name="TK">属性类型</typeparam> /// <param name="propertyInfo">属性描述</param> /// <returns>获取属性值方法</returns> public static Func<T, TK> BuildGetPropertyValueDelegate<T, TK>(this PropertyInfo propertyInfo) var invokeObjExpr = Expression.Parameter(typeof(T), "item"); var getValueExpr = Expression.Call( invokeObjExpr,//Invoke第一个参数,即调用方法的实例,静态方法为null propertyInfo.GetGetMethod(),//反射调用的方法 null//Invoke第二个参数,即调用方法的参数 表达式 ); var getValueReturnExpr = Expression.Convert(getValueExpr, typeof(TK)); var lambdaExpr = Expression.Lambda<Func<T, TK>>(getValueReturnExpr, invokeObjExpr); return lambdaExpr.Compile(); /// <summary> /// 构建设置属性值方法 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 /// </summary> /// <typeparam name="T">类型</typeparam> /// <typeparam name="TK">属性类型</typeparam> /// <param name="propertyInfo">属性描述</param> /// <returns>设置属性值方法</returns> public static Action<T, TK> BuildSetPropertyValueDelegate<T, TK>(this PropertyInfo propertyInfo) var invokeObjExpr = Expression.Parameter(typeof(T), "item"); var propValExpr = Expression.Parameter(propertyInfo.PropertyType, "propertyValue"); var setValueExpr = Expression.Call( invokeObjExpr, propertyInfo.GetSetMethod(), propValExpr ); var lambdaExpr = Expression.Lambda<Action<T, TK>>(setValueExpr, invokeObjExpr, propValExpr); return lambdaExpr.Compile(); /// <summary> /// 构建获取属性值方法 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 /// </summary> /// <typeparam name="T">类型</typeparam> /// <typeparam name="TK">属性类型</typeparam> /// <param name="propertyName">属性名</param> /// <returns>获取属性值方法和属性描述</returns> public static (Func<T, TK> getPropertyValueDelegate, PropertyInfo propertyInfo) BuildGetPropertyValueDelegate<T, TK>(string propertyName) var propertyInfo = typeof(T).GetProperty(propertyName); if (propertyInfo == null) throw new ArgumentNullException($"属性不存在,属性名:propertyName"); if (!propertyInfo.CanRead) throw new NotSupportedException($"属性不支持读操作,属性名:propertyName"); var getValue = BuildGetPropertyValueDelegate<T, TK>(propertyInfo); return (getValue, propertyInfo); /// <summary> /// 创建设置属性值方法 /// 方法只需构建一次,适合大批量修改某一对象某一属性值 /// </summary> /// <typeparam name="T">类型</typeparam> /// <typeparam name="TK">属性类型</typeparam> /// <param name="propertyName">属性名</param> /// <returns>设置属性值方法和属性描述</returns> public static (Action<T, TK> setPropertyValueDelegate, PropertyInfo propertyInfo) BuildSetPropertyValueDelegate<T, TK>(string propertyName) var propertyInfo = typeof(T).GetProperty(propertyName); if (propertyInfo == null) throw new ArgumentNullException($"属性不存在,属性名:propertyName"); if (!propertyInfo.CanWrite) throw new NotSupportedException($"属性不支持写操作,属性名:propertyName"); var setValue = BuildSetPropertyValueDelegate<T, TK>(propertyInfo); return (setValue, propertyInfo); /// <summary> /// 获取属性值 /// </summary> /// <typeparam name="T">集合元素类型</typeparam> /// <typeparam name="TK">属性值类型</typeparam> /// <param name="data">集合</param> /// <param name="propertyName">属性名</param> /// <returns>属性值集合</returns> public static (IEnumerable<TK> propertyValues, PropertyInfo propertyInfo) GetAllProperty<T, TK>(this IEnumerable<T> data, string propertyName) where T : class if (!data.Any()) throw new ArgumentException($"集合无元素,元素类型:nameof(T)"); var (getValue, propertyInfo) = BuildGetPropertyValueDelegate<T, TK>(propertyName); var propertyValueList = new List<TK>(); foreach (var item in data) propertyValueList.Add(getValue(item)); return (propertyValueList, propertyInfo); /// <summary> /// 设置属性值 /// </summary> /// <typeparam name="T">集合元素类型</typeparam> /// <typeparam name="TK">集合元素中属性类型</typeparam> /// <param name="data">集合</param> /// <param name="propertyName">属性名</param> /// <param name="propertyValue">属性值</param> public static PropertyInfo SetAllProperty<T, TK>(this IEnumerable<T> data, string propertyName, TK propertyValue) where T : class if (!data.Any()) throw new ArgumentException($"集合无元素,元素类型:nameof(T)"); var (setValue, propertyInfo) = BuildSetPropertyValueDelegate<T, TK>(propertyName); foreach (var item in data) setValue(item, propertyValue); return propertyInfo;
测试下性能和结果:
class EntityHelperExtensionTest static void Main(string[] args) EfficiencyTest(); Console.ReadKey(); static void EfficiencyTest() var person = new Person(); var namePropertyInfo = typeof(Person).GetProperty(nameof(Person.Name)); var sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 1000000; i++) person.Name = "X"; sw.Stop(); Console.WriteLine($"属性赋值Name= person.Name:"+sw.ElapsedMilliseconds); sw.Restart(); for (int i = 0; i < 1000000; i++) namePropertyInfo.SetValue(person, "XX"); sw.Stop(); Console.WriteLine($"反射Name= person.Name:" + sw.ElapsedMilliseconds); sw.Restart(); var setValue = namePropertyInfo.BuildSetPropertyValueDelegate<Person, string>(); for (int i = 0; i < 1000000; i++) setValue.Invoke(person, "XXX"); sw.Stop(); Console.WriteLine($"表达式Name= person.Name:" + sw.ElapsedMilliseconds); Console.WriteLine("-------------------"); var people = GetData(); sw.Restart(); foreach (var person1 in people) namePropertyInfo.SetValue(person1, "X"); sw.Stop(); Console.WriteLine($"反射Name= people[0].Name:" + sw.ElapsedMilliseconds); sw.Restart(); people.SetAllProperty("Name", "XX"); sw.Stop(); Console.WriteLine($"表达式Name= people[0].Name:" + sw.ElapsedMilliseconds); public static List<Person> GetData() List<Person> people = new (); for (int i = 0; i < 1000000; i++) Person person = new(); person.Name = "张三"; people.Add(person); return people;
结果:
以上是关于表达式的应用:反射方法调用效率优化的主要内容,如果未能解决你的问题,请参考以下文章
反射-优化及程序集等(用委托的方式调用需要反射调用的方法(或者属性字段),而不去使用Invoke方法)