热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

玩转动态编译高级篇:三,实例属性的读取与设置

实例属性的读取先来回顾下静态属性读取的IL代码:.methodpublichidebysiginstancestringAAA()cilmanaged{.maxstack8L_0
  • 实例属性的读取

先来回顾下静态属性读取的IL代码:

.method public hidebysig instance string AAA() cil managed
{
.maxstack 8L_0000: call string blqw.IL.Demo.Program/MyClass::get_Name()L_0005: ret
}

string AAA()
{
return MyClass.Name;
}

C#代码

再来看下读取实例属性的IL代码

.method private hidebysig instance string AAA(class blqw.IL.Demo.Program/MyClass my) cil managed
{
.maxstack 8L_0000: ldarg.0 L_0001: callvirt instance string blqw.IL.Demo.Program/MyClass::get_Name()L_0006: ret
}

string AAA(MyClass my)
{
return my.Name;
}

C#代码

区别很明显,多个一个指令ldarg.0 ,并且指令有所区别

// 将索引为 0 的参数加载到计算堆栈上。
public static readonly OpCode Ldarg_0;

操作实例方法和操作静态方法不同,静态方法不需要任何额外的参数,而实例方法必须要提供一个参数,这个参数指示操作的实例对象

转换成C#代码就是这样的

public static Funcstring> ILTest()
{
var type = typeof(MyClass);var prop = type.GetProperty("Name");//反射属性var dm = new DynamicMethod("", typeof(string), new[] { typeof(MyClass) }, type);var il = dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Callvirt, prop.GetGetMethod());il.Emit(OpCodes.Ret);return (Funcstring>)dm.CreateDelegate(typeof(Funcstring>));
}

  • 实例属性的设置

IL代码

.method private hidebysig instance void AAA(class blqw.IL.Demo.Program/MyClass my, string name) cil managed
{
.maxstack 8L_0000: ldarg.0 L_0001: ldarg.1 L_0002: callvirt instance void blqw.IL.Demo.Program/MyClass::set_Name(string)L_0007: ret
}

好吧,又多了一个参数,不过这个是显而易见的,既然你要设置值,总要把值当作参数传递进去吧

对应C#代码如下:

public static Actionstring> ILTest()
{
var type = typeof(MyClass);var prop = type.GetProperty("Name");//反射属性var dm = new DynamicMethod("", null, new[] { typeof(MyClass), typeof(string) }, type);var il = dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);il.Emit(OpCodes.Callvirt, prop.GetSetMethod());il.Emit(OpCodes.Ret);return (Actionstring>)dm.CreateDelegate(typeof(Actionstring>));
}

重复来重复去都是这几样东西了..有些无聊了吧...

 那么接下来就做一些实际情况下的应用了

  • 实际应用

这次我要举起来的栗子就是DataTable转模型对象

不过在这之前,我要对现有的方法进行一些调整

public delegate void PropertySetter(object instance, object value);
public static PropertySetter CreateSetter(PropertyInfo property)
{
var type = property.DeclaringType;var dm = new DynamicMethod("", null, new[] { typeof(object), typeof(object) }, type);//=== IL ===var il = dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);if (property.PropertyType.IsValueType)//判断属性类型是否是值类型
{il.Emit(OpCodes.Unbox,property.PropertyType);//如果是值类型就拆箱
}else{il.Emit(OpCodes.Castclass, property.PropertyType);//否则强转
}il.Emit(OpCodes.Callvirt, property.GetSetMethod());il.Emit(OpCodes.Ret);//=== IL ===return (PropertySetter)dm.CreateDelegate(typeof(PropertySetter));
}

修改的地方不是很多,应该不难理解,关于类型转换部分,请参考上一篇

现在我可以很方便的通过这个方法创建一个任意实例属性的Set方法委托

接下来我需要一个的新的类

public class ObjectProperty
{
public PropertyInfo Info { get; set; }public PropertySetter Setter { get; set; }
}

这个类包含一个属性和这个属性的Set方法委托

在接下来我需要一个方法,把任意一个类中的所有公开的实例属性,转换成ObjectProperty集合

static readonly Dictionary<Type, ObjectProperty[]> Cache &#61; new Dictionary<Type, ObjectProperty[]>();public static ObjectProperty[] GetProperties(Type type)
{ObjectProperty[] arr;
if (Cache.TryGetValue(type, out arr))//优先从缓存中获取
{return arr;}PropertyInfo[] ps &#61; type.GetProperties(); arr &#61; new ObjectProperty[ps.Length];for (int i &#61; 0; i ){ObjectProperty op &#61; new ObjectProperty();op.Info &#61; ps[i];op.Setter &#61; CreateSetter(op.Info); //之前定义的方法arr[i] &#61; op;}Cache.Add(type, arr); //加入缓存return arr;
}

 把他们整合起来

public class ObjectProperty
{
///

属性信息/// public PropertyInfo Info { get; set; }/// Set方法委托/// public PropertySetter Setter { get; set; }//缓存static readonly Dictionary Cache &#61; new Dictionary();/// 获取一个类中的所有公开实例属性和它们的Set方法委托/// public static ObjectProperty[] GetProperties(Type type){ObjectProperty[] arr;if (Cache.TryGetValue(type, out arr))//优先从缓存中获取
{return arr;}PropertyInfo[] ps &#61; type.GetProperties();arr &#61; new ObjectProperty[ps.Length];for (int i &#61; 0; i ){ObjectProperty op &#61; new ObjectProperty();op.Info &#61; ps[i];op.Setter &#61; CreateSetter(op.Info); //之前定义的方法arr[i] &#61; op;}Cache.Add(type, arr); //加入缓存return arr;}/// 创建指定属性的Set方法委托/// public static PropertySetter CreateSetter(PropertyInfo property){var type &#61; property.DeclaringType;var dm &#61; new DynamicMethod("", null, new[] { typeof(object), typeof(object) }, type);//&#61;&#61;&#61; IL &#61;&#61;&#61;var il &#61; dm.GetILGenerator();il.Emit(OpCodes.Ldarg_0);il.Emit(OpCodes.Ldarg_1);if (property.PropertyType.IsValueType)//判断属性类型是否是值类型
{il.Emit(OpCodes.Unbox, property.PropertyType);//如果是值类型就拆箱
}else{il.Emit(OpCodes.Castclass, property.PropertyType);//否则强转
}il.Emit(OpCodes.Callvirt, property.GetSetMethod());il.Emit(OpCodes.Ret);//&#61;&#61;&#61; IL &#61;&#61;&#61;return (PropertySetter)dm.CreateDelegate(typeof(PropertySetter));}
}
C#代码

现在就可以写出一个将DataTable转为实体类的方法了

public static List ConvertToModels(DataSet ds)where T : new()
{
var prop &#61; ObjectProperty.GetProperties(typeof(T));List list &#61; new List(ds.Tables[0].Rows.Count);var cols &#61; ds.Tables[0].Columns;foreach (DataRow row in ds.Tables[0].Rows){T m &#61; new T();foreach (var p in prop){if (cols.Contains(p.Info.Name)){var val &#61; row[p.Info.Name];if ((val is DBNull) &#61;&#61; false){p.Setter(m, val);}}}list.Add(m);}return list;
}

好了,我现在模拟出一个DataSet和一个实体类来测试下

public class User
{
public User(){}public int Id { get; set; }public string Name { get; set; }public bool Sex { get; set; }public Guid Uid { get; set; }public DateTime Time { get; set; }public string SexText{get{return Sex ? "" : "";}set{Sex &#61; (value &#61;&#61; "");}}
}
//模拟方法
static public DataSet GetDataSet(string sql)
{DataTable table
&#61; new DataTable("User");table.Columns.Add("Id", typeof(int));table.Columns.Add("Name", typeof(string));table.Columns.Add("Sex", typeof(bool));table.Columns.Add("Uid", typeof(Guid));table.Columns.Add("Time", typeof(DateTime));table.Columns.Add("多出来的属性", typeof(string));for (int i &#61; 0; i <20; i&#43;&#43;){table.Rows.Add(i, "blqw" &#43; i, true, Guid.NewGuid(), DateTime.Now, "多余的");}DataSet ds &#61; new DataSet();ds.Tables.Add(table);return ds;
}

C#代码

 

  • 反射和IL

就功能上来说IL可以做的,反射都可以做.基本上IL的操作指令很多参数都是需要用到反射对象的

那么我们为什么要选择麻烦的IL,而不直接用反射呢,答案就是性能

就拿上面的栗子来说,如果我们用反射来实现的话是这样的

static public List ConvertToModels2(DataSet ds)where T : new()
{
var prop &#61; ObjectProperty.GetProperties(typeof(T));List list &#61; new List(ds.Tables[0].Rows.Count);var cols &#61; ds.Tables[0].Columns;foreach (DataRow row in ds.Tables[0].Rows){T m &#61; new T();foreach (var p in prop){if (cols.Contains(p.Info.Name)){var val &#61; row[p.Info.Name];if (Convert.IsDBNull(val) &#61;&#61; false){p.Info.SetValue(m, val, null);//这里直接用反射的SetValue
}}}list.Add(m);}return list;
}

