一,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. 浅谈 js 数字格式类型

    原文:浅谈 js 数字格式类型 很多人也许只知道 ,123.456,0xff 之类的数字格式.其实 js 格式还有很多数字格式类型,比如 1., .1 这样的,也有 .1e2 这样的. 可能有人说这是 ...

  2. Vs2012 构建配置 Lua5.2.3

    随着手机游戏client程序员,当然,遇到这样的问题,该游戏已经提交出版.但第二天一早,发现有一个逻辑游戏BUG.怎么办,不严重,在一般情况下,非强制性的更新.假设一个严重BUG,他们将不得不强制更新 ...

  3. Flex在使用无线电的button切换直方图横坐标和叙述性说明

    1.问题叙述性说明 一组单选button,有周和月之分,选择"周",柱状图横坐标显示的是周,纵坐标显示的是人数:选择"月",柱状图横坐标显示的月,纵坐标显示的是 ...

  4. Access to the temp directory is denied. Identity 'NT AUTHORITY\NETWORK SERVICE' under which XmlSerializer is running does not have sufficient permiss

    造成错误的原因是用bat代码清理系统垃圾时造成的权限丢失而引起的 错误描述 1.An error occurred creating the configuration section handler ...

  5. NSString 常用分类

    #pragma mark 清空字符串中的空白字符 - (NSString *)trimString { return [self stringByTrimmingCharactersInSet:[NS ...

  6. Access denied for user: &#39;root@localhost&#39; (Using password: YES)

    centos 设备mysql成功后 首次使用root登录发生:Access denied for user: 'root@localhost' (Using password: YES) 因为mysq ...

  7. cocos2d-x 3.1.1 学习笔记[13] listen 监听器

    文章出自于  http://blog.csdn.net/zhouyunxuan //创建监听器 auto listen = EventListenerTouchOneByOne::create(); ...

  8. thrift js javascript C# Csharp webservice

    http://www.cnblogs.com/xxxteam/archive/2013/04/15/3023159.html 利用thrift实现js与C#通讯的例子 关键字:thrift js ja ...

  9. 运维自动化之SALTSTACK简单入门

    运维自动化之SaltStack简单入门 饱食终日而无所事事,是颓也,废也.但看昨日,费九牛二虎之力除一BUG便流连于新番之中,不知东方之既黑,实乃颓颓然而荒废矣.故今日来缀一文以忏昨日之悔. Salt ...

  10. ajax的get请求

    get和post是http请求方法最主要的两种方式. get: 先实现一个简单的get请求 index.html <!DOCTYPE html> <html lang="e ...