经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
用lambda表达式树优化反射
来源:cnblogs  作者:Fode  时间:2018/12/7 9:37:50  对本文有异议

本节重点不讲反射机制,而是讲lambda表达式树来替代反射中常用的获取属性和方法,来达到相同的效果但却比反射高效。

每个人都知道,用反射调用一个方法或者对属性执行SetValue和GetValue操作的时候都会比直接调用慢很多,这其中设计到CLR中内部的处理,不做深究。然而,我们在某些情况下又无法不使用反射,比如:在一个ORM框架中,你要将一个DataRow转化为一个对象,但你又不清楚该对象有什么属性,这时候你就需要写一个通用的泛型方法来处理,以下代码写得有点恶心,但不妨碍理解意思:

 

  1.      //将DataReader转化为一个对象
         private
    static T GetObj<T>(SqliteDataReader reader) where T : class
  2. {
  3. T obj = new T();
  4. PropertyInfo[] pros = obj.GetType().GetProperties();
  5. foreach (PropertyInfo item in pros)
  6. {
  7. try
  8. {
  9. Int32 Index = reader.GetOrdinal(item.Name);
  10. String result = reader.GetString(Index);
  11. if (typeof(String) == item.PropertyType)
  12. {
  13. item.SetValue(obj, result);
  14. continue;
  15. }
  16. if (typeof(DateTime) == item.PropertyType)
  17. {
  18. item.SetValue(obj, Convert.ToDateTime(result));
  19. continue;
  20. }
  21. if (typeof(Boolean) == item.PropertyType)
  22. {
  23. item.SetValue(obj, Convert.ToBoolean(result));
  24. continue;
  25. }
  26. if (typeof(Int32) == item.PropertyType)
  27. {
  28. item.SetValue(obj, Convert.ToInt32(result));
  29. continue;
  30. }
  31. if (typeof(Single) == item.PropertyType)
  32. {
  33. item.SetValue(obj, Convert.ToSingle(result));
  34. continue;
  35. }
  36. if (typeof(Single) == item.PropertyType)
  37. {
  38. item.SetValue(obj, Convert.ToSingle(result));
  39. continue;
  40. }
  41. if (typeof(Double) == item.PropertyType)
  42. {
  43. item.SetValue(obj, Convert.ToDouble(result));
  44. continue;
  45. }
  46. if (typeof(Decimal) == item.PropertyType)
  47. {
  48. item.SetValue(obj, Convert.ToDecimal(result));
  49. continue;
  50. }
  51. if (typeof(Byte) == item.PropertyType)
  52. {
  53. item.SetValue(obj, Convert.ToByte(result));
  54. continue;
  55. }
  56. }
  57. catch (ArgumentOutOfRangeException ex)
  58. {
  59. continue;
  60. }
  61. }
  62. return obj;
  63. }

 

  对于这种情况,其执行效率是特别低下的,具体多慢在下面例子会在.Net Core平台上和.Net Framework4.0运行测试案例.对于以上我举例的情况,效率上我们还可以得到提升。但对于想在运行时修改一下属性的名称或其他操作,反射还是一项特别的神器,因此在某些情况下反射还是无法避免的。

