玩转动态编译 - 高级篇:三,实例属性的读取与设置
- 实例属性的读取
先来回顾下静态属性读取的IL代码:
.method public hidebysig instance string AAA() cil managed {.maxstack 8L_0000: call string blqw.IL.Demo.Program/MyClass::get_Name()L_0005: ret }
![](/assets/blank.gif)
![](/assets/blank.gif)
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 }
![](/assets/blank.gif)
![](/assets/blank.gif)
string AAA(MyClass my) {return my.Name; }
C#代码
区别很明显,多个一个指令ldarg.0 ,并且指令有所区别
// 将索引为 0 的参数加载到计算堆栈上。 public static readonly OpCode Ldarg_0;
操作实例方法和操作静态方法不同,静态方法不需要任何额外的参数,而实例方法必须要提供一个参数,这个参数指示操作的实例对象
转换成C#代码就是这样的
public static Func<MyClass, string> 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 (Func<MyClass, string>)dm.CreateDelegate(typeof(Func<MyClass, string>)); }
- 实例属性的设置
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 Action<MyClass, string> 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 (Action<MyClass, string>)dm.CreateDelegate(typeof(Action<MyClass, string>)); }
重复来重复去都是这几样东西了..有些无聊了吧...
那么接下来就做一些实际情况下的应用了
- 实际应用
这次我要举起来的栗子就是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 = new Dictionary<Type, ObjectProperty[]>();public static ObjectProperty[] GetProperties(Type type) {ObjectProperty[] arr;if (Cache.TryGetValue(type, out arr))//优先从缓存中获取 {return arr;}PropertyInfo[] ps = type.GetProperties(); arr = new ObjectProperty[ps.Length];for (int i = 0; i < ps.Length; i++){ObjectProperty op = new ObjectProperty();op.Info = ps[i];op.Setter = CreateSetter(op.Info); //之前定义的方法arr[i] = op;}Cache.Add(type, arr); //加入缓存return arr; }
把他们整合起来
![](/assets/blank.gif)
![](/assets/blank.gif)
public class ObjectProperty {/// <summary> 属性信息/// </summary>public PropertyInfo Info { get; set; }/// <summary> Set方法委托/// </summary>public PropertySetter Setter { get; set; }//缓存static readonly Dictionary<Type, ObjectProperty[]> Cache = new Dictionary<Type, ObjectProperty[]>();/// <summary> 获取一个类中的所有公开实例属性和它们的Set方法委托/// </summary>public static ObjectProperty[] GetProperties(Type type){ObjectProperty[] arr;if (Cache.TryGetValue(type, out arr))//优先从缓存中获取 {return arr;}PropertyInfo[] ps = type.GetProperties();arr = new ObjectProperty[ps.Length];for (int i = 0; i < ps.Length; i++){ObjectProperty op = new ObjectProperty();op.Info = ps[i];op.Setter = CreateSetter(op.Info); //之前定义的方法arr[i] = op;}Cache.Add(type, arr); //加入缓存return arr;}/// <summary> 创建指定属性的Set方法委托/// </summary>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));} }
C#代码
现在就可以写出一个将DataTable转为实体类的方法了
public static List<T> ConvertToModels<T>(DataSet ds)where T : new() {var prop = ObjectProperty.GetProperties(typeof(T));List<T> list = new List<T>(ds.Tables[0].Rows.Count);var cols = ds.Tables[0].Columns;foreach (DataRow row in ds.Tables[0].Rows){T m = new T();foreach (var p in prop){if (cols.Contains(p.Info.Name)){var val = row[p.Info.Name];if ((val is DBNull) == false){p.Setter(m, val);}}}list.Add(m);}return list; }
好了,我现在模拟出一个DataSet和一个实体类来测试下
![](/assets/blank.gif)
![](/assets/blank.gif)
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 = (value == "男");}} } //模拟方法 static public DataSet GetDataSet(string sql) {DataTable table = 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 = 0; i < 20; i++){table.Rows.Add(i, "blqw" + i, true, Guid.NewGuid(), DateTime.Now, "多余的");}DataSet ds = new DataSet();ds.Tables.Add(table);return ds; }
C#代码
- 反射和IL
就功能上来说IL可以做的,反射都可以做.基本上IL的操作指令很多参数都是需要用到反射对象的
那么我们为什么要选择麻烦的IL,而不直接用反射呢,答案就是性能
就拿上面的栗子来说,如果我们用反射来实现的话是这样的
![](/assets/blank.gif)
![](/assets/blank.gif)
static public List<T> ConvertToModels2<T>(DataSet ds)where T : new() {var prop = ObjectProperty.GetProperties(typeof(T));List<T> list = new List<T>(ds.Tables[0].Rows.Count);var cols = ds.Tables[0].Columns;foreach (DataRow row in ds.Tables[0].Rows){T m = new T();foreach (var p in prop){if (cols.Contains(p.Info.Name)){var val = row[p.Info.Name];if (Convert.IsDBNull(val) == false){p.Info.SetValue(m, val, null);//这里直接用反射的SetValue }}}list.Add(m);}return list; }
反射代码
这只是构造10000个只有5个属性的实体类而已
因为先运行的是动态编译IL的测试,所以缓存什么的都已经在这个时候建好了,下面反射只是调用缓存
如果这个测试还看不出太大区别的话,那就看下直接对比Set部分的性能
一般来说是6倍左右的性能差异
玩转动态编译 - 高级篇:三,实例属性的读取与设置相关推荐
- 玩转动态编译 - 高级篇:一,IL访问静态属性和字段
IL介绍 通用中间语言(Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一种属于通用语言架构和.NET ...
- Pyhton类、实例属性的获取和设置
由于python是一种动态语言,python类和属性的获取和设置非常灵活,本文主要介绍一下几个方面: 实例属性和类属性的绑定:动态绑定属性和方法:__slots__ 的使用:@property的使用 ...
- python学习高级篇(part7)--特殊属性和特殊方法
学习笔记,仅供参考,有错必纠 文章目录 python 学习高级篇 特殊属性和特殊方法 获取对象的信息之特殊属性`__dict__` 获取对象的信息之反射 类对象的特殊方法`__len__()` pyt ...
- python学习高级篇(part1)--类属性
学习笔记,仅供参考,有错必纠 学习资源:图解python 文章目录 类属性 举个例子 举个例子 类属性 类属性指的是类对象所绑定的属性. 绑定类属性(给类对象绑定属性)的方式有两种: 在类对象的内部( ...
- vue $ 符号(例如vm.$data vs vm.data):读取实例属性 vs 读取 data 数据
vm.$ 读取实例中属性列表(第一层) vm.data 直接读取实例 data 数据属性中的数据值(第二层).等价于 vm.$data.data <script>var ok=new Vu ...
- android studio 编译高级篇-gradle多版本编译,定制任务
导读 本文旨在介绍Gradle构建的一些高级功能,包含了多版本编译.定制任务等功能: 为相同的app构建多个版本 如果在Gradle的过程中添加定制的任务 如何使用android库module 3.构 ...
- mysql高级篇三:常用sql技巧
文章目录 SQL执行顺序 正则表达式使用 MySQL 常用函数 SQL执行顺序 编写顺序 SELECT DISTINCT<select list> FROM<left_table&g ...
- 【Spring 基础篇三】属性注入与属性编辑器
上篇我们了解了一下applicationContext.xml的两种注入方式,本篇我们来了解一下关于属性的注入以及操作. 在敲代码的过程中,我们很容易遇到这样的问题,比如一个Lis ...
- 谷粒商城-个人笔记(高级篇二)
目录 二.商城业务-首页 1.整合thymeleaf渲染首页 1).在"gulimall-product"项目中导入前端代码: 2).渲染一级分类数据&&整合dev ...
最新文章
- kali linux安装wine32,永恒之蓝msf下 ms17_010 (64位kali下安装wine32)
- 电脑记事本_办公便签记事本
- Xamarin Andro教程搭建Xamarin Androidid开发环境(一)
- [问题解决]基于注解配置dubbo遇到ConnectionLoss for /dubbo/xxx问题解决
- 接待员如何向客人upsell_如何提升自我做好客户服务与管理?
- TCP/IP review之 静态路由
- ioca0中断 pic单片机_关于PIC单片机的一些经验总结
- 宏杉科技高端存储再获认可 成功中标国家电网集采
- vue相关插件及框架全家桶
- Linux升级glibc版本汉字乱码,glibc版本升级
- 恒流LED升压驱动芯片2.5V~24V输入【待机功耗低 电流精度高3%】惠海半导体H6911方案分析
- 金石无线服务器效果怎么样,天线效果不好?快来看看是不是这几个地方出了问题!...
- 如何在excel中输入身份证号
- 使用java压缩文件成zip——三种方式压缩文件速度对比
- LVGL 8.2图片缩放及旋转
- 读书到什么程度才能算融会贯通?
- 微信小程序学习(五)
- 视频监控SVAC安全控制简介
- 点亮一盏灯,温暖一个梦
- 從turtle海龜動畫學習Python-高中彈性課程1
热门文章
- 为什么用JS取不到cookie的值?解决方法如下!
- linux批量创建系统,linux系统批量创建用户
- 远程控制slam小车及pid调试PC与树莓派ssh链接时出现间歇性联通段开网络故障acailable I Destination Host Unreachable_然后5s后切换了一个地图
- springBoot跨域注解@CrossOrigin
- c++输出的值精确到小数点后5位_c/c++linux 2019最新阿里研发类面试题及答案分享...
- Druid monitor中SQL监控显示不出数据(已解决)
- 相对定位(HTML、CSS)
- python中int的用法归类
- 大一新生开学考计算机知识点,2018年大一新生入学考试科目及考试资料和内容解读...
- 基于模型与不基于模型的深度增强学习_CVPR2018: 基于时空模型无监督迁移学习的行人重识别...