- 高级篇:二,IL设置静态属性,字段和类型转换
- 高级篇:二,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_Any和OpCodes.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>));
}

- 转型的注意事项
在转型虽然不是必须的,但最好是带上,不然你不知道什么时候会出现一些莫名其妙的错误
比如下面这个栗子:

错误的栗子


当把强转加上之后

虽然会导致程序异常,但至少比刚才那种情况好多了不是吗
- 下篇预告
实例属性/字段的读取与设置,及其实用价值
- 高级篇:二,IL设置静态属性,字段和类型转换的更多相关文章
- 玩转动态编译 - 高级篇:二,IL设置静态属性,字段和类型转换
静态属性赋值 先来看 Reflector反射出的IL源码(感谢Moen的提示),这次用 Release模式编译,去掉那些无用的辅助指令 public void AAA(string s) { MyCl ...
- 玩转动态编译 - 高级篇:一,IL访问静态属性和字段
IL介绍 通用中间语言(Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一种属于通用语言架构和.NET ...
- 一,IL访问静态属性和字段
一,IL访问静态属性和字段 IL介绍 通用中间语言(Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一 ...
- Newtonsoft.Json高级篇:TypeNameHandling设置
原文:Newtonsoft.Json高级篇:TypeNameHandling设置 此示例使用TypeNameHandling 设置在序列化JSON和读取类型信息时包含类型信息,以便在反序列化JSON时 ...
- Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法
一.静态属性 静态属性相当于数据属性. 用@property语法糖装饰器将类的函数属性变成可以不用加括号直接的类似数据属性. 可以封装逻辑,让用户感觉是在调用一个普通的数据属性. 例子 class R ...
- iOS开发——高级篇——二维码的生产和读取
一.二维码的生成 从iOS7开始集成了二维码的生成和读取功能此前被广泛使用的zbarsdk目前不支持64位处理器 生成二维码的步骤:导入CoreImage框架通过滤镜CIFilter生成二维码 二维码 ...
- iOS开发——高级篇——换肤、静态库
一.换肤 1.思路1> 解决方案1,使用颜色作为图片素材的命名关键字 问题1:要保证每套图片的文件名 颜色+ 名称.png的格式比较麻烦 问题2:如果要将某一个图片应用到其他皮肤不方便2> ...
- JAVA高级篇(二、JVM内存模型、内存管理之第二篇)
本文转自https://zhuanlan.zhihu.com/p/25713880. JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...
- JAVA高级篇(二、JVM内存模型、内存管理之第一篇)
JVM内存结构如 Java堆(Heap),是Java虚拟机所管理的内存中最大的一块.Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 ...
随机推荐
- POJ 3299 Humidex(简单的问题)
[简要题意]:什么是温度,湿度--,之间的转换.. [分析]:式已被赋予. // 252k 0Ms /* 当中exp表示的是求e的x次幂 解法就直接依据题目中的公式解决就好!! */ #include ...
- 为什么使用Hystrix?
分布式服务弹性框架“Hystrix”实践与源码研究(一) 文章初衷 为了应对将来在线(特别是无线端)业务量的成倍增长,后端服务的分布式化程度需要不断提高,对于服务的延迟和容错管理将面临更大挑战,公 ...
- SHDocVw, AxSHDocVw的引用
原文:SHDocVw, AxSHDocVw的引用 SHDocVw的引用SHDocVw一定要在下面这个路径找: 类似 C:\Program Files\Microsoft Visual Studio 9 ...
- ASP.NET 5 :读写数据库连接字符串
ASP.NET 5 :读写数据库连接字符串 1.概述 ASP.NET 5模板项目配置文件采用了JSON格式,你依然可以采用以前的xml格式.C#对web.config或app.config读写及加密有 ...
- itext之pdf导出添加水印Java工具类
import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentExce ...
- 一步一步写算法(之prim算法 上)
原文:一步一步写算法(之prim算法 上) [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 前面我们讨论了图的创建.添加.删除和保存等问题.今 ...
- 上海及周边地区产品技术创业QQ群:98905958
创业是一件骄傲的事,每一个行业里最棒人才都应该创业或參与创业或以一个创业者的心态进行职业远景规划: 创业是一件寻常的事,跟上班打工一样寻常,没有什么必须的前置条件才干够開始,也没有什么前置条件能保证我 ...
- 快速构建Windows 8风格应用23-App Bar概述及使用规范
原文:快速构建Windows 8风格应用23-App Bar概述及使用规范 本篇博文主要介绍App Bar概述.App Bar命令组织步骤.App Bar最佳实践. App Bar概述 Windo ...
- VMware Linux 下 Nginx
负载 VMware Linux 下 Nginx 安装配置 - nginx.conf 配置 [负载两个 Tomcat] (三) Weiseditor 2014-11-26 23:42 阅读:1 评论 ...
- VS2015 Apache Cordova
VS2015 Apache Cordova第一个Android和IOS应用 前言 本人个人博客原文链接地址为http://aehyok.com/Blog/Detail/75.html. http: ...