但是对于只是简单的SetValue或者GetValue,包括用反射构造函数,我们可以想一个中继的方法,那就是使用表达式树。对于不理解表达式树的,可以到微软文档查看,点击我。表达式树很容易通过对象模型表示表达式,因此强烈建议学习。查看以下代码:

  1. static void Main()
  2. {
  3. Dog dog = new Dog();
  4. PropertyInfo propertyInfo = dog.GetType().GetProperty(nameof(dog.Name)); //获取对象Dog的属性
  5. MethodInfo SetterMethodInfo = propertyInfo.GetSetMethod(); //获取属性Name的set方法
  6. ParameterExpression param = Expression.Parameter(typeof(Dog), "param");
  7. Expression GetPropertyValueExp = Expression.Lambda(Expression.Property(param, nameof(dog.Name)), param);
  8. Expression<Func<Dog, String>> GetPropertyValueLambda = (Expression<Func<Dog, String>>)GetPropertyValueExp;
  9. ParameterExpression paramo = Expression.Parameter(typeof(Dog), "param");
  10. ParameterExpression parami = Expression.Parameter(typeof(String), "newvalue");
  11. MethodCallExpression MethodCallSetterOfProperty = Expression.Call(paramo, SetterMethodInfo, parami);
  12. Expression SetPropertyValueExp = Expression.Lambda(MethodCallSetterOfProperty, paramo, parami);
  13. Expression<Action<Dog, String>> SetPropertyValueLambda = (Expression<Action<Dog, String>>)SetPropertyValueExp;
  14. //创建了属性Name的Get方法表达式和Set方法表达式,当然只是最简单的
  15. Func<Dog, String> Getter = GetPropertyValueLambda.Compile();
  16. Action<Dog, String> Setter = SetPropertyValueLambda.Compile();
  17. Setter?.Invoke(dog, "WLJ"); //我们现在对dog这个对象的Name属性赋值
  18. String dogName = Getter?.Invoke(dog); //获取属性Name的值
  19. Console.WriteLine(dogName);
  20. Console.ReadKey();
  21. }
  22. public class Dog
  23. {
  24. public String Name { get; set; }
  25. }

 

 以下代码可能很难看得懂,但只要知道我们创建了属性的Get、Set这两个方法就行,其结果最后也能输出狗的名字 WLJ,拥有ExpressionTree的好处是他有一个名为Compile()的方法,它创建一个代表表达式的代码块。现在是最有趣的部分,假设你在编译时不知道类型(在这篇文章中包含的代码我在不同的程序集上创建了一个类型)你仍然可以应用这种技术,我将对于常用的属性的set,get操作进行分装。

  1.   /// <summary>
  2.   /// 属性类,仿造反射中的PropertyInfo
  3. /// </summary>
  4.   public class Property
  5. {
  6. private readonly PropertyGetter getter;
  7. private readonly PropertySetter setter;
  8. public String Name { get; private set; }
  9. public PropertyInfo Info { get; private set; }
  10. public Property(PropertyInfo propertyInfo)
  11. {
  12. if (propertyInfo == null)
  13. throw new NullReferenceException("属性不能为空");
  14. this.Name = propertyInfo.Name;
  15. this.Info = propertyInfo;
  16. if (this.Info.CanRead)
  17. {
  18. this.getter = new PropertyGetter(propertyInfo);
  19. }
  20. if (this.Info.CanWrite)
  21. {
  22. this.setter = new PropertySetter(propertyInfo);
  23. }
  24. }
  25. /// <summary>
  26.    /// 获取对象的值
  27. /// </summary>
  28.   /// <param name="instance"></param>
  29.   /// <returns></returns>
  30.    public Object GetValue(Object instance)
  31. {
  32. return getter?.Invoke(instance);
  33. }
  34. /// <summary>
  35.    /// 赋值操作
  36. /// </summary>
  37.   /// <param name="instance"></param>
  38.   /// <param name="value"></param>
  39.    public void SetValue(Object instance, Object value)
  40. {
  41. this.setter?.Invoke(instance, value);
  42. }
  43. private static readonly ConcurrentDictionary<Type, Core.Reflection.Property[]> securityCache = new ConcurrentDictionary<Type, Property[]>();
  44. public static Core.Reflection.Property[] GetProperties(Type type)
  45. {
  46. return securityCache.GetOrAdd(type, t => t.GetProperties().Select(p => new Property(p)).ToArray());
  47. }
  48. }
  49. /// <summary>
  50.   /// 属性Get操作类
  51. /// </summary>
  52.    public class PropertyGetter
  53. {
  54. private readonly Func<Object, Object> funcGet;
  55. public PropertyGetter(PropertyInfo propertyInfo) : this(propertyInfo?.DeclaringType, propertyInfo.Name)
  56. {
  57. }
  58. public PropertyGetter(Type declareType, String propertyName)
  59. {
  60. if (declareType == null)
  61. {
  62. throw new ArgumentNullException(nameof(declareType));
  63. }
  64. if (propertyName == null)
  65. {
  66. throw new ArgumentNullException(nameof(propertyName));
  67. }
  68. this.funcGet = CreateGetValueDeleagte(declareType, propertyName);
  69. }
  70. //代码核心部分
  71.    private static Func<Object, Object> CreateGetValueDeleagte(Type declareType, String propertyName)
  72. {
  73. // (object instance) => (object)((declaringType)instance).propertyName
  74.     var param_instance = Expression.Parameter(typeof(Object));
  75. var body_objToType = Expression.Convert(param_instance, declareType);
  76. var body_getTypeProperty = Expression.Property(body_objToType, propertyName);
  77. var body_return = Expression.Convert(body_getTypeProperty, typeof(Object));
  78. return Expression.Lambda<Func<Object, Object>>(body_return, param_instance).Compile();
  79. }
  80. public Object Invoke(Object instance)
  81. {
  82. return this.funcGet?.Invoke(instance);
  83. }
  84. }

  85.  public class PropertySetter
  86. {
  87. private readonly Action<Object, Object> setFunc;
  88. public PropertySetter(PropertyInfo property)
  89. {
  90. if (property == null)
  91. {
  92. throw new ArgumentNullException(nameof(property));
  93. }
  94. this.setFunc = CreateSetValueDelagate(property);
  95. }
  96. private static Action<Object, Object> CreateSetValueDelagate(PropertyInfo property)
  97. {
  98. // (object instance, object value) =>
  99. // ((instanceType)instance).Set_XXX((propertyType)value)
  100. //声明方法需要的参数
  101. var param_instance = Expression.Parameter(typeof(Object));
  102. var param_value = Expression.Parameter(typeof(Object));
  103. var body_instance = Expression.Convert(param_instance, property.DeclaringType);
  104. var body_value = Expression.Convert(param_value, property.PropertyType);
  105. var body_call = Expression.Call(body_instance, property.GetSetMethod(), body_value);
  106. return Expression.Lambda<Action<Object, Object>>(body_call, param_instance, param_value).Compile();
  107. }
  108. public void Invoke(Object instance, Object value)
  109. {
  110. this.setFunc?.Invoke(instance, value);
  111. }
  112. }

