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……