- 高级篇:二,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. C# Parse和Convert的区别分析(转)

    大家都知道在进行类型转换的时候有连个方法供我们使用就是Convert.to和*.Parse,但是疑问就是什么时候用C 什么时候用P 通俗的解释大家都知道: Convert 用来转换继承自object类 ...

  2. 九度OJ 1068 球半径和数量 (模拟)

    题目1068:球的半径和体积 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4797 解决:1696 题目描写叙述: 输入球的中心点和球上某一点的坐标,计算球的半径和体积 输入: 球的中心 ...

  3. Socket 学习(三).5 UDP 的弱点

    前面 讲到了,udp 传输文本的例子,发现 udp 确实 比tcp 高效一些,现在我用来传输文件,问题果然来了,结果发现 他不能一次 传输大于 64K的东西! 那么 我自然想到了 切包,多次发送,再合 ...

  4. MS SQL Server递归查询

    原文:MS SQL Server递归查询 刚才在论坛上看到网友一个要求.参考如下,Insus.NET分析一下,可以使用MS SQL Server的递归查询,得到结果.准备一张表: 根据网友提供的数据, ...

  5. Codeforces 363A Soroban

    模拟算盘 #include<bits/stdc++.h> using namespace std; int main() { char s[20]; scanf("%s" ...

  6. C#修改用户名

    string strCmdText; strCmdText = "useraccount where name='" + 旧密码 + "' rename " + ...

  7. jquery validate remote验证唯一性

    jquery.validate.js 的 remote 后台验证 之前已经有一篇关于jquery.validate.js验证的文章,还不太理解的可以先看看:jQuery Validate 表单验证(这 ...

  8. JAVA开发语言基础

    很多时候我们都不知道,为什么android最开始要用java作为基础语言 看看知乎上都是怎么回答的 为什么java最初成为android开发的基础语言 ------------------------ ...

  9. 微信SDK使用总结

    最近做一个分享功能,需要使用微信SDK分享内容给朋友或朋友圈.期间遇到些奇怪的问题,花了点时间去折腾了一番. 首先需要到微信开放平台http://open.weixin.qq.com/?lang=zh ...

  10. SpringMVC视图

    SpringMVC视图机制详解[附带源码分析] 目录 前言 重要接口和类介绍 源码分析 编码自定义的ViewResolver 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一 ...