在将代码应用到实例:

  1. Dog dog = new Dog();
  2. PropertyInfo propertyInfo = dog.GetType().GetProperty(nameof(dog.Name));
  3. //反射操作
  4. propertyInfo.SetValue(dog, "WLJ");
  5. String result = propertyInfo.GetValue(dog) as String;
  6. Console.WriteLine(result);
  7. //表达式树的操作
  8. Property property = new Property(propertyInfo);
  9. property.SetValue(dog, "WLJ2");
  10. String result2 = propertyInfo.GetValue(dog) as String;
  11. Console.WriteLine(result2);

发现其实现的目的与反射一致,但效率却有明显的提高。

以下测试以下他们两之间的效率。测试代码如下:

  1.        Student student = new Student();
  2. PropertyInfo propertyInfo = student.GetType().GetProperty(nameof(student.Name));
  3. Property ExpProperty = new Property(propertyInfo);
  4. Int32 loopCount = 1000000;
  5. CodeTimer.Initialize(); //测试环境初始化
  6. //下面该方法个执行1000000次
  7. CodeTimer.Time("基础反射", loopCount, () => {
  8. propertyInfo.SetValue(student, "Fode",null);
  9. });
  10. CodeTimer.Time("lambda表达式树", loopCount, () => {
  11. ExpProperty.SetValue(student, "Fode");
  12. });
  13. CodeTimer.Time("直接赋值", loopCount, () => {
  14. student.Name = "Fode";
  15. });
  16. Console.ReadKey();

其.Net4.0环境下运行结果如下:

.Net Core环境下运行结果:

 

从以上结果可以知道,迭代同样的次数反射需要183ms,而用表达式只要34ms,直接赋值需要7ms,在效率上,使用表达式这种方法有显著的提高,您可以看到使用此技术可以完全避免使用反射时的性能损失。反射之所以效率有点低主要取决于其加载的时候时在运行期下,而表达式则在编译期,下篇有空将会介绍用Emit技术优化反射,会比表达式略快一点。

注:对于常用对象的属性,最好将其缓存起来,这样效率会更高。

代码下载

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号