类库探源-反射机制

导读

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方法来加载程序集。

本文完

原文地址:https://www.cnblogs.com/Aphasia/p/4375143.html