• 实例属性的读取

先来回顾下静态属性读取的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 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;
}

把他们整合起来

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和一个实体类来测试下

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,而不直接用反射呢,答案就是性能

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

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倍左右的性能差异

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

  1. 玩转动态编译 - 高级篇:一,IL访问静态属性和字段

    IL介绍 通用中间语言(Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一种属于通用语言架构和.NET ...

  2. Pyhton类、实例属性的获取和设置

    由于python是一种动态语言,python类和属性的获取和设置非常灵活,本文主要介绍一下几个方面: 实例属性和类属性的绑定:动态绑定属性和方法:__slots__ 的使用:@property的使用 ...

  3. python学习高级篇(part7)--特殊属性和特殊方法

    学习笔记,仅供参考,有错必纠 文章目录 python 学习高级篇 特殊属性和特殊方法 获取对象的信息之特殊属性`__dict__` 获取对象的信息之反射 类对象的特殊方法`__len__()` pyt ...

  4. python学习高级篇(part1)--类属性

    学习笔记,仅供参考,有错必纠 学习资源:图解python 文章目录 类属性 举个例子 举个例子 类属性 类属性指的是类对象所绑定的属性. 绑定类属性(给类对象绑定属性)的方式有两种: 在类对象的内部( ...

  5. vue $ 符号(例如vm.$data vs vm.data):读取实例属性 vs 读取 data 数据

    vm.$ 读取实例中属性列表(第一层) vm.data 直接读取实例 data 数据属性中的数据值(第二层).等价于 vm.$data.data <script>var ok=new Vu ...

  6. android studio 编译高级篇-gradle多版本编译,定制任务

    导读 本文旨在介绍Gradle构建的一些高级功能,包含了多版本编译.定制任务等功能: 为相同的app构建多个版本 如果在Gradle的过程中添加定制的任务 如何使用android库module 3.构 ...

  7. mysql高级篇三:常用sql技巧

    文章目录 SQL执行顺序 正则表达式使用 MySQL 常用函数 SQL执行顺序 编写顺序 SELECT DISTINCT<select list> FROM<left_table&g ...

  8. 【Spring 基础篇三】属性注入与属性编辑器

         上篇我们了解了一下applicationContext.xml的两种注入方式,本篇我们来了解一下关于属性的注入以及操作.      在敲代码的过程中,我们很容易遇到这样的问题,比如一个Lis ...

  9. 谷粒商城-个人笔记(高级篇二)

    目录 二.商城业务-首页 1.整合thymeleaf渲染首页 1).在"gulimall-product"项目中导入前端代码: 2).渲染一级分类数据&&整合dev ...

最新文章

  1. kali linux安装wine32,永恒之蓝msf下 ms17_010 (64位kali下安装wine32)
  2. 电脑记事本_办公便签记事本
  3. Xamarin Andro教程搭建Xamarin Androidid开发环境(一)
  4. [问题解决]基于注解配置dubbo遇到ConnectionLoss for /dubbo/xxx问题解决
  5. 接待员如何向客人upsell_如何提升自我做好客户服务与管理?
  6. TCP/IP review之 静态路由
  7. ioca0中断 pic单片机_关于PIC单片机的一些经验总结
  8. 宏杉科技高端存储再获认可 成功中标国家电网集采
  9. vue相关插件及框架全家桶
  10. Linux升级glibc版本汉字乱码,glibc版本升级
  11. 恒流LED升压驱动芯片2.5V~24V输入【待机功耗低 电流精度高3%】惠海半导体H6911方案分析
  12. 金石无线服务器效果怎么样,天线效果不好?快来看看是不是这几个地方出了问题!...
  13. 如何在excel中输入身份证号
  14. 使用java压缩文件成zip——三种方式压缩文件速度对比
  15. LVGL 8.2图片缩放及旋转
  16. 读书到什么程度才能算融会贯通?
  17. 微信小程序学习(五)
  18. 视频监控SVAC安全控制简介
  19. 点亮一盏灯,温暖一个梦
  20. 從turtle海龜動畫學習Python-高中彈性課程1

热门文章

  1. 为什么用JS取不到cookie的值?解决方法如下!
  2. linux批量创建系统,linux系统批量创建用户
  3. 远程控制slam小车及pid调试PC与树莓派ssh链接时出现间歇性联通段开网络故障acailable I Destination Host Unreachable_然后5s后切换了一个地图
  4. springBoot跨域注解@CrossOrigin
  5. c++输出的值精确到小数点后5位_c/c++linux 2019最新阿里研发类面试题及答案分享...
  6. Druid monitor中SQL监控显示不出数据(已解决)
  7. 相对定位(HTML、CSS)
  8. python中int的用法归类
  9. 大一新生开学考计算机知识点,2018年大一新生入学考试科目及考试资料和内容解读...
  10. 基于模型与不基于模型的深度增强学习_CVPR2018: 基于时空模型无监督迁移学习的行人重识别...