反射代码

 

这只是构造10000个只有5个属性的实体类而已

因为先运行的是动态编译IL的测试,所以缓存什么的都已经在这个时候建好了,下面反射只是调用缓存

如果这个测试还看不出太大区别的话,那就看下直接对比Set部分的性能

一般来说是6倍左右的性能差异

 



推荐阅读
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
  • 本文详细介绍了如何利用 Bootstrap Table 实现数据展示与操作,包括数据加载、表格配置及前后端交互等关键步骤。 ... [详细]
  • 在将 Android Studio 从 3.0 升级到 3.1 版本后,遇到项目无法正常编译的问题,具体错误信息为:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDemoProductDebugResources'。 ... [详细]
  • 使用QT构建基础串口辅助工具
    本文详细介绍了如何利用QT框架创建一个简易的串口助手应用程序,包括项目的建立、界面设计与编程实现、运行测试以及最终的应用程序打包。 ... [详细]
  • 原文地址:https:blog.csdn.netqq_35361471articledetails84715491原文地址:https:blog.cs ... [详细]
  • 视觉Transformer综述
    本文综述了视觉Transformer在计算机视觉领域的应用,从原始Transformer出发,详细介绍了其在图像分类、目标检测和图像分割等任务中的最新进展。文章不仅涵盖了基础的Transformer架构,还深入探讨了各类增强版Transformer模型的设计思路和技术细节。 ... [详细]
  • 基于SSM框架的在线考试系统:随机组卷功能详解
    本文深入探讨了基于SSM(Spring, Spring MVC, MyBatis)框架构建的在线考试系统中,随机组卷功能的设计与实现方法。 ... [详细]
  • 本文详细介绍了如何在最新版本的Xcode中重命名iOS项目,包括项目名称、应用名称及相关的文件夹和配置文件。通过本文,开发者可以轻松完成项目的重命名工作。 ... [详细]
  • td{border:1pxsolid#808080;}参考:和FMX相关的类(表)TFmxObjectIFreeNotification ... [详细]
  • 使用TabActivity实现Android顶部选项卡功能
    本文介绍如何通过继承TabActivity来创建Android应用中的顶部选项卡。通过简单的步骤,您可以轻松地添加多个选项卡,并实现基本的界面切换功能。 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
  • 小编给大家分享一下Vue3中如何提高开发效率,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获, ... [详细]
  • 本文详细介绍了`android.os.Binder.getCallingPid()`方法的功能和应用场景,并提供了多个实际的代码示例。通过这些示例,开发者可以更好地理解如何在不同的开发场景中使用该方法。 ... [详细]
author-avatar
鲁弗斯ll
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有