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


    一,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

    复制代码
     View Code

    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#
  • 相关阅读:
    vs2005中 fstream 不支持中文路径问题的解决方法:
    CString char* string 互转
    OpenGl显示,任意大小的图片作为背景
    combo box
    Android 开发环境搭建
    打开子对话框 选择文件
    (转)Static MemoryLayout_shadow memory
    (转)深入剖析I/O约束
    (转)set_input_delay/ set_output_delay之图解
    (转)PrimeTime分析流程
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3256663.html
Copyright © 2020-2023  润新知