通天大圣王蛇和纪中棠:应用程序配置和动态加载4----反射

来源:百度文库 编辑:九乡新闻网 时间:2024/04/18 08:26:32

反射

在程序集中可以包含多个模块,而每个模块中又可以包含类、接口、委托等类型,在每个类型中又因为类型的不同包含不同的组成部分,比如类(class)中又可以包含字段、属性、方法、实现的接口、继承的父类等信息。字段又可以有类型,名称,是否是静态的等性质,方法中有参数,返回类型等信息。这些信息对于程序的编制人员来说没有什么神秘的,因为这些东西都是程序员自己实现的。可是对于使用这些材料的另外的用户来说,就显得十分神秘,因为这些东西对于使用者来说都是透明的。那么现在能不能有一种方法,得到这些信息呢?

答案是肯定的,因为在程序集中还有一部分元数据——类型元数据。这些类型元数据描述了对应代码中定义的类型和方法等信息。使用.net框架中的反射就可以得到你想要的所有东西。

反射是.net框架中很值得注意的一项功能。通过使用反射,程序可以收集和操纵它自己的原数据。这是一种可以察看程序内部的程序集和对象集合的强有力的机制。反射所需要的类包含在System.Reflection名称空间里。通过反射程序员可以收集和检查某一个对象的类型、属性、方法和事件的信息,并且可以创建这些类型的实例,还可以调用这些类型中的方法。使用反射还可以在内存中创建动态的程序集并执行程序集中的方法,执行完成后你还可以决定是否将动态创建的程序集保存。

反射具有强大的功能,同时也是一个非常庞大的话题。牵扯到的知识点也很多,包括程序集、自定义特性、泛型等,想要完全掌握它非常不易。本节仅仅对反射做一个概要介绍,关于它更精深的内容,需要在实践中逐渐掌握。

简单来说,反射提供这样几个能力:

1、查看和遍历类型(及其成员)的基本信息和程序集元数据(metadata);

2、迟绑定(Late-Binding)方法和属性。

3、创建类型实例(并可以动态调用所创建的实例的方法、字段、属性)。

4、在内存中动态创建程序集,并执行。

11.2.1 反射与System.Type类

System.Type类对于反射起着核心的作用。可以说Type为反射功能的根,也是访问元数据的主要方式。当请求反射某个类型时,就会生成一个Type类的对象,在Type对象中就封装了关于反射类型对象的信息。Type对象就像一个入口,进入这个入口就可以得到反射类型对象的信息,如构造函数、方法、字段、属性 (Property) 和类的事件,以及在其中部署该类的模块和程序集。所以反射的第一步就是获取关于反射类型的Type对象或者实例。有了这个实例就可以使用Type提供的属性和方法获取这个类型的一切信息(方法、字段、属性、事件、参数、构造函数等)。

获取Type对象有两种形式,一种是获取当前加载程序集中的类型(Runtime),一种是获取没有加载的程序集的类型。

先考虑Runtime时的Type,一般来说有三种获取方法:

1. 使用Type类提供的静态方法GetType()

比如想要获得Stream类型的Type实例,则可以这样:

Type t = Type.GetType("System.IO.Stream");

txtOutput.Text = t.ToString();

注意到GetType方法接受字符串形式的类型名称。该名称需要使用一个类型的全名。

2. 使用 typeof 操作符

也可以使用C# 提供的typeof 操作符来完成这一过程:

// 如果在程序文件前面使用了using System.IO; 也可以直接用typeof(Stream);

Type t = typeof(System.IO.Stream);

这时typeof的参数是一个类型,而不是一个字符串,Stream类型被传递到typeof操作符中。

3. 通过类型实例获得Type对象

可以通过类型的实例来获得:

String name = "this is a String";

Type t = name.GetType();

使用这种方法时应当注意,尽管是通过对象去获取Type对象,但是Type对象不包含关于这个特定对象的信息,仍是保存对象的类型(String)的信息。

到现在为止,已经多次提过Type封装了类型的信息,那么这些类型信息都包含什么内容呢?假设现在有一个类型的实例,它的名字叫做objdemo,对它的信息一无所知,并通过下面代码获取了对于它的Type实例:

// 前面某处的代码实例化了objdemo对象

Type t = objdemo.GetType();

现在,t包含了关于objdemo的哪些信息呢?

1. objdemo类型的基本信息

当然首先想知道objdemo是什么类型的,也就是objdemo的类型名称;还想知道该类型位于什么命名空间下;它的基类型是什么;以及它在.Net运行库中的映射类型;它是值类型还是引用类型;它是不是Public的;它是枚举、是类、是数组、还是接口。它是不是基础类型(int等)。等等。

表11-3列出了Type提供的一些常用属性,用于获取类型的基本信息。

表11-3

 

 

Name

获取类型名称

FullName

类型全名

Namespace

命名空间名称

BaseType

获取对于基类的Type类型的引用

UnderlyingSystemType

在.Net中映射的类型的引用

Attributes

获取指定类型的属性,比如public等信息

IsValueType

是否值类型

IsByRef

是否由引用传递

IsEnum

是否枚举

IsClass

是否类

IsInterface

是否接口

IsSealed

是否密封类

IsPrimitive

是否基类型(比如int)

IsAbstract

是否抽象

IsPublic

是否公开

IsNotPublic

是否非公开

IsVisible

是否程序集可见

2. objdemo类型的成员信息

接着还想知道它有哪些字段;有些什么属性,以及关于这些属性的信息;有哪些构造函数;有哪些方法,方法有哪些参数,有什么样的返回值;包含哪些事件;实现了哪些接口;还可以不加区分地获得它的所有前面列出的成员。

就拿第一个来说,想获取类型都有哪些字段,以及这些字段的信息。而字段都包含哪些信息呢?可能有字段的类型、字段的名称、字段是否public、字段是否为const、字段是否是static等等,那么是不是应该将字段的这些信息也封装起来呢?

实际上,.Net中提供了FiledInfo类型,它封装了关于字段的相关信息。按照前面说列举的成员,类似的还有PropertyInfo类型、ConstructorInfo类型、MethodInfo类型、EventInfo类型。而对于方法而言,对于它的参数,也会有in参数,out参数,参数类型等信息,类似的,在 System.Reflection 命名空间下,除了有上面的提到的那么多Info后缀结尾的类型,还有个ParameterInfo类型,用于封装方法的参数信息。

最后,应该注意到Type类型,以及所有的Info类型均继承自MemberInfo类型(成员信息类型,包含字段、方法、构造函数等所有的成员的类型),MemberInfo类型提供了获取类型基础信息的能力。

在VS2008中键入Type,选中它,再按下F12跳转到Type类型的定义,纵览Type类型的成员,发现可以大致将属性和方法分成这样几组:

IsXXXX,比如 IsAbstract,这组bool属性用于判断类型是否属于某个类型,表11-3已经列举了一些。

GetXXXX(),比如GetField(),返回FieldInfo,这组方法用于获取某个成员的信息。

GetXXXXs(),比如GetFields(),返回FieldInfo的一个数组,这组方法让用户获取某类成员信息。

由于MemberInfo是一个基类,当获得一个MemberInfo后,我们并不知道它是PropertyInfo(封装了属性信息的对象)还是FieldInfo(封装了属性信息的对象),所以,有必要提供一个办法来加以判断,在Reflection命名空间中,使用位标记来进行判断,这里先介绍第一个位标记MemberTypes,它用于标记成员类型,可能的取值如下:

[Flags]

public enum MemberTypes

{

    Constructor = 1,  //  该成员是一个构造函数

    Event = 2,        //  该成员是一个事件

    Field = 4,        //  该成员是一个字段

    Method = 8,           //  该成员是一个方法

    Property = 16,    //  该成员是一个属性

    TypeInfo = 32,    //  该成员是一种类型

    Custom = 64,      //  自定义成员类型

    NestedType = 128, //  该成员是一个嵌套类型

    All = 191,        //  指定所有成员类型。

}

具体的使用方法在后面会逐渐介绍到。