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

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

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

  1. 建立强类型委托, 使用Delegate.CreateDelegate()将反射方法绑定到一个定义好委托中;
  2. 使用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     /// <summary>
 13     /// 实体辅助类
 14     /// </summary>
 15     public static class EntityHelperExtension
 16     {
 17         /// <summary>
 18         /// 构建获取属性值方法
 19         /// 方法只需构建一次,适合大批量修改某一对象某一属性值
 20         /// </summary>
 21         /// <typeparam name="T">类型</typeparam>
 22         /// <typeparam name="TK">属性类型</typeparam>
 23         /// <param name="propertyInfo">属性描述</param>
 24         /// <returns>获取属性值方法</returns>
 25         public static Func<T, TK> BuildGetPropertyValueDelegate<T, TK>(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<Func<T, TK>>(getValueReturnExpr, invokeObjExpr);
 36             return lambdaExpr.Compile();
 37         }
 38 
 39         /// <summary>
 40         /// 构建设置属性值方法
 41         /// 方法只需构建一次,适合大批量修改某一对象某一属性值
 42         /// </summary>
 43         /// <typeparam name="T">类型</typeparam>
 44         /// <typeparam name="TK">属性类型</typeparam>
 45         /// <param name="propertyInfo">属性描述</param>
 46         /// <returns>设置属性值方法</returns>
 47         public static Action<T, TK> BuildSetPropertyValueDelegate<T, TK>(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<Action<T, TK>>(setValueExpr,
 58                 invokeObjExpr, propValExpr);
 59             return lambdaExpr.Compile();
 60         }
 61 
 62         /// <summary>
 63         /// 构建获取属性值方法
 64         /// 方法只需构建一次,适合大批量修改某一对象某一属性值
 65         /// </summary>
 66         /// <typeparam name="T">类型</typeparam>
 67         /// <typeparam name="TK">属性类型</typeparam>
 68         /// <param name="propertyName">属性名</param>
 69         /// <returns>获取属性值方法和属性描述</returns>
 70         public static (Func<T, TK> getPropertyValueDelegate, PropertyInfo propertyInfo) BuildGetPropertyValueDelegate<T, TK>(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<T, TK>(propertyInfo);
 77 
 78             return (getValue, propertyInfo);
 79         }
 80 
 81         /// <summary>
 82         /// 创建设置属性值方法
 83         /// 方法只需构建一次,适合大批量修改某一对象某一属性值
 84         /// </summary>
 85         /// <typeparam name="T">类型</typeparam>
 86         /// <typeparam name="TK">属性类型</typeparam>
 87         /// <param name="propertyName">属性名</param>
 88         /// <returns>设置属性值方法和属性描述</returns>
 89         public static (Action<T, TK> setPropertyValueDelegate, PropertyInfo propertyInfo) BuildSetPropertyValueDelegate<T, TK>(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<T, TK>(propertyInfo);
 96 
 97             return (setValue, propertyInfo);
 98         }
 99 
100         /// <summary>
101         /// 获取属性值
102         /// </summary>
103         /// <typeparam name="T">集合元素类型</typeparam>
104         /// <typeparam name="TK">属性值类型</typeparam>
105         /// <param name="data">集合</param>
106         /// <param name="propertyName">属性名</param>
107         /// <returns>属性值集合</returns>
108         public static (IEnumerable<TK> propertyValues, PropertyInfo propertyInfo) GetAllProperty<T, TK>(this IEnumerable<T> data,
109             string propertyName) where T : class
110         {
111             if (!data.Any()) throw new ArgumentException($"集合无元素,元素类型:{nameof(T)}");
112 
113             var (getValue, propertyInfo) = BuildGetPropertyValueDelegate<T, TK>(propertyName);
114 
115             var propertyValueList = new List<TK>();
116             foreach (var item in data)
117             {
118                 propertyValueList.Add(getValue(item));
119             }
120 
121             return (propertyValueList, propertyInfo);
122         }
123 
124         /// <summary>
125         /// 设置属性值
126         /// </summary>
127         /// <typeparam name="T">集合元素类型</typeparam>
128         /// <typeparam name="TK">集合元素中属性类型</typeparam>
129         /// <param name="data">集合</param>
130         /// <param name="propertyName">属性名</param>
131         /// <param name="propertyValue">属性值</param>
132         public static PropertyInfo SetAllProperty<T, TK>(this IEnumerable<T> 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<T, TK>(propertyName);
138 
139             foreach (var item in data)
140             {
141                 setValue(item, propertyValue);
142             }
143 
144             return propertyInfo;
145         }
146     }
147 }
View Code

  测试下性能和结果:

 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.BuildSetPropertyValueDelegate<Person, string>();
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<Person>  GetData()
59         {
60             List<Person> 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     }
View Code

  结果:

 

posted @ 2022-01-26 00:32  JN-SHao  阅读(108)  评论(0编辑  收藏  举报