24.1.2 访问元数据

要访问元数据,我们先介绍下Type类,Type是一个表示类型声明的抽象基类,Type的实例可表示以下任何类型:

❑类

❑值类型

❑数组

❑接口

❑指针

❑枚举

❑构造泛型类型和泛型类型定义

❑构造泛型类型、泛型类型定义和泛型方法定义的类型实参和类型形参要获取一个Type类型,有三种方法:

❑使用Object类型中定义的GetType()方法,每个类型都有此方法。

❑使用typeof运算符,返回一个Type对象。

❑使用Type抽象类中定义的静态GetType()方法。

要注意的是,typeof运算符不能应用于一个表达式,它只能使用一个类型作为参数。接下来,是一段代码示例,演示的就是上述三种方法的使用,如代码清单24-2所示。

代码清单24-2 获取一个Type类型对象的三种方法


using System;

namespace ProgrammingCSharp4

{

class MetadataSample

{

private static void Main()

{

//声明并实例化MetadataSample类型的变量

MetadataSample ms=new MetadataSample();

//使用从Object类型继承来的GetType()方法获取Type对象

Console.WriteLine(ms.GetType());

//使用typeof运算符获取Type对象,该操作符不能用于表达式

Console.WriteLine(typeof(MetadataSample));

//使用Type.getType()静态方法获取Type对象

Console.WriteLine(Type.GetType("ProgrammingCSharp4.MetadataSample"));

}

}

}


上述代码的运行结果为:


ProgrammingCSharp4.MetadataSample

ProgrammingCSharp4.MetadataSample

ProgrammingCSharp4.MetadataSample

请按任意键继续……


从运行结果可见,这三种方式是等同的。只是需要注意的是,typeof运算符不能用于表达式,这一点尤其需要注意。

那么,我们接下来就探讨下如何访问到这些元数据信息。

首先,Type类是访问元数据的主要方式,通过Type类的成员可以取得非常多的信息,例如类型的构造函数、方法、属性、事件等,以及该类所在的模块和程序集信息。稍后我们即将学习的“特性(Attribute)”也可通过Type对象取得。接下来用一个例子来说明常用的元数据操作方法,如代码清单24-3所示。

关于代码清单24-3的说明如下:

❑在例子中,首先使用Assembly的LoadFile方法加载指定的程序集文件,这里使用的是本书示例代码的一个工程文件:ProgrammingCSharp4.exe。如果文件路径正确无误,那么LoadFile方法将返回一个Assembly类型的对象。

❑有了这样一个Assembly类型的对象,就可以读取到该程序集的相关元数据信息了,如程序集名称、路径、是否位于GAC中,等等。另外,一个程序集包含若干个Module,因此这里可以返回Module的集合(通过Assembly对象的GetModules()方法)。

❑因为一个程序集可能有多个Module,因此针对每个Module通过调用Module对象的GetTypes()实例方法返回一个Type类型的集合。

❑通过遍历Type类型的集合,从Type的实例对象获得了进一步的元数据信息:字段(FieldInfo)、属性(PropertyInfo)、方法(MethodInfo)等。

❑然后我们进一步通过MethodInfo对象获取方法的元数据,例如本地变量、参数等信息。

代码清单24-3 访问元数据的完整示例


using System;

using System.Collections.Generic;

using System.Reflection;

namespace ProgrammingCSharp4

{

class MetadataSample

{

//读取程序集的路径

private static string ReadAssemblyFullPath()

{

//定义程序集的路径和名称变量

string AssemblyPath,AssemblyName;

//输出程序集的元数据信息

Console.Write("Type the assembly path:");

//读取用户输入的程序集路径

AssemblyPath=Console.ReadLine();

Console.Write("Type the assembly name:");

//读取用户输入的程序集名称

AssemblyName=Console.ReadLine();

//返回程序集的路径

return AssemblyPath+@"\"+AssemblyName;

}

public static void Main()

{

//获取用户输入的程序集路径

string AssemblyFullPath=ReadAssemblyFullPath();

//列出程序集的信息

GenerateReport(Assembly.LoadFile(AssemblyFullPath));

}

//列出程序集的信息

private static void GenerateReport(Assembly assembly)

{

Console.WriteLine();

Console.WriteLine("\t\t\tAssembly's Info Is Below");

Console.WriteLine("-".PadLeft(70,'-'));

//输出程序集的名称

Console.WriteLine("Assembly's Name:\t{0}",assembly.GetName().Name);

//输出程序集的位置

Console.WriteLine("Assembly's Location:\t{0}",assembly.Location);

//输出程序集是否在GAC的布尔值

Console.WriteLine("Assembly Is In GAC:\t{0}",assembly.GlobalAssemblyCache);

//从assembly对象获取Modules(模块)实例数组

Module[]modules=assembly.GetModules();

foreach(Module module in modules)

{

//显示Module元数据

DisplayModuleInfo(module);

}

}

//读取并显示Module的元数据信息

private static void DisplayModuleInfo(Module module)

{

Console.WriteLine();

//显示模块的名称

Console.WriteLine("\t\t\tModule[{0}]",module.Name);

Console.WriteLine("-".PadLeft(70,'-'));

//通过module对象获取Type实例数组

Type[]types=module.GetTypes();

foreach(Type type in types)

{

DisplayTypeInfo(type);

}

}

//显示Type实例的元数据信息

private static void DisplayTypeInfo(Type type)

{

//输出类型的名称

Console.WriteLine("Type:\t{0}",type.Name);

Console.WriteLine("-".PadLeft(70,'-'));

Console.WriteLine("Fields:");

//获取FieldInfo实例数组

FieldInfo[]fieldInfos=type.GetFields();

//遍历显示各FieldInfo实例的元数据

foreach(FieldInfo info in fieldInfos)

{

//字段名称

Console.WriteLine("\tField Name:{0}",info.Name);

//字段类型

Console.WriteLine("\tField Type:{0}",info.FieldType);

//字段的可访问性是否为public

Console.WriteLine("\tField is Public:{0}",info.IsPublic);

}

Console.WriteLine("Properties:");

//获取PropertyInfo实例数组

PropertyInfo[]propertyInfos=type.GetProperties();

//遍历显示各PropertyInfo实例的元数据

foreach(PropertyInfo info in propertyInfos)

{

//属性名称

Console.WriteLine("\tProperty Name:{0}",info.Name);

//属性类型

Console.WriteLine("\tProperty Type:{0}",info.PropertyType);

//属性是否可读

Console.WriteLine("\tProperty is CanRead:{0}",info.CanRead);

//属性是否可写

Console.WriteLine("\tProperty is CanWrite:{0}",info.CanWrite);

Console.WriteLine();

}

Console.WriteLine("Methods:");

//获取MethodInfo实例数组

MethodInfo[]memberInfos=type.GetMethods();

int i=1;

//遍历显示各MethodInfo实例的元数据

foreach(MethodInfo info in memberInfos)

{

//显示MethodInfo实例的元数据

DisplayMethodInfo(info);

//每显示5条数据暂停一下

if(i++%5==0)

{

Wait();

}

}

Wait();

}

//显示MethodInfo实例的元数据信息

private static void DisplayMethodInfo(MethodInfo info)

{

Console.WriteLine("\tMethod:");

//获取MethodBody实例

MethodBody methodBody=info.GetMethodBody();

//方法名

Console.WriteLine("\t\tMethod Name:{0}",info.Name);

//如果方法体不为null

if(methodBody!=null)

{

//获取执行方法时操作数堆栈上的项的最大数目

Console.WriteLine("\t\tMethod MaxStackSize:\t{0}",methodBody.MaxStackSize);

//获取方法局部变量的集合

IList<LocalVariableInfo>variableInfos=methodBody.LocalVariables;

//如果有局部变量

if(variableInfos.Count>0)

{

Console.WriteLine("\t\tLocal Variables:");

//遍历显示LocalVariableInfo实例的元数据

foreach(LocalVariableInfo variableInfo in variableInfos)

{

//方法体内局部变量的索引

Console.WriteLine("\t\t\tLocal Variable Index:{0}",

variableInfo.LocalIndex);

//局部变量的类型

Console.WriteLine("\t\t\tLocal Variable Type:{0}",

variableInfo.LocalType);

Console.WriteLine();

}

}

else

{

Console.WriteLine("\t\tLocal Variables:none");

}

}

//获取ParameterInfo实例(参数)数组

ParameterInfo[]parameterInfos=info.GetParameters();

//如果有参数

if(parameterInfos.Length>0)

{

Console.WriteLine("\t\tParameters:");

//遍历显示各ParameterInfo实例的元数据

foreach(ParameterInfo parameterInfo in parameterInfos)

{

//参数名

Console.WriteLine("\t\t\tParameter Name:{0}\t",parameterInfo.Name);

//参数类型

Console.WriteLine("\t\t\tParameter Type:{0}",

parameterInfo.ParameterType.Name);

//该参数是否可选

Console.WriteLine("\t\t\tParameter IsOptional:{0}",

parameterInfo.IsOptional);

//是否为输出参数

Console.WriteLine("\t\t\tParameter IsOut:{0}",

parameterInfo.IsOut);

//是否为Retval参数

Console.WriteLine("\t\t\tParameter IsRetval:{0}",

parameterInfo.IsRetval);

Console.WriteLine();

}

}

else

{

Console.WriteLine("\t\tParameters:none");

}

}

//暂停,需要用户按任意键继续

private static void Wait()

{

Console.WriteLine("Press Any key to continue……");

Console.ReadKey();

}

}

}


上述代码的运行结果如下,这里只是部分结果,因为上述代码可以输出程序集中的所有模块及类型。


Type the assembly path:C:\WorkPlace\ProgrammingCSharp4\ProgrammingCSharp4\bin\Debug Type the assembly name:ProgrammingCSharp4.exe

Assembly's Info Is Below


Assembly's Name:ProgrammingCSharp4

Assembly's Location:C:\WorkPlace\ProgrammingCSharp4\ProgrammingCSharp4\bin\

Debug\ProgrammingCSharp4.exe

Assembly Is In GAC:False

Module[ProgrammingCSharp4.exe]


Type:AddressBookEntryClass


Fields:

Field Name:Name

Field Type:System.String

Field is Public:True

Field Name:MobilePhone

Field Type:System.String

Field Type:System.Int32

……

Field is Public:True

Properties:

Methods:

Method:

Method Name:Print

Method MaxStackSize:8

Local Variables:none

Parameters:none

Method:

Method Name:ToString

Method MaxStackSize:8

Local Variables:none

Parameters:none

Method:

Method Name:Equals

Method MaxStackSize:8

Local Variables:none

Parameters:

Parameter Name:obj

Parameter Type:Object

Parameter IsOptional:False

Parameter IsOut:False

Parameter IsRetval:False

Method:

Method Name:GetHashCode

Method MaxStackSize:8

Local Variables:none

Parameters:none

Method:

Method Name:GetType

Parameters:none

Press Any key to continue……