zoukankan      html  css  js  c++  java
  • 玩转动态编译

    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; }
        }
    }
    View Code

    然后用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);
    下期预告

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

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

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

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

    我写的文章,除了纯代码,其他的都是想表达一种思想,一种解决方案.希望各位看官不要局限于文章中的现成的代码,要多关注整个文章的主题思路,谢谢!
    我发布的代码,没有任何版权,遵守WTFPL协议(如有引用,请遵守被引用代码的协议)
    qq群:5946699 希望各位喜爱C#的朋友可以在这里交流学习,分享编程的心得和快乐
  • 相关阅读:
    NYOJ 158 省赛来了(变相组合数)
    NYOJ 111 分数加减法
    NYOJ 14 会场安排问题 (贪心)
    POJ 3903 Stock Exchange(LIS)
    NYOJ 456 邮票分你一半(01背包)
    HDU 4521 小明系列问题——小明序列 (LIS加强版)
    CSU 1120 病毒(经典模板例题:最长公共递增子序列)
    挑战程序设计竞赛里面的几道深度优先搜索
    2009 Multi-University Training Contest 4
    USACO sec1.1
  • 原文地址:https://www.cnblogs.com/blqw/p/3256196.html
Copyright © 2011-2022 走看看