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#的朋友可以在这里交流学习,分享编程的心得和快乐
  • 相关阅读:
    webdav srs相关
    How To Configure WebDAV Access with Apache on Ubuntu 14.04
    ubuntu 编译lighttpd
    srs编译及推流测试
    Compile pciutils (lspci, setpci) in Windows x86,在 Windows x86 平台下编译 pciutils (lspci, setpci)
    mingw MSYS2 区别
    Qt之美(三):隐式共享
    Qt之美(二):元对象
    Qt之美(一):d指针/p指针详解
    C++的栈空间和堆空间
  • 原文地址:https://www.cnblogs.com/blqw/p/3256196.html
Copyright © 2011-2022 走看看