.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);
}
}
运行结果:(测试代码我会在文章末尾给出)
后来我了解到,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反射的优化的更多相关文章
- C# 之 反射性能优化3
阅读目录 开始 用Delegate优化反射的缺点 用Delegate优化反射的优点 用CodeDOM优化反射的优点 如何用好CodeDOM? 用CodeDOM优化反射的缺点 能不能不使用委托? 根据反 ...
- C# 之 反射性能优化2
问题回顾 在上篇博客中,我介绍了优化反射的第一个步骤:用委托调用代替直接反射调用. 然而,那只是反射优化过程的开始,因为新的问题出现了:如何保存大量的委托? 如果我们将委托保存在字典集合中,会发现这种 ...
- C# 之 反射性能优化1
反射是一种很重要的技术,然而它与直接调用相比性能要慢很多,因此如何优化反射性能也就成为一个不得不面对的问题. 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性. ...
- 深入分析Java反射(八)-优化反射调用性能
Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行Deb ...
- Java使用反射机制优化工厂方法
我先举个例子,有一个接口People,这个接口有一个方法: package com.wjy.reflect; public interface People { public abstract voi ...
- 利用Java反射机制优化简单工厂设计模式
之前项目有个需求,审批流程的时候要根据配置发送信息:发送短信.发送邮件.当时看到这个就想到要用工厂模式,为什么要用工厂模式呢?用工厂模式进行大型项目的开发,可以很好的进行项目并行开发.就是一个程序员和 ...
- 泛型,注解,反射配合优化BaseDao的猜想
package test; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.la ...
- .NET面试题系列[6] - 反射
反射 - 定义,实例与优化 在面试中,通常会考察反射的定义(操作元数据),可以用反射做什么(获得程序集及其各个部件),反射有什么使用场景(ORM,序列化,反序列化,值类型比较等).如果答得好,还可能会 ...
- Loogn.OrmLite映射优化记录
大家对ORM效率的争议多半在映射性能方面.自己的ORMLite也是如此,经过前段时间的折腾,已经找不出一个简单的方法再提升一下这部分的方法了.在此把优化涉及的几点记录一下. 注:用于性能测试的Code ...
随机推荐
- 使用对象作为hashMap的键,需要覆盖hashcode和equals方法
1:HashMap可以存放键值对,如果要以对象(自己创建的类等)作为键,实际上是以对象的散列值(以hashCode方法计算得到)作为键.hashCode计算的hash值默认是对象的地址值. 这样就会忽 ...
- 深入理解python之二——python列表和元组
从一开始学习python的时候,很多人就听到的是元组和列表差不多,区别就是元组不可以改变,列表可以改变. 从数据结构来说,这两者都应当属于数组,元组属于静态的数组,而列表属于动态数组.稍后再内存的分配 ...
- [LeetCode] 64. Minimum Path Sum_Medium tag: Dynamic Programming
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which ...
- [LeetCode] 680. Valid Palindrome II_Easy tag: Two Pointers
Given a non-empty string s, you may delete at most one character. Judge whether you can make it a pa ...
- nodejs加WebSocket,聊天工具
1.WebSocket必须要与服务器连接,所以这里采用node起服务,这里用到了ws,,也有人用nodejs-websocket 2.首先 npm install ws 3.新建一个server.js ...
- jmeter 正则表达式提取器的使用(提取第一个匹配结果)
原文地址https://www.cnblogs.com/xueli/p/7405258.html?utm_source=itdadao&utm_medium=referral 正则表达式的用处 ...
- 使用Fiddler手机抓包https-----重要
Fiddler不仅可以对手机进行抓包,还可以抓取别的电脑的请求包,今天就想讲一讲使用Fiddler手机抓包! 使用Fiddler手机抓包有两个条件: 一:手机连的网络或WiFi必须和电脑(使用fidd ...
- redis桌面管理工具 redis-desktop-manager使用指南(转)
版权声明:转自 http://blog.csdn.net/li396864285/article/details/54629898 概要:一款好用的Redis桌面管理工具,支持命令控制台操作, ...
- windows上mysql的配置文件my.ini内容
# Other default tuning values# MySQL Server Instance Configuration File# --------------------------- ...
- Object-C-内存管理 对象生命周期
autoreleasepool 池子被销毁的时候被标记 autorelease 的对象调用一次release Person *p2=[[[Person alloc]init]autorelease]; ...