- 高级篇:二,IL设置静态属性,字段和类型转换

  • 静态属性赋值

先来看 Reflector反射出的IL源码(感谢Moen的提示),这次用 Release模式编译,去掉那些无用的辅助指令

public void AAA(string s)
{
MyClass.Name = s;
}
.method public hidebysig instance void AAA(string s) cil managed
{
.maxstack 8
//L_0000: ldarg.1 //这个是真正反射出的内容,但是理论上 这里应该是ldarg.0
//下面一行是我特意修改的,上面的现象我无法解释,请知道的朋友也可以告知一二
L_0000: ldarg.0//参数0,也就是string s这个参数推送的堆栈上
L_0001: stsfld string blqw.IL.Demo.MyClass::Name//使用当前堆栈上最近的一个参数,执行静态字段赋值指令,
L_0006: ret //方法结束
}

  • 小贴士:

每个操作系统都会从堆栈中获取指定数量的参数,比如上一篇中的静态字段/属性取值操作,这个操作不需要用到任何参数,比如执行一个方法,这个方法签名有几个参数,就需要提供几个参数,再比如执行一次比较,需要提供2个参数等等,每个操作需要的参数都是事先就指定好的

再来看C#中的代码:

public static Action<string> ILTest()
{
var dm = new DynamicMethod("", null, new[] { typeof(string) }, typeof(MyClass));
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stsfld, typeof(MyClass).GetField("Name"));
il.Emit(OpCodes.Ret);
return (Action<string>)dm.CreateDelegate(typeof(Action<string>));
}

进行一些测试

测试方法

测试结果

静态属性赋值

public static Action<string> ILTest()
{
var dm = new DynamicMethod("", null, new[] { typeof(string) }, typeof(MyClass));
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(MyClass).GetProperty("Name").GetSetMethod());
il.Emit(OpCodes.Ret);
return (Action<string>)dm.CreateDelegate(typeof(Action<string>));
}
  • 类型转换

之前看的栗子都是方法参数类型和属性字段类型相同的情况下的赋值,那么如果类型需要转换呢?

比如这样: 将MyClass的Name属性的类型改为int

class MyClass
{
public static int Name { get; set; }
}

然后把要生成的方法改为传入Object类型的参数进行复制,但是在调用的时候依然传入int

static void Main(string[] args)
{
var act = ILTest();
act(222);
} public static Action<object> ILTest()
{
var dm = new DynamicMethod("", null, new[] { typeof(object) }, typeof(MyClass));
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(MyClass).GetProperty("Name").GetSetMethod());
il.Emit(OpCodes.Ret);
return (Action<object>)dm.CreateDelegate(typeof(Action<object>));
}

注意加了下划线的几个地方

再来看运行结果

测试代码

111
50310368
请按任意键继续. . .

虽然程序没有抛出异常,但是结果确错了...

其实这个时候IL相当于生成了一个这样的方法

public void AAA(object i)
{
MyClass.Name = i; //编译器在这里就报错了
}

如果这个方法你是在VS中写的,那么在编译的时候编译器就告诉你这样写是错误,并且中断你的程序编译

编译器希望你改成这样

public void AAA(object i)
{
MyClass.Name = (int)i;//增加类型转换
}

虽然这样写,在传入参数错误的情况下也会抛出异常,但是这是运行时的错误,就跟程序本身没关系了

所以IL代码也需要加一个转换的操作

public static Action<object> ILTest()
{
var dm = new DynamicMethod("", null, new[] { typeof(object) }, typeof(MyClass));
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Unbox_Any, typeof(int));//加上这一句拆箱操作
il.Emit(OpCodes.Call, typeof(MyClass).GetProperty("Name").GetSetMethod());
il.Emit(OpCodes.Ret);
return (Action<object>)dm.CreateDelegate(typeof(Action<object>));
}

再来看运行结果

好了,这回就对了

  • 重点:

别看我们在C#代码中类型转换操作都是一样的(Type)Object,但是在IL中值类型和引用类型会被编译成不同的指令,原因就是大家都知道的,值类型和引用类型的储存方式和位置不同引起的

object转值类型被称为拆箱,对应指令是OpCodes.Unbox_AnyOpCodes.Unbox(我不知道区别,一般都是用OpCodes.Unbox_Any)

object转引用类型就是强转,对应指令是OpCodes.Castclass

上面2个指令都需要提供一个指令参数,如il.Emit(OpCodes.Castclass, typeof(int));表示拆箱后的类型,或强转后的类型

值类型转为object称为装箱,对应指令是OpCodes.Box,不需要提供指令参数

引用类型转object,也是强转,对应指令是OpCodes.Castclass

所以其实我们昨天的栗子中有一部分也是需要改正的,在获取静态属性和字段值的时候,返回值的object,但是IL中没有进行转换

昨天的栗子

修改后就应该变成这样:

public static Func<object> ILTest()
{
var dm = new DynamicMethod("", typeof(object), null, typeof(MyClass));
var il = dm.GetILGenerator();
var MyClass_Name = typeof(MyClass).GetProperty("Name");
il.Emit(OpCodes.Call, MyClass_Name.GetGetMethod());
if (MyClass_Name.PropertyType.IsValueType)//判断属性类型是否是值类型
{
il.Emit(OpCodes.Box);//如果是值类型就装箱
}
else
{
il.Emit(OpCodes.Castclass, typeof(object));//引用类型就强转为object
}
il.Emit(OpCodes.Ret);
return (Func<object>)dm.CreateDelegate(typeof(Func<object>));
}
  • 转型的注意事项

