ReflectionHelper






4.93/5 (31投票s)
这个类使获取 MemberInfo 变得容易,无需使用魔术字符串(因此它对重构友好),并且还允许您创建委托来快速访问这些项目,比使用普通的 Invoke、GetValue 或 SetValue 方法快得多。
引言
我喜欢反射。它对很多事情都很有用。总的来说,它帮助我创建了自我发现代码,无论是用于序列化,还是用从数据库返回的数据填充类型,甚至是使用正确的模式在运行时实现一个接口。
最大的问题是速度。反射很慢。但它不应该这样。事实上,我不知道它为什么这么慢,所以我决定做一些事情来让它更快。
如何更快地使用反射?
当我们使用反射来获取或设置属性值时,我们主要遭受着未类型化的问题。也就是说,数据以对象的形式获取或设置,因此通常需要进行类型转换和装箱。这肯定会减慢速度。但是做一个简单的测试,创建一个访问字段(例如 int 类型)的方法,并使其将该值作为对象返回。它将比调用 field.GetValue 快得多。
好吧... 这不是一个公平的比较。字段调用是一种虚拟调用。所以,创建一个带有 GetValue 方法的接口,它将接收一个作为对象的“实例”,将其转换为正确的类型,进行字段获取并将其作为 int 返回。它仍然比反射调用快得多。
但是在接口和委托之间,我认为委托更简洁。事实上,我认为它们仍然快一点。因此,考虑到我们可以编译表达式来生成委托,我决定创建表达式来访问字段、属性甚至方法,并进行所有必要的类型转换。
当然,我们将花费时间编译委托。但是,如果您编译一次委托,然后调用该委托数千次,那么这样做肯定值得。
使用代码
好的... 如果你看到了这个提示的描述,你就会看到我将展示两件事
- 如何获取 fieldinfos、propertyinfos 等,而无需使用字符串和
- 如何生成委托以进行快速访问。
这两件事都通过使用 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。有很多这样的方法,因此您可以获得一个属性获取器(在我的情况下最常见)或一个构造函数委托。
完整的代码?嗯... 它是我的个人库的一部分,最初我只发布了那个。现在我只是在更新这篇文章,以放入一个您可以添加到任何项目的单个单元版本。