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

IL介绍

通用中间语言Common Intermediate Language,简称CIL,发音为"sill"或"kill")是一种属于通用语言架构和.NET框架的低阶(lowest-level)的人类可读的编程语言。目标为.NET 框架的语言被编译成CIL,然后汇编成字节码。CIL类似一个面向对象的汇编语言,并且它是完全基于堆栈的。它运行在虚拟机上,其主要的语言有C#、Visual Basic .NET、C++/CLI以及 J♯。

在.NET语言的测试版中,CIL原本叫做微软中间语言,即Microsoft Intermediate Language,简称MSIL。由于C#和通用语言架构的标准化,在.Net开发平台下,所有语言(C#、VB.NET、J#、Managed C++)都会被编译为MSIL,再由CLR负责运行,字节码现在已经官方地成为了CIL。因此,CIL仍旧经常与MSIL相提并论,特别是那些.NET语言的老用户。

以上引用自维基百科

简单的来说,就是.NET在编译C#编码时会由一个“C#代码编译组件”将C#代码编译为IL中间码,然后再由另一套组件将IL中间码编译为字节码

利用Reflector查看IL源码

虽然IL码很复杂,不过编写一些简单的问题也不大。

下面就由浅入深的举几个栗子

我们先编译一个简单的方法AAA


 
using System;
using System.Collections.Generic;
using System.Text;
namespace blqw.IL.Demo
{
class Program
{
static void Main(string[] args)
{
}
public object AAA()
{
return MyClass.Name;
}
}
class MyClass
{
public static string Name { get; set; }
}
}

然后用reflector反射,看看生成的IL代码

先找到AAA方法,然后在语言的下拉框中选择IL

分析IL

简单介绍一下每句IL的含义

.method public hidebysig instance object AAA() cil managed
{
.maxstack 1
.locals init (
[0] obje-0001ct CS$1$0000)//(以上的内容都不是我们需要关心的)
L_0000: nop //如果修补操作码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操作。(也就是说这个步骤是么意义的)
L_0001: call string blqw.IL.Demo.MyClass::get_Name()//调用由传递的方法说明符指示的方法。(将返回值保存在堆栈中)
L_0006: stloc.0 //从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。(将刚才的返回值,保存在locals[0],也就是那个CS$1$0000中)
L_0007: br.s L_0009//无条件地将控制转移到目标指令(类似于goto吧,跳转到下一行)
L_0009: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上。
L_000a: ret // 从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。(这句必须是方法中的最后一句,指示方法已经结束了,这时如果堆栈中有值,则返回值,没有则返回void)
}

上面注释中括号里的都是我写的,括号外的是微软官方的注释

仔细看就会发现,其实编译好的IL生成了很多不必要的操作

比如L_0000.连微软自家都说这个操作是无意义的,尽可能消耗时间而已..

还有L_0006~L_0009.都是无用的;连起来看就会发现,它先把堆栈中的值保存到变量0中,然后goto到下一行,然后再把变量0的值加载到堆栈上,结果堆栈上原来是什么值还是什么值

所以真正有用的只有L_0001和L_000a,一句是执行方法,一句是返回

在C#中编写IL访问静态属性

好了现在我们按照这个思路自己写一个IL,只有2句啊,超简单有木有!!!!!!

public static Func<object> ILTest()
{
//编译li动态方法
//1.声明动态方法对象DynamicMethod
// 第一个参数是方法名,可以为空字符,不可以是null
// 第二个参数是动态方法返回值类型
// 第三个参数是Type[],表示方法参数,如果没有参数可以是null
// 第四个参数是声明逻辑关联类,可以让动态方法访问该类有权访问所有字段,包括逻辑关联类的私有字段
var dm = new DynamicMethod("", typeof(object), null, typeof(MyClass));
//2.声明il编译器
var il = dm.GetILGenerator();
//3.执行MyClass类的Name属性的Get方法 这句对应刚才的L_0001
il.Emit(OpCodes.Call, typeof(MyClass).GetProperty("Name").GetGetMethod());
//4.方法结束,这句对应刚才的L_000a
il.Emit(OpCodes.Ret);
//5.由il编译器创建指定类型的动态方法委托
return (Func<object>)dm.CreateDelegate(typeof(Func<object>));
}

