写在前面

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);
}
}

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

后来我了解到,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);

针对反射,及程序运行时得到对应的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 > )
      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 = ; 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 = ; 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 = ; i < count; i++)
setter4.Set(testObj, "Test"); watch5.Stop();
Console.WriteLine(watch5.Elapsed.ToString());
}

结论: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 = ; 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 = ; 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 = ; i < count; i++)
setter4(testObj, "Test"); watch5.Stop();
Console.WriteLine(watch5.Elapsed.ToString());
}

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

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

.NET反射的优化的更多相关文章

  1. C# 之 反射性能优化3

    阅读目录 开始 用Delegate优化反射的缺点 用Delegate优化反射的优点 用CodeDOM优化反射的优点 如何用好CodeDOM? 用CodeDOM优化反射的缺点 能不能不使用委托? 根据反 ...

  2. C# 之 反射性能优化2

    问题回顾 在上篇博客中,我介绍了优化反射的第一个步骤:用委托调用代替直接反射调用. 然而,那只是反射优化过程的开始,因为新的问题出现了:如何保存大量的委托? 如果我们将委托保存在字典集合中,会发现这种 ...

  3. C# 之 反射性能优化1

    反射是一种很重要的技术,然而它与直接调用相比性能要慢很多,因此如何优化反射性能也就成为一个不得不面对的问题. 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性. ...

  4. 深入分析Java反射(八)-优化反射调用性能

    Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行Deb ...

  5. Java使用反射机制优化工厂方法

    我先举个例子,有一个接口People,这个接口有一个方法: package com.wjy.reflect; public interface People { public abstract voi ...

  6. 利用Java反射机制优化简单工厂设计模式

    之前项目有个需求,审批流程的时候要根据配置发送信息:发送短信.发送邮件.当时看到这个就想到要用工厂模式,为什么要用工厂模式呢?用工厂模式进行大型项目的开发,可以很好的进行项目并行开发.就是一个程序员和 ...

  7. 泛型,注解,反射配合优化BaseDao的猜想

    package test; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.la ...

  8. .NET面试题系列[6] - 反射

    反射 - 定义,实例与优化 在面试中,通常会考察反射的定义(操作元数据),可以用反射做什么(获得程序集及其各个部件),反射有什么使用场景(ORM,序列化,反序列化,值类型比较等).如果答得好,还可能会 ...

  9. Loogn.OrmLite映射优化记录

    大家对ORM效率的争议多半在映射性能方面.自己的ORMLite也是如此,经过前段时间的折腾,已经找不出一个简单的方法再提升一下这部分的方法了.在此把优化涉及的几点记录一下. 注:用于性能测试的Code ...

随机推荐

  1. 007-Redi-命令-脚本命令、链接命令、服务器命令、事务、HyperLogLog

    Redis 脚本命令 下表列出了 redis 脚本常用命令: 序号 命令及描述 1 EVAL script numkeys key [key ...] arg [arg ...] 执行 Lua 脚本. ...

  2. VMware Pro v14.0.0 官方版本及激活密钥

    热门虚拟机软件VMware Workstation Pro 14.0 全新版本发布,此次更新了诸多客户机操作系统版本,另外完全兼容Wind10创建者更新支持.12.0之后属于大型更新,专门为Win10 ...

  3. 浅谈远程登录时,ssh的加密原理

    SSH:Secure Shell,是一种网络安全协议,主要用于登录远程计算机的加密过程. 登录方式主要有两种: 1.基于用户密码的登录方式:   加密原理:   当服务器知道用户请求登录时,服务器会把 ...

  4. 在ASP.NET Web Application中通过SOAP协议调用Bing搜索服务

    本文介绍了如何在ASP.NET Web Application中将Bing搜索作为Web Service来使用,并通过HTTP的SOAP协议在ASP.NET Web Application中调用Bin ...

  5. testng入门教程12 TestNG执行多线程测试

    testng入门教程 TestNG执行多线程测试 testng入门教程 TestNG执行多线程测试 并行(多线程)技术在软件术语里被定义为软件.操作系统或者程序可以并行地执行另外一段程序中多个部分或者 ...

  6. background 背景图片 在IE8中不显示解决方法

    我给ul加了一个背景图片 background 火狐 ie9 ch都显示.唯独在IE8中不显示 之前的样式代码 background: url( rgba(, , , ); 在ie8中改成 backg ...

  7. myeclipse自带的数据库查看文件

    jdbc:mysql://localhost:3306/videocms?useUnicode=true&characterEncoding=utf8

  8. 初次使用git上传代码(转)

    转自 http://www.cnblogs.com/cxk1995/p/5800196.html 首先你需要一个github账号,所有还没有的话先去注册吧! https://github.com/ 我 ...

  9. C# NPOI 操作excel

    转载的文章,方便自己查看. 一.下载NPOI:http://down.gougou.com/down?cid=DAEA322D9D7F934B898077FB01C3A8CB02A746E6 二.项目 ...

  10. Integer类之成员变量

    一.一共11个成员变量. 二.详情介绍. 1.value值.这个是Integer类的唯一标志.最重要的实例属性. 2.最小值和最大值常量.注意,计算机里面是以补码形式保存的,因此用十六进制时,给的数据 ...