之前在上篇博客说到用表达式来替代反射机制, 可以获得较高的性能提升. 这篇我们来说说用 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 文件, 就像运行代码, 保存代码并调用编译器来编译代码一样. 大多数情况下, 您需要此功能和此命名空间用于自定义脚本引擎和编译器.
Reflection.Emit 命名空间有许多可用于重要的的类. 以下是两个最重要的:
AssemblyBuilder 类是在运行时发出代码并具有创建动态模块的方法的任何应用程序的起点.
ModuleBuilder 类用作在运行时向动态程序集添加类和结构等类型的起点.
生成 MSIL 指令的 ILGenerator.OpCodes 类包括其所需字段中的所有 IL 指令. MSIL 是 CLR 或中间语言的基本汇编语言的无类型操作代码. 当您编写任何 C#代码并对其进行编译时, 它将首先转换为 MSIL. 然后, 当您在 MSIL 中调用程序集时, 它将以相应的机器语言进行转换和执行. 学习 MSIL 的最简单方法是反汇编您编译的简单代码. 您可以使用. NET SDK 实用程序之一 ILDasm.exe(IL 反汇编程序)在 Vs 插件库下载即可, 来反汇编任何已编译的. NET 代码.
本文通过 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 = 1000000; // 执行次数
- 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 指令使用方法(含实例)
从 IDataReader 中读取数据实体
来源: https://www.cnblogs.com/fode/p/10085407.html