.NET反射的优化

写在前面

1)本篇博客并不是原创,而是我针对.NET反射相关知识的总结。本篇内容来源汇总于3篇博客。在后面的介绍中会在开头给出对应的连接,方便读者自行学习。
2)本篇博客主要针对表达式树代码进行详细讲解。
html

反射优化简介

咱们知道反射与直接调用相比性能要慢不少,所以本篇主要针对如何对反射进行优化。 目前最多见的优化反射性能的方法就是采用委托:用委托的方式调用须要反射调用的方法(或者属性、字段)。编程

那么如何获得委托? 三种方法:Emit、Delegate.CreateDelegate、ExpressionTree 。下面将分别介绍这3中方法。app

Emit建立委托

Emit建立委托,主要靠本身编写IL代码。操做IL代码:难度高,并且不易于阅读。因此我对这种方法并不感冒。下面将直接给出代码,在代码中有对应的注释,各位有兴趣能够研究研究。ide

public class EmitToReflector { public delegate void SetValueDelegate(object target, object arg); public static SetValueDelegate CreatePropertySetter(PropertyInfo property) { //验证
        if (property == null) throw new ArgumentNullException("property"); if (!property.CanWrite) return null; MethodInfo setMethod = property.GetSetMethod(true); //建立一个名为PropertySetter的动态方法,其目的就是生成上面的setMethod
        DynamicMethod dm = new DynamicMethod("PropertySetter", null,new Type[] { typeof(object), typeof(object) },property.DeclaringType, true); //建立一个MSIL生成器,为动态方法生成代码
        ILGenerator il = dm.GetILGenerator(); //下面全是操做IL
        if (!setMethod.IsStatic) { il.Emit(OpCodes.Ldarg_0); } il.Emit(OpCodes.Ldarg_1); EmitCastToReference(il, property.PropertyType); if (!setMethod.IsStatic && !property.DeclaringType.IsValueType) { il.EmitCall(OpCodes.Callvirt, setMethod, null); } else il.EmitCall(OpCodes.Call, setMethod, null); //方法结束,返回
 il.Emit(OpCodes.Ret); //生成对应的委托
        return (SetValueDelegate)dm.CreateDelegate(typeof(SetValueDelegate)); } private static void EmitCastToReference(ILGenerator il, Type type) { if (type.IsValueType) il.Emit(OpCodes.Unbox_Any, type); else il.Emit(OpCodes.Castclass, type); } } 
View Code

运行结果:(测试代码我会在文章末尾给出)函数

后来我了解到,Emit其强大之处在于:能够动态的生成程序集、方法,而后对其进行调用,实现AOP的编程思想。(这个我没了解过,前文只是抛砖引玉之功用)性能

原文连接:http://www.cnblogs.com/yingql/archive/2009/03/20/1418007.html学习

Delegate.CreateDelegate建立委托

Delegate.CreateDelegate()也能够建立委托,可是它有一个缺点,就是只能建立参数是强类型(<T>)委托,而不能建立参数是通用类型(Object)委托。
测试

下面的测试代码,说明了Delegate.CreateDelegate()没法建立参数是通用类型的委托。优化

    Person person = new Person(); PropertyInfo propInfo = typeof(Person).GetProperty("Name"); //只能建立强类型委托 //Action<Person, string> setter = (Action<Person, string>)Delegate.CreateDelegate( // typeof(Action<Person, string>), null, propInfo.GetSetMethod()); //放开这段代码,你会发现便宜错误
    Action<object, object> setter = (Action<object, object>)Delegate.CreateDelegate( typeof(Action<object, object>), null, propInfo.GetSetMethod()); setter(person, "Test"); Console.WriteLine(person.Name);
View Code

针对反射,及程序运行时获得对应的Type(若是知道具体的类型,又何须用反射呢?),而后对其进行操做。因此支持参数是通用类型是必须的。spa

解决具体类型-》通用类型的转换分如下几个步骤

1.具体类型-》泛型,及Action<Person,string>-》Action<TTarget,TValue>。

public class DelegateToReflector<TTarget, TValue> { Action<TTarget, TValue> _setter; public DelegateToReflector(PropertyInfo propertyInfo) { if (propertyInfo == null) throw new ArgumentNullException("propertyInfo"); if (propertyInfo.CanWrite == false) throw new NotSupportedException("属性不支持写操做。"); MethodInfo m = propertyInfo.GetSetMethod(true); _setter = (Action<TTarget, TValue>)Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), null, m); } }

2.生成赋值委托时,根据入参的属性(Property),没法直接生成Action<TTarget,TValue>。因此在①中把Action<TTarget,TValue>做为类的一个变量。经过构造函数对其进行赋值。这样在②中能够经过Type.MakeGenericType()获取对应的类型,而后建立对应的实例,从而为Action<TTarget,TValue>赋值。

public class GetterSetterFactory { public static object CreatePropertySetterWrapper(PropertyInfo propertyInfo) {     if (propertyInfo == null)       throw new ArgumentNullException("propertyInfo");     if (propertyInfo.CanWrite == false)       throw new NotSupportedException("属性不支持写操做。");     MethodInfo mi = propertyInfo.GetSetMethod(true);     if (mi.GetParameters().Length > 1)       throw new NotSupportedException("不支持构造索引器属性的委托。");     Type instanceType = typeof(DelegateToReflector<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType);     return Activator.CreateInstance(instanceType, propertyInfo); } }

3.经过①、②获得可用的Action<TTarget,TValue>(setter)后。下面就是调用。调用将以通用的形式(setter(object,object))的形式调用。能够定义一个接口

public interface ISetValue { void Set(object target, object val); } public class DelegateToReflector<TTarget, TValue> : ISetValue { //...以前的代码

  public void Set(object target, object value)   {     _setter((TTarget)target, (TValue)value);   } } public class GetterSetterFactory { public static ISetValue CreatePropertySetterWrapper(PropertyInfo propertyInfo) { //...以前的代码
          return (ISetValue)Activator.CreateInstance(instanceType, propertyInfo); } }

4.最后就是测试代码

static void TestDelegateToReflector() { int count = 1000000; Person testObj = new Person(); PropertyInfo propInfo = typeof(Person).GetProperty("Name"); Console.Write("泛型委托花费时间: "); DelegateToReflector<Person, string> setter3 = new DelegateToReflector<Person, string>(propInfo); Stopwatch watch4 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter3.Set(testObj, "Test"); watch4.Stop(); Console.WriteLine(watch4.Elapsed.ToString()); Console.Write("通用接口花费时间: "); var setter4 = GetterSetterFactory.CreatePropertySetterWrapper(propInfo); Stopwatch watch5 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter4.Set(testObj, "Test"); watch5.Stop(); Console.WriteLine(watch5.Elapsed.ToString()); }
View Code

结论:Delegate.CreateDelegate建立委托,代码可读性Emit建立委托好太多,可是实现上有一些绕(因为没法直接映射Action<object,object>,因此须要绕了一个大圈子来解决这个问题),不够直接。

原文连接:http://www.cnblogs.com/fish-li/archive/2013/02/18/2916253.html

表达式树(Expression)建立委托

表达式树建立委托,实现的思路上比Delgate.CreateDelagate直接,代码的实现上也易于阅读。依旧以SetProperty()做为示例,Delegate.CreateDelegate()没法直接建立Action<object,object>,而表达式树能够直接建立Action<object,object>。

public static Action<object, object> InitSetter(PropertyInfo propertyInfo) { //((T)instance).Property = ((V)parm) //定义两个参数:instance(属性的拥有者),parm(要设置的值) var instance = Expression.Parameter(typeof(object), "instance"); var parm = Expression.Parameter(typeof(object), "parm"); //判断方法是不是静态的,若非静态方法,须要把instance转换为对应的类型 var instanceCast = propertyInfo.GetSetMethod().IsStatic ? null : Expression.Convert(instance, propertyInfo.ReflectedType); //参数类型转换((V)parm) var parmCast = Expression.Convert(parm, propertyInfo.PropertyType); //((T)instance).Property var property = Expression.Property(instanceCast, propertyInfo); //属性赋值操做 ((T)instance).Property = ((V)parm) var setProperty = Expression.Assign(property, parmCast); //定义委托 var lambda = Expression.Lambda<Action<object, object>>(setProperty, instance, parm); //建立委托 return lambda.Compile(); }  

说明:

1.Expression有许多静态方法,你须要自行了解。

2.代码已经给出对应的注释,我想不用我多做赘述了。

3.注意这个方法的第一行注释,就是这个方法的实现思路。

4.表达式树须要作对应的类型转换,这点必定要切记。

最后就是测试代码:

static void TestExpressionToReflector() { int count = 1000000; Person testObj = new Person(); PropertyInfo propInfo = typeof(Person).GetProperty("Name"); Console.Write("泛型委托花费时间: "); DelegateToReflector<Person, string> setter3 = new DelegateToReflector<Person, string>(propInfo); Stopwatch watch4 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter3.Set(testObj, "Test"); watch4.Stop(); Console.WriteLine(watch4.Elapsed.ToString()); Console.Write("通用接口花费时间: "); var setter4 = ExpressionToReflector.InitSetter(propInfo); Stopwatch watch5 = Stopwatch.StartNew(); for (int i = 0; i < count; i++) setter4(testObj, "Test"); watch5.Stop(); Console.WriteLine(watch5.Elapsed.ToString()); }
View Code

原文连接:http://www.cnblogs.com/JeffreyZhao/archive/2008/11/24/1338682.html

 

结束语:优化反射,通常都是经过建立委托来对其进行优化。三种生成委托的方法,我我的最喜欢表达式树,理由:直接。思路直接,实现直接。其实处理反射,还有一种运行时处理方法---dynamic。最后,感谢你们的耐心阅读。测试代码下载

相关文章
相关标签/搜索