- 高级篇:二,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堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 ...
随机推荐
- 如何快速正确的安装 Ruby, Rails 运行环境
如何快速正确的安装 Ruby, Rails 运行环境 https://ruby-china.org/wiki/install_ruby_guide 对于新入门的开发者,如何安装 Ruby, Ruby ...
- UI 收集
semantic http://www.semantic-ui.com.cn/modules/reveal.html sbadmin http://startbootstrap.com/templat ...
- 我的MYSQL学习心得(九)
原文:我的MYSQL学习心得(九) 我的MYSQL学习心得(九) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYSQL ...
- IBM BigInsights 3.0.0.2 集群环境搭建
1. 改动hosts文件和永久主机名 由于BigInsights 3.0版本号不像之前的版本号能够直接用IP来添加节点,因此我们须要更改每台server的hosts文件和主机名: vim/etc/ho ...
- POI导出Excel文档通用工具方法
import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; imp ...
- Android Studio设置自己主动编project
在Eclipse自己主动编译兄弟习惯,刚搬到Android Studio.当然,错过这个功能,自己主动编译每次执行意味着更短的时间. Android Studio里面事实上也是有自己主动编译功能的,只 ...
- c#之冒泡排序的三种实现和性能分析
冒泡排序算法是我们经常见到的尤其是子一些笔试题中. 下面和大家讨论c#中的冒泡排序,笔者提供了三种解决方案,并且会分析各自的性能优劣. 第一种估计大家都掌握的,使用数据交换来实现,这种就不多说了,园子 ...
- java在string和int相互转化
1 如何串 String 转换成整数 int? A. 有两种方法: 1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([St ...
- nginx 502 Bad Gateway 错误问题收集
nginx 502 Bad Gateway 错误问题收集 (2010-11-18 13:51:37) 转载▼ 标签: 杂谈 分类: 工作 nginx 502 Bad Gateway 错误问题收集 因为 ...
- 使用UpdatePanel控件
使用UpdatePanel控件(二) UpdatePanel可以用来创建丰富的局部更新Web应用程序,它是ASP.NET 2.0 AJAX Extensions中很重要的一个控件,其强大之处在于不用编 ...