在转型虽然不是必须的,但最好是带上,不然你不知道什么时候会出现一些莫名其妙的错误

比如下面这个栗子:

错误的栗子

当把强转加上之后

虽然会导致程序异常,但至少比刚才那种情况好多了不是吗

  • 下篇预告

实例属性/字段的读取与设置,及其实用价值

分享代码,分享快乐
 
分类: C#

- 高级篇:二,IL设置静态属性,字段和类型转换的更多相关文章

  1. 玩转动态编译 - 高级篇:二,IL设置静态属性,字段和类型转换

    静态属性赋值 先来看 Reflector反射出的IL源码(感谢Moen的提示),这次用 Release模式编译,去掉那些无用的辅助指令 public void AAA(string s) { MyCl ...

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

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

  3. 一,IL访问静态属性和字段

    一,IL访问静态属性和字段 IL介绍 通用中间语言(Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一 ...

  4. Newtonsoft.Json高级篇:TypeNameHandling设置

    原文:Newtonsoft.Json高级篇:TypeNameHandling设置 此示例使用TypeNameHandling 设置在序列化JSON和读取类型信息时包含类型信息,以便在反序列化JSON时 ...

  5. Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法

    一.静态属性 静态属性相当于数据属性. 用@property语法糖装饰器将类的函数属性变成可以不用加括号直接的类似数据属性. 可以封装逻辑,让用户感觉是在调用一个普通的数据属性. 例子 class R ...

  6. iOS开发——高级篇——二维码的生产和读取

    一.二维码的生成 从iOS7开始集成了二维码的生成和读取功能此前被广泛使用的zbarsdk目前不支持64位处理器 生成二维码的步骤:导入CoreImage框架通过滤镜CIFilter生成二维码 二维码 ...

  7. iOS开发——高级篇——换肤、静态库

    一.换肤 1.思路1> 解决方案1,使用颜色作为图片素材的命名关键字 问题1:要保证每套图片的文件名 颜色+ 名称.png的格式比较麻烦 问题2:如果要将某一个图片应用到其他皮肤不方便2> ...

  8. JAVA高级篇(二、JVM内存模型、内存管理之第二篇)

    本文转自https://zhuanlan.zhihu.com/p/25713880. JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...

  9. JAVA高级篇(二、JVM内存模型、内存管理之第一篇)

    JVM内存结构如 Java堆(Heap),是Java虚拟机所管理的内存中最大的一块.Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 ...

随机推荐

  1. CSDN博客频道维护公告

    各位亲爱的用户:        为了给大家提供更稳定的使用环境,2014年4月23日23点至04月24日1点(本周四凌晨)博客频道server将进行维护,维护期间不能正常訪问.给大家带来不便,敬请广大 ...

  2. VisualStudio2012轻松把JSON数据转换到POCO的代码

    原文:VisualStudio2012轻松把JSON数据转换到POCO的代码       在Visual Studio 2012中轻松把JSON数据转换到POCO的代码,首先你需要安装Web Esse ...

  3. Xutils呼叫流源代码文件下载方法

    //我主要是好奇Xutils哪里回调onLoading(),查找等了很久也没找到,果然easy查找只是把它写下来 前言: 1.代码摘要只有主线,提供一般流程 2.为了易于理解,码变量名,而是类名的驼峰 ...

  4. 有趣Web之Json(四)---json与(Object/List/Map)相互转化

    干web报名时间.通常,他们需要json转换为Object/list/map要么Object/List/map转换为json,由能够编写代码的简单包装非常多,以减轻负担. 本文将给出json的一系列的 ...

  5. Sql Server 存储过程中查询数据无法使用 Union(All)

    原文:Sql Server 存储过程中查询数据无法使用 Union(All) 微软Sql Server数据库中,书写存储过程时,关于查询数据,无法使用Union(All)关联多个查询. 1.先看一段正 ...

  6. 反射调用方法报InvocationTargetException异常

    利用 Method 对象的 invoke 方法调用目标对象的方法时, 若在目标对象的方法内部抛出异常, 会被包装成 InvocationTargetException 异常抛出,  可以通过调用 In ...

  7. leetcode第22题--Merge k Sorted Lists

    problem:Merge k sorted linked lists and return it as one sorted list. Analyze and describe its compl ...

  8. 实例学习SSIS(四)--使用日志记录和错误流重定向

    原文:实例学习SSIS(四)--使用日志记录和错误流重定向 导读: 实例学习SSIS(一)--制作一个简单的ETL包 实例学习SSIS(二)--使用迭代 实例学习SSIS(三)--使用包配置 实例学习 ...

  9. jquery无缝滚动效果实现

    demo如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  10. HDInsight-Hadoop现实(两)传感器数据分析

    HDInsight-Hadoop现实(两)传感器数据分析 简要 现在,含传感器非常个人和商用设备收集来自物理世界的信息.例如.大多数手机都有 GPS.健身器材可以跟踪的步骤,你去数,恒温控制器可以监视 ...