65.9K
CodeProject 正在变化。 阅读更多。
Home

ReflectionHelper

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (31投票s)

2012 年 6 月 29 日

CPOL

4分钟阅读

viewsIcon

78117

downloadIcon

2256

这个类使获取 MemberInfo 变得容易,无需使用魔术字符串(因此它对重构友好),并且还允许您创建委托来快速访问这些项目,比使用普通的 Invoke、GetValue 或 SetValue 方法快得多。

引言

我喜欢反射。它对很多事情都很有用。总的来说,它帮助我创建了自我发现代码,无论是用于序列化,还是用从数据库返回的数据填充类型,甚至是使用正确的模式在运行时实现一个接口。 

最大的问题是速度。反射很慢。但它不应该这样。事实上,我不知道它为什么这么慢,所以我决定做一些事情来让它更快。

如何更快地使用反射? 

当我们使用反射来获取或设置属性值时,我们主要遭受着未类型化的问题。也就是说,数据以对象的形式获取或设置,因此通常需要进行类型转换和装箱。这肯定会减慢速度。但是做一个简单的测试,创建一个访问字段(例如 int 类型)的方法,并使其将该值作为对象返回。它将比调用 field.GetValue 快得多。

好吧... 这不是一个公平的比较。字段调用是一种虚拟调用。所以,创建一个带有 GetValue 方法的接口,它将接收一个作为对象的“实例”,将其转换为正确的类型,进行字段获取并将其作为 int 返回。它仍然比反射调用快得多。

但是在接口和委托之间,我认为委托更简洁。事实上,我认为它们仍然快一点。因此,考虑到我们可以编译表达式来生成委托,我决定创建表达式来访问字段、属性甚至方法,并进行所有必要的类型转换。

当然,我们将花费时间编译委托。但是,如果您编译一次委托,然后调用该委托数千次,那么这样做肯定值得。  

使用代码

好的... 如果你看到了这个提示的描述,你就会看到我将展示两件事

  1. 如何获取 fieldinfos、propertyinfos 等,而无需使用字符串和 
  2. 如何生成委托以进行快速访问。 

这两件事都通过使用 ReflectionHelper 静态类来完成。

例如,要获取静态字段的 fieldinfo,您可以使用这个

ReflectionHelper.GetField(() => Type.EmptyTypes) 

在这种情况下,我们将获取 EmptyTypes 字段的 fieldinfo。不难想象您在那里有一个 GetMethod。所以,你可以这样使用它

ReflectionHelper.GetMethod(() => Console.WriteLine("")); 

你将获得该特定 WriteLine 方法重载的方法信息。但是,嘿,你可能想获取实例字段、属性或方法,对吧?

然后,您将使用 ReflectionHelper 类的泛型版本。泛型参数是实例的类型。因此,要获取 string 类中找到的 Length 属性的 propertyinfo,我们这样做

ReflectionHelper<string>.GetProperty((str) => str.Length)  

而所有这些方法实际上只是对 GetMember 方法的结果进行类型转换,它看起来像这样

public static MemberInfo GetMember<T>(Expression<Func<ForType, T>> expression)
{
  if (expression == null)
    throw new ArgumentNullException("expression");
        
  var body = expression.Body;
      
  switch(body.NodeType)
  {
    case ExpressionType.MemberAccess:
      MemberExpression memberExpression = (MemberExpression)body;
      return memberExpression.Member;
        
    case ExpressionType.Call:
      MethodCallExpression callExpression = (MethodCallExpression)body;
      return callExpression.Method;
        
    case ExpressionType.New:
      NewExpression newExpression = (NewExpression)body;
      return newExpression.Constructor;
  }
      
  throw new ArgumentException("expression.Body must be a member or call expression.", "expression");
 
} 

我想理解它的工作原理并不难。

当然,我更希望有一个 fieldinfoof 关键字,而不是构建一个表达式来完成这项工作。但是,由于我们没有这样的选项,我认为它是有效的。

 

然后,对于有趣的部分。我们如何为我们获得的 memberinfo 创建快速的“调用”?

我们只需创建正确的表达式,然后编译它。

例如,创建字段获取委托的代码如下所示

      private static Delegate _GetFieldGetterDelegate(Type instanceType, Type resultType, FieldInfo field)
      {
        var funcType = typeof(Func<,>).MakeGenericType(instanceType, resultType);
        
        var parameter = Expression.Parameter(instanceType, "instance");
        Expression resultExpression;
 
        if (field.IsStatic)
          resultExpression = Expression.MakeMemberAccess(null, field);
        else
        {
          Expression readParameter = parameter;
      
          if (field.DeclaringType != instanceType)
            readParameter = Expression.Convert(parameter, field.DeclaringType);
      
          resultExpression = Expression.MakeMemberAccess(readParameter, field);
        }
 
        if (field.FieldType != resultType)
          resultExpression = Expression.Convert(resultExpression, resultType);
        
        var lambda = Expression.Lambda(funcType, resultExpression, parameter);
 
        var result = lambda.Compile();
        return result;
      }  

事实上,如果我只生成一个 Func<object, object>,代码可以更简单,但是代码已准备好根据您想要的泛型参数为您进行类型转换(或避免类型转换)。它可以生成一个 Func<object, int>,因此类型转换将对输入参数进行,但是如果属性已经是 int,则不会对结果进行类型转换。

并且要使用该代码,您将像这样调用方法

ReflectionHelper.GetFieldGetterDelegate<MyType, int>(fieldInfo); 

在这种情况下,返回的委托是 Func<MyType, int>,因此输入应该已经被转换为 MyType,结果将是一个 int。有很多这样的方法,因此您可以获得一个属性获取器(在我的情况下最常见)或一个构造函数委托。 

完整的代码?嗯... 它是我的个人库的一部分,最初我只发布了那个。现在我只是在更新这篇文章,以放入一个您可以添加到任何项目的单个单元版本。 

© . All rights reserved.