zoukankan      html  css  js  c++  java
  • 类库探源-反射机制

    导读

    1、什么是反射

    2、反射的基石——元数据

    3、用ildasm.exe 查看元数据

    4、System.Reflection 命名空间下需关注的成员

    5、获取 Type 实例的方式

    6、晚绑定与System.Activator 类


    什么是反射

    在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。这是Wiki的解释。C#和Java都支持反射,主流的C#和Java框架中都大量应用了反射。反射的主要应用场景如下:

    1、使用反射来动态分析来探索程序集中的类型(ildasm.exe 和 ILSpy 等反编译工具)

    2、晚绑定

    3、Attribute

    4、访问某个类的私有成员

    5、CLR在执行某些操作是会用到反射机制如:序列化对象时会用到反射、GC用反射构造引用树、指类型的Equal()方法使用反射逐一比较相应字段等

    反射的基石——元数据

    上一节说明了反射的定义以及反射的主要应用场景。那么为什么C#和Java会有反射机制?或者说是不是所有编程语言都有反射机制?要回答这些问题,我们就必须谈到反射的基石——元数据。

    什么是元数据?

    元数据是描述数据的数据(MetaDat is data about data)。这个定义看着可能比较模糊,以C#为例来说,元数据就是记录类、结构以及成员变量的一个数据结构。

    用ildasm.exe 查看元数据

    上一节对于元数据的定义可能比较模糊,这一节我将一个例子结合 ildasm.exe 工具谈下元数据

    using System;
    
    class DemoLib
    {
        private string _name;
        public string Name
        {
            get{return _name;}
            set{_name = value;}
        }
        
        public void Print()
        {
            Console.WriteLine(Name);
        }
    }

    csc /t:library DemoLib.cs

    用 Ildasm.exe 打开刚才生成的 DemoLib.dll ,按住 Ctrl +M 得到当前程序集的元数据信息如图所示

    image

    我们细看下这个 MetaInfo

    TypeDef #n、TypeRef #n、Assembly 、AssemblyRef #n、User Strings

    上面的5个地方是我们需要重点看的

    TypeDef #n        代表类型定义 n是正整数(下同)

    TypeRef #n         代表类型引用(当前类型引用的类型)

    Assembly            当前程序集的信息

    AssemblyRef #n   当前程序集引用的程序集信息

    User Strings        当前程序集用到的字面字符串值

    我们主要看下 TypeDef 下的内容

    Field #1 (04000001)          类型中定义的字段

    Method #1 (06000001)     类型中定义的方法

    Property #1 (17000001)   类型中定义的属性

    元数据中记录类型数据,这也就是为什么元数据被称为描述数据的数据。

    System.Reflection 命名空间下需关注的成员

    System.Reflection 命名控件提供了与反射机制相关的成员

    类型 作用
    Assembly 该抽象类包含了许多静态方法,通过它可以加载、了解和操纵一个程序集
    MemberInfo 该类是抽象基类,它为EventInfo、FieldInfo、MethodInfo和PropertyInfo类型定义了公共的行为
    MethodInfo 该抽象类包含给定方法的信息

    下图简要说明了反射的一些过程

    image

    获取 Type 实例的方式

    由上图可知,获取一个对象的Type实例是反射机制的核心操作。在C#中一共提供了4中获取Type实例的方法,现归纳如下

    1、使用 System.Object.GetType() 获得 Type实例

    显而易见,想要使用这个方法,必须得到类型的编译时信息,并且在内存中有类型实例

    2、使用 typeof() 得获得Type 实例

    需要提供类型的编译时信息, typeof 需要的是类型的强类型名称,而不是文本信息

    3、使用 System.Type.GetType() 获得 Type实例

    通过调用 System.Type 类的静态方法 GetType() 指定类型的完全限定名,获得Type实例,采用这种方法,我们不需要编译时指定类型信息,System.Type.GetType() 方法有多个重载方法,详细信息查看MSDN

    4、使用 Assmbly.GetType( ) 获得Type实例

    第四种是最常用的,先加载dll获得程序集实例,在通过程序集实例获得对应类型信息

    晚绑定与System.Activator 类

    晚绑定是一种创建一个给定类型的实例并在运行时调用其成员,而不需要在编译时知道它存在的一种技术。通过这种方法使我们的系统扩展性加强

    System.Activator 类提供了 创建实例的方法 CreateInstance() 这个方法有很多重载方法详见MSDN

    我们来看一个反射实例

    using System;
    namespace XXX.Common.Lib
    {
        public class TestLib
        {
            public TestLib()
            {
                Console.WriteLine("TestLib Ctor.");
            }
            
            public int Sum(int a,int b)
            {
                Console.WriteLine("Method TestLib.Sum() Invoked");
                return a+b;
            }
        }
    }

    csc /t:library Lib.cs

    得到 Lib.dll

    我们在写一个 App.cs 通过反射的方式加载 Lib.cs 中的Sum 方法

    using System;
    using System.Reflection;
    
    class App
    {
        static void Main()
        {
            Assembly assmbly = Assembly.LoadFrom("Lib.dll"); // LoadFrom("FileName.dll")  Load(FileName)
            Type type = assmbly.GetType("XXX.Common.Lib.TestLib");
            object obj = Activator.CreateInstance(type);     // 进入 XXX.Common.Lib.TestLib 构造器
            
            MethodInfo mi = type.GetMethod("Sum");
            object[] pars = new object[]{1,2};
            int result = 0;
            result = (int)mi.Invoke(obj,pars);
            Console.WriteLine(result);
        }
    }

    csc app.cs

    运行结果

    image

    常见的3种加载程序集的方法:

    1、Assembly.Load

    使用 Assembly.Load 加载程序集的顺序如下:首先会找GAC(全局程序集缓存),然后到应用程序的根目录查找,最后到应用程序的私有路径查找

    2、Assembly.LoadFile

    该方法从指定路径的文件来加载程序集

    3、Assembly.LoadFrom

    该方法从指定的路径来加载程序集,实际上该方法被调用的时候,CLR会打开这个文件,获取其中程序集版本、公钥等信息,然后把它们传递给Load方法,通过Load方法来加载程序集。

    本文完

  • 相关阅读:
    微软职位内部推荐-Software Engineer II
    微软职位内部推荐-Senior Software Engineer
    Linux日期时间显示输出
    Redis性能优化
    Can't use Subversion command line client: svn
    redis常用性能分析命令
    Linux下配置tomcat + apr + native应对高并发
    Tomcat7并发和线程数
    mongodb常用命令小结
    Spring MVC @PathVariable被截断
  • 原文地址:https://www.cnblogs.com/Aphasia/p/4375143.html
Copyright © 2011-2022 走看看