去掉注释和方法声明,整个方法体才5行啊!

查看下运行结果

成功了!而且由于自己编写IL比微软生成好的代码少了好多,所以性能上更好

编写IL访问静态字段

下面用一样的方法看看静态字段怎么访问

第一步,直接把MyClass的Name属性改为静态字段

class MyClass
{
public static string Name;
}

查看反编译的IL代码

.method public hidebysig instance object AAA() cil managed
{
.maxstack 1
.locals init (
[0] object CS$1$0000)
L_0000: nop
L_0001: ldsfld string blqw.IL.Demo.MyClass::Name
L_0006: stloc.0
L_0007: br.s L_0009
L_0009: ldloc.0
L_000a: ret
}

好吧,只有L_0001不一样了

对应C#中的代码更变:

public static Func<object> ILTest()
{
//编译li动态方法
//1.声明动态方法对象DynamicMethod
// 第一个参数是方法名,可以为空字符,不可以是null
// 第二个参数是动态方法返回值类型
// 第三个参数是Type[],表示方法参数,如果没有参数可以是null
// 第四个参数是声明逻辑关联类,可以让动态方法访问该类有权访问所有字段,包括逻辑关联类的私有字段
var dm = new DynamicMethod("", typeof(object), null, typeof(MyClass));
//2.声明il编译器
var il = dm.GetILGenerator();
//3.执行MyClass类的Name属性的Get方法 这句对应刚才的L_0001
//il.Emit(OpCodes.Call, typeof(MyClass).GetProperty("Name").GetGetMethod());
//OpCodes.Ldsfld : 将静态字段的值推送到计算堆栈上。
il.Emit(OpCodes.Ldsfld, typeof(MyClass).GetField("Name"));
//4.方法结束,这句对应刚才的L_000a
il.Emit(OpCodes.Ret);
//5.由il编译器创建指定类型的动态方法委托
return (Func<object>)dm.CreateDelegate(typeof(Func<object>));
}

执行结果和刚才一样的就不贴了

动态方法访问私有属性和字段

其实在刚才的方法注释中我就已经提到了,第三个参数是"逻辑关联类"

这个可以理解成你把这个动态方法编译到哪个类中去了,在这个动态方法中可以访问逻辑关联类可以访问的所有对象

把刚才的栗子改为私有的试下?

class MyClass
{
static string Name; //这个方法是为了给Name赋值用
public static void SetName(string name)
{
Name = name;
}
}
public static Func<object> ILTest()
{
//编译li动态方法
//1.声明动态方法对象DynamicMethod
// 第一个参数是方法名,可以为空字符,不可以是null
// 第二个参数是动态方法返回值类型
// 第三个参数是Type[],表示方法参数,如果没有参数可以是null
// 第四个参数是声明逻辑关联类,可以让动态方法访问该类有权访问所有字段,包括逻辑关联类的私有字段
var dm = new DynamicMethod("", typeof(object), null, typeof(MyClass));
//2.声明il编译器
var il = dm.GetILGenerator();
//3.执行MyClass类的Name属性的Get方法 这句对应刚才的L_0001
//il.Emit(OpCodes.Call, typeof(MyClass).GetProperty("Name").GetGetMethod());
//OpCodes.Ldsfld : 将静态字段的值推送到计算堆栈上。
il.Emit(OpCodes.Ldsfld, typeof(MyClass).GetField("Name", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static));//这里稍微改一下,反射到私有静态字段是要添加额外参数的
//4.方法结束,这句对应刚才的L_000a
il.Emit(OpCodes.Ret);
//5.由il编译器创建指定类型的动态方法委托
return (Func<object>)dm.CreateDelegate(typeof(Func<object>));
}

访问静态私有属性的方式基本雷同,反射私有属性的代码是:

typeof(MyClass).GetProperty("Name",System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetGetMethod(true);
下期预告

昨天是情人节,早早就上床了

嗯..别误会,出去玩了下,累了,所以睡得比较早

.本来应该把静态属性和字段的设置也写完的,真的没时间的,马上要上班去了

下一篇再说属性设置吧,同时吧类型转换一起将了,到时候还可以搞点小破坏哈~~

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

一,IL访问静态属性和字段的更多相关文章

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

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

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

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

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

    - 高级篇:二,IL设置静态属性,字段和类型转换 静态属性赋值 先来看 Reflector反射出的IL源码(感谢Moen的提示),这次用 Release模式编译,去掉那些无用的辅助指令 public ...

  4. 类实例化对象可以访问静态(static)方法,但是不能访问静态属性。

    类-> 访问->静态方法(类的方法)->可以 类 ->访问->普通方法(对象的方法)->不可以(虽然方法里不用$this关键字时,可以!但不支持这种写法) 类-&g ...

  5. WPF整理-XAML访问静态属性

    "XAML provides an easy way to set values of properties—type converters and the extended propert ...

  6. PHP 访问类中的静态属性

    静态属性和普通属性不一样,静态属性只属于类本身而不属于类的任何实例,所以他们的访问方式也不一样.你可以把静态属性认为是存储在类当中的全局变量,而且你可以在任何地方通过类来访问它们. 在类本身中访问静态 ...

  7. PHP static静态属性和静态方法

    这里分析了php面向对象中static静态属性和静态方法的调用.关于它们的调用(能不能调用,怎么样调用),需要弄明白了他们在内存中存放位置,这样就非常容易理解了.静态属性.方法(包括静态与非静态)在内 ...

  8. 不可或缺 Windows Native (19) - C++: 对象的动态创建和释放, 对象的赋值和复制, 静态属性和静态函数, 类模板

    [源码下载] 不可或缺 Windows Native (19) - C++: 对象的动态创建和释放, 对象的赋值和复制, 静态属性和静态函数, 类模板 作者:webabcd 介绍不可或缺 Window ...

  9. php面向对象中static静态属性和静态方法的调用

    这篇文章主要介绍了php面向对象中static静态属性和静态方法的调用,实例分析了static静态属性和静态方法的原理与调用技巧,需要的朋友可以参考下 本文实例讲述了php中static静态属性和静态 ...

随机推荐

  1. MVC日期格式化的2种方式

    原文:MVC日期格式化的2种方式 假设有这样的一个类,包含DateTime类型属性,在编辑的时候,如何使JoinTime显示成我们期望的格式呢? using System; using System. ...

  2. asp.net 获得客户端 mac 地址

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  3. Lua的多任务机制——协程(coroutine)

    并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定 ...

  4. java设计模式之七装饰器模式(Decorator)

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下: Source类是被装饰类,Decorator类是一个 ...

  5. C语言库函数大全及应用实例二

    原文:C语言库函数大全及应用实例二                                              [编程资料]C语言库函数大全及应用实例二 函数名: bioskey 功 能 ...

  6. (C语言)共用体union的使用方法举例

    曾经在学校学习C语言的时候一直搞不懂那个共用体union有什么用的.工作之后才发现它的一些妙用,现举比例如以下: 1. 为了方便看懂代码. 比方说想写一个3 * 3的矩阵,能够这样写: [ 注:以下用 ...

  7. leetcode[50] N-Queens

    题目:给定一个n,那么在n*n的棋盘里面放国际象棋的皇后,皇后之间互不在攻击范围.(皇后的攻击范围是她所在位置的哪一行,那一列,和她的正负1的对角线) The n-queens puzzle is t ...

  8. sql语句 面试题

    ql语句 面试题   自动编号   学号   姓名 课程编号 课程名称 分数 1        2005001  张三  0001      数学    69 2        2005002  李四 ...

  9. linux sar 命令详解(转载)

    linux sar 命令详解 2013-04-01 11:05 [小 大] 来源: 开源中国社区 评论: 0 分享至: 百度权重查询 词库网 网站监控 服务器监控 SEO监控 手机游戏 iPhone游 ...

  10. UVA 141 The Spot Game 斑点游戏。。

     The Spot Game  The game of Spot is played on an NxN board as shown below for N = 4. During the game ...