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

 



推荐阅读
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • C#datatable序列化后整数带有小数点或者小数点变成整数原来datatable的列有个datatype属性,可以指定为int类型或者decimal类型的,如果指定int类型, ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了如何将CIM_DateTime解析为.Net DateTime,并分享了解析过程中可能遇到的问题和解决方法。通过使用DateTime.ParseExact方法和适当的格式字符串,可以成功解析CIM_DateTime字符串。同时还提供了关于WMI和字符串格式的相关信息。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 可空类型可空类型主要用于参数类型声明和函数返回值声明。主要的两种形式如下: ... [详细]
  • 人脸检测 pyqt+opencv+dlib
    一、实验目标绘制PyQT界面,调用摄像头显示人脸信息。在界面中,用户通过点击不同的按键可以实现多种功能:打开和关闭摄像头, ... [详细]
  • 浅谈Java8 的foreach跳出循环break/return_java
    这篇文章主要介绍了Java8的foreach跳出循环breakreturn,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完 ... [详细]
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社区 版权所有