用Emit技术替代反射
之前在上篇博客说到用表达式来替代反射机制,可以获得较高的性能提升。这篇我们来说说用Emit技术来替代反射。
System.Reflection.Emit命名空间类可用于动态发出Microsoft中间语言(MSIL)代码,以便生成的代码可以直接执行。反射也用于获取有关类及其成员的信息。换句话说,反射是一种技术,允许您检查描述类型及其成员的元数据,你可能以编程方式访问过组件对象模型类型库, .NET中的反射非常相似,但功能强大且易于使用。使用.NET编译器编译源文件时,编译器会产生源文件中语句中的MSIL代码以及描述文件中定义的类型的元数据。正是这个元数据,.NET中的反射API使你能够检查。在这个System.Reflection命名空间中,有一些类可用于帮助访问程序中固有的结构,比如类、类型、字段、结构、枚举、成员和方法。例如,您使用Type类来标识所反映的类的类型,FieldInfo类表示结构或枚举的字段。MemberInfo类表示反射类的成员,并使用MethodInfo类表示反射类的方法。PrimeRealFipe类表示反射类中的方法的参数。
使用System.Reflection.Emit命名空间类在可以编译时创建代码,但前提是必须懂IL代码。(本文不做IL代码详解,因为我也不会。。。)事实上,你实际编写的是就是幕后的MSIL本身。你可以使用反射在内存中定义程序集,为该程序集创建类/模块,然后为该模块创建其他模块成员和新类型。你同样也可以使用Emit来构造程序集。Reflection.Emit是一个强大的命名空间,我们可以在运行时动态地发出瞬态和持久化程序集。Reflection.Emit产生一个低级,语言中立的MSIL。通常,我们通过将源代码保存到磁盘然后编译该源代码来创建程序集,然后我们调用我们需要从该程序集中使用的类的方法,该程序集是在磁盘上编译的。但是你可以想象,这涉及额外的磁盘写入和读取工作!使用反射生成代码,我们可以省略此开销并立即将操作代码直接发送到内存中。反射发射只不过是直接在代码中编写任何汇编代码,然后即时调用生成的代码。这也并不是说反射效率就是高,因为在运行期产生指令也是需要时间,各有优缺点。
System.Reflection.Emit命名空间提供用户动态创建.exe文件所需的类。它的类允许编译器或工具发出元数据和MSIL。因此,您可以动态地在磁盘上创建.exe文件,就像运行代码,保存代码并调用编译器来编译代码一样。大多数情况下,您需要此功能和此命名空间用于自定义脚本引擎和编译器。
- AssemblyBuilder类是在运行时发出代码并具有创建动态模块的方法的任何应用程序的起点。
- ModuleBuilder类用作在运行时向动态程序集添加类和结构等类型的起点。
本文通过Emit技术来提高后期绑定对象的性能,尽管您不能像硬绑定那样快速执行调用,但执行效果会比在运行时产生代码在绑定更好。代码基本与前篇博客用lambda表达式树替代反射基本一样,核心代码替换过来即可,如下:
public class PropertyEmit
{ private PropertySetterEmit setter;
private PropertyGetterEmit getter;
public String PropertyName { get; private set; }
public PropertyInfo Info { get; private set; } public PropertyEmit(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw new ArgumentNullException("属性不能为空");
} if (propertyInfo.CanWrite)
{
setter = new PropertySetterEmit(propertyInfo);
} if (propertyInfo.CanRead)
{
getter = new PropertyGetterEmit(propertyInfo);
} this.PropertyName = propertyInfo.Name;
this.Info = propertyInfo;
} /// <summary>
/// 属性赋值操作(Emit技术)
/// </summary>
/// <param name="instance"></param>
/// <param name="value"></param>
public void SetValue(Object instance,Object value)
{
this.setter?.Invoke(instance, value);
} /// <summary>
/// 属性取值操作(Emit技术)
/// </summary>
/// <param name="instance"></param>
/// <returns></returns>
public Object GetValue(Object instance)
{
return this.getter?.Invoke(instance);
} private static readonly ConcurrentDictionary<Type, PropertyEmit[]> securityCache = new ConcurrentDictionary<Type, PropertyEmit[]>(); /// <summary>
/// 获取对象属性
/// </summary>
/// <param name="type">对象类型</param>
/// <returns></returns>
public static PropertyEmit[] GetProperties(Type type)
{
return securityCache.GetOrAdd(type, t => t.GetProperties().Select(p => new PropertyEmit(p)).ToArray());
}
} /// <summary>
/// Emit 动态构造 Get方法
/// </summary>
public class PropertyGetterEmit
{ private readonly Func<Object, Object> getter;
public PropertyGetterEmit(PropertyInfo propertyInfo)
{
//Objcet value = Obj.GetValue(Object instance);
if (propertyInfo == null)
{
throw new ArgumentNullException("propertyInfo");
}
this.getter = CreateGetterEmit(propertyInfo); } public Object Invoke(Object instance)
{
return getter?.Invoke(instance);
} private Func<Object, Object> CreateGetterEmit(PropertyInfo property)
{
if (property == null)
throw new ArgumentNullException("property"); MethodInfo getMethod = property.GetGetMethod(true); DynamicMethod dm = new DynamicMethod("PropertyGetter", typeof(Object),
new Type[] { typeof(Object) },
property.DeclaringType, true); ILGenerator il = dm.GetILGenerator(); if (!getMethod.IsStatic)
{
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Callvirt, getMethod, null);
}
else
il.EmitCall(OpCodes.Call, getMethod, null); if (property.PropertyType.IsValueType)
il.Emit(OpCodes.Box, property.PropertyType);
il.Emit(OpCodes.Ret);
return (Func<Object, Object>)dm.CreateDelegate(typeof(Func<Object, Object>));
}
} /// <summary>
/// Emit动态构造Set方法
/// </summary>
public class PropertySetterEmit
{
private readonly Action<Object, Object> setFunc;
public PropertySetterEmit(PropertyInfo propertyInfo)
{
//Obj.Set(Object instance,Object value)
if (propertyInfo == null)
{
throw new ArgumentNullException("propertyInfo");
}
this.setFunc = CreatePropertySetter(propertyInfo); } private Action<Object, Object> CreatePropertySetter(PropertyInfo property)
{
if (property == null)
throw new ArgumentNullException("property"); MethodInfo setMethod = property.GetSetMethod(true); DynamicMethod dm = new DynamicMethod("PropertySetter", null,
new Type[] { typeof(Object), typeof(Object) }, property.DeclaringType, true); ILGenerator il = dm.GetILGenerator(); 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 (Action<Object, Object>)dm.CreateDelegate(typeof(Action<Object, Object>));
} private static void EmitCastToReference(ILGenerator il, Type type)
{
if (type.IsValueType)
il.Emit(OpCodes.Unbox_Any, type);
else
il.Emit(OpCodes.Castclass, type);
} public void Invoke(Object instance,Object value)
{
this.setFunc?.Invoke(instance, value);
}
}
与表达式一起对比,其测试代码如下:
Student student = new Student(); //学生对象,里面有一个Name属性
PropertyInfo propertyInfo = student.GetType().GetProperty(nameof(student.Name));
Property PropertyExp = new Property(propertyInfo);
PropertyEmit propertyEmit = new PropertyEmit(propertyInfo); Int32 loopCount = ; //执行次数
CodeTimer.Initialize(); //测试环境初始化 CodeTimer.Time("基础反射", loopCount, () => {
propertyInfo.SetValue(student, "Fode",null);
});
CodeTimer.Time("lambda表达式树", loopCount, () => {
PropertyExp.SetValue(student, "Fode");
});
CodeTimer.Time("Emit",loopCount,()=> {
propertyEmit.SetValue(student, "Fode");
});
CodeTimer.Time("直接赋值", loopCount, () => {
student.Name = "Fode";
});
Console.ReadKey();
测试效果图如下:表达式与Emit速度基本相同,将我上述的方法CreatePropertySetter改成静态会比表达式快一点。在使用的过程中,最好将其封装成一个静态泛型类缓存起来,一直new PropertyEmit这个对象反而效率会很低。代码下载。

文章结尾在分享几个我认为写得不错,可能对大家有帮助的文章:
C# 之 反射性能优化1
Emit常用Opcodes指令使用方法(含实例)
用Emit技术替代反射的更多相关文章
- 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截
程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构 .要想在之后的江湖历练中通关,数据结构必不可少. ...
- 用lambda表达式树替代反射
本节重点不讲反射机制,而是讲lambda表达式树来替代反射中常用的获取属性和方法,来达到相同的效果但却比反射高效. 每个人都知道,用反射调用一个方法或者对属性执行SetValue和GetValue操作 ...
- Emit技术使用实例及应用思路
System.Reflection.Emit提供了动态创建类并生成程序集的功能. 适用于.NET Framework 2.0及其以后的版本. 动态生成类在对于O/R Mapping来说有很大的作用,在 ...
- [SAP ABAP开发技术总结]反射,动态创建内表、结构、变量
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 什么是Emit,什么是反射,二者区别到底是什么?(转)
Emit的准确定义,我们看看微软给出的答案 System.Reflection.Emit 命名空间包含{ 允许编译器或工具发出元数据和发出 Microsoft 中间语言 (MSIL) ,并可选择在磁盘 ...
- 第十四篇 .NET高级技术之反射
两个现实中的例子:1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况.这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内发射B型超声波,当超声波遇到内脏壁的时 ...
- Java技术——Java反射机制分析
)生成动态代理. 2. Java反射API 反射API用来生成在当前Java虚拟机中的类.接口或者对象的信息. Class类:反射的核心类,可以获取类的属性,方法等内容信息. Field类:Java. ...
- Emit优化反射(属性的设置与获取)
在频繁的通过反射来设置和获取属性的值时是比较耗时的,本章通过Emit技术优化反射来提高获取和设置属性值的效率 一.实现代码: /// <summary> /// 设置器委托 /// < ...
- C# 反射与特性(十):EMIT 构建代码
目录 构建代码 1,程序集(Assembly) 2,模块(Module) 3,类型(Type) 4,DynamicMethod 定义方法与添加 IL 前面,本系列一共写了 九 篇关于反射和特性相关的文 ...
随机推荐
- linux历史命令
"忘记历史的Linux用户注定要输入很多信息.” 这也让强有力的历史命令(包括Bash shell的历史变体)不仅在援引之前执行命令而不需重新输入它们时有用,在调用其它很少用到的命令时也有用 ...
- 9、Django实战第9天:用户注册功能
今天完成的是用户注册功能... 首先把注册页面的前端文件register.html复制到templates目录下 编辑users.views.py,创建一个注册的类 class RegisterVie ...
- sed 概述
sed 是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送 ...
- 初步接触CERNVM
初步接触的来源是对ROOT数据分析工具的搜索,看到一个叫做Life as a Physicist的国外博客.知道了这个包含容器分发的软件,跟重要的是,这个欧洲核子中心开发的平台,对于我等科研人员是一大 ...
- [xsy1294]sub
给出一棵$N$个节点的无根树,节点$i$有权值$v_i$.现在有$M$次操作,操作有如下两种: $1\ x\ y$ 将节点$x$的权值$v_x$修改为$y$ $2$ 选择一个联通块(也可以不选择),使 ...
- 【动态规划】【spfa】【最短路】bzoj1003 [ZJOI2006]物流运输trans
预处理cost[a][b] 表示第a天到第b天用同一条线路的成本. 具体转移看代码. #include<cstdio> #include<algorithm> #include ...
- Spring入门程序-前端控制器配置器
1,处理器的第二种配置方式 <!--配置handler --> <bean id="/FirstController" class="com.songy ...
- Asp.Net MVC part1
路由简介在Global中注册了路由数据包括:默认Controller,默认Action,请求地址匹配路由规则 约定大于配置为了尽量少的配置,于是将常用的配置作为默认约定,如果不同则进行少量配置主要从存 ...
- Swift中混编OC第三方库
现在Swift的第三方库还比较少,有时候需要使用OC的第三方库,其实也是很容易的. 我们使用如下步骤: 1.新建的Swift项目,第一次创建OC文件时会询问是否生成 桥接头,选择是的话会生成一个桥 ...
- ajax请求不能下载文件(转载)
最近在做文件下载,后台写了个控制层,直接走进去应该就可以下载文件,各种文件图片,excel等 但是起初老是下载失败,并且弹出下面的乱码: 前台请求代码: $('#fileexcel').unbind( ...