表达式的应用:反射方法调用效率优化

Posted SHao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了表达式的应用:反射方法调用效率优化相关的知识,希望对你有一定的参考价值。

  反射通过操作元数据,一般使用场景:一个是晚期绑定,CLR运行时动态加载程序集,建立类型对象等操作(如加载插件);另一个是提供通用的模型,进行通用的功能操作,一般和泛型一起用(如ORM)。

  反射方法调用效率慢,是因为反射当于黑盒操作,看一下MethodInfo的Invoke()方法的参数就知道了,参数个数、类型都是未知的,都需要和真正的方法签名的参数进行校验,还会遇到装箱的操作。性能优化就是要解决参数校验的问题(明确参数的个数、类型),方法如下:

  1. 建立强类型委托, 使用Delegate.CreateDelegate()将反射方法绑定到一个定义好委托中;
  2. 使用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方法)

java(十四)反射和正则表达式

java反射究竟消耗多少效率

通过编译lambda表达式来创建实例(可在反射时候用,效率比反射高一些)

Loogn.OrmLite映射优化记录