反射通过操作元数据,一般使用场景:一个是晚期绑定,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 /// <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
结果: