zoukankan      html  css  js  c++  java
  • .NET反射的优化

    写在前面

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

    反射优化简介

    我们知道反射与直接调用相比性能要慢很多,因此本篇主要针对如何对反射进行优化。 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性、字段)。

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

    Emit创建委托

    Emit创建委托,主要靠自己编写IL代码。操作IL代码:难度高,而且不易于阅读。所以我对这种方法并不感冒。下面将直接给出代码,在代码中有对应的注释,各位有兴趣可以研究研究。

    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(如果知道具体的类型,又何必用反射呢?),然后对其进行操作。所以支持参数是通用类型是必须的。

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

    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。最后,感谢大家的耐心阅读。测试代码下载

  • 相关阅读:
    P1456 Monkey King
    P3377 【模板】左偏树(可并堆)
    P1074 靶形数独
    P1120 小木棍
    P5490 【模板】扫描线
    糖糖别胡说,我真的不是签到题目
    最长公共子序列
    最长上升子序列
    数的三次方根
    地、颜色、魔法(dfs)
  • 原文地址:https://www.cnblogs.com/color-wolf/p/7620406.html
Copyright © 2011-2022 走看看