上一节结尾部分的示例显示了与给定对象兼容的类型列表。它不过是将类型定义变为机器可读(machine-readable)的一个很小的例子。这个功能常常被称为反射(reflection),Java将这个术语变得广为流行。
反射使得程序能够触及类型定义的所有方面,不管是在开发时还是在运行时。尽管反射对于构建高度动态的系统是很有用的,但把它作为一个开发时工具可能更合适。反射最主要的应用是代码生成( code generation)。代码生成往往被认为是开发时的活动,而反射具有这方面的能力。然而,与使用解释风格的设计相比,即使在运行时使用反射的系统,也能够从动态生成代码中获得更多的性能优势。
CIR提供了丰富的实用部件用于轻松地生成代码。System.Reflection.Emit库允许基于CLR的程序发射类型、模块和程序集。IMetaDataEmit接口向C++/COM程序提供丁相同的功能:最后,System.CodeDOM提供了用于在内存中构造更高级的c#或VB NET程序的功能,并在执行前把它们编译到模块和程序集。本章重点是对类型定义的读取,这属于System.Reflection命名空间的范畴。
图4.3显示了反射对象模型。注意,这个列象模型反映丁下列事实,即类型属于模块,而模块又属于程序集。此外,这个模型还反映了类型包含诸如字段和方法成员的事实。
在System.Reflection命名空间中的主要类型是Memberinfo类型。如前面一章所讨论的那样.类型中包含了成员。各个成员的运行时的描述通过System.Type.GetMembers方法都能够进行编程访问。每个成员描述的是一个类型的实例,而该类型是由System.Reflection.Memberinfo类型派生而来。如图4.4所示,Memberinfo类型相当于大多数特定反射类型的通用基共型。
MemberInfo类型有四个重要属性:Name属性把该成员名称作为一个字符串返回;MenberType属性返回一个SYstem.Reflection.MemberType值,表明该成员是否是一个字段、方法或者另外一种成员.ReflectedType属性返回Memberinfo对象从属的System.Type对象;对于继承的情形,ReflectedType属性返回的类型可能是(也可能不是)与实际声明该成员的类型相同;要获取声明该成员的类型,必须使用DeclaringType属性。
下面的程序列举了给定类型的所有成员。注意GetMembers方法所返回的数组将由字段、方法和嵌套类型组成的,它们交织在一起。这些成员的顺序将与它们被存储存模块元数据中的顺序相匹配。
这个实现只列出了类型的公有约定中的那部分成员。此外,该实现还跳过了该类型的所有静态成员。如果你要改变这种情形,就需要提供一个System.RefleCtion.BindingFlags参数,用于表明哪些成员将要被返回。
System.Reflection.BindingFlags是一个在整个反射类型的层次结构上通用的枚举类犁。大多数访问类型成员的方法将通过可选的BindingFlags参数,确定待要访问的目标成员。Static和Instance标志控制静态或实例成员(或两者)是古在被访问之列。Public和NonPublic标志控制公有或非公有实例成员是否在被访问之列。只有在基类型上而不是在派生类型上调用CetMembers方法时,才能看到基类型的private成员。最后要说的是,即使指定丁NonPublic和Static标志,基类型的静态成员也不在被访问之列。如果要包含这些成员,则必须指定FlattenHierarchy标志。
下面是前面例子的变体,它将列出类型的所有成员,而不管其访问级别或者静态/实例状态:
在这个例子中,惟一没有被列出的成员就是基类型的private成员,它们无法通过反射进行公开。kR鷁v^O(u哊