解析
反射技术在运行时可以获取程序集中每个类型的成员,包括字段、方法、属性、事件等,并进一步获取这些成员的详细信息。反射技术还可以动态载入外部程序集(私有程序集或共享程序集),获取程序集中类型的相关数据。有意思的是从外部动态载入的程序集还可通过晚期绑定,创建外部程序集中某类型的实例(对象),并且可以进一步调用其成员(如方法和属性)。在这个过程中,并不知道外部程序集的任何信息(甚至不知道该程序集是否存在)。
简而言之,反射技术以编程的方式获取程序集的元数据信息,通常情况下,只能通过ildasm.exe程序载入程序集或模块,才能读取其元数据。 NET的反射技术从程序集中获取各种细节类型元数据(如FieldInfo类描述了字段的细节)实例,并做进一步的操作,而这些细节类型大多数属于 System.Reflection命名空间。
说明:ildasm.exe是.Net Framework自带的程序,用于查看程序集的IL代码、元数据等信息。假设C盘是系统分区,则ildasm.exe程序通常位于C:/WINDOWS /Microsoft.NET/ Framework/v2.0.50727目录下。
System.Reflection命名空间包含了很多与反射有关的类型,通过这些类型可以获取指定类型的所有细节。本题要求获取指定类型中方法的完整信息,而System.Reflection命名空间下关于方法的类型为MethodInfo类型和ParameterInfo类型,所以只需要获取指定类型中方法的这2个类相关的对象即可。要达到这个目的,需要获取表示指定类型元数据的Type对象,Type对象即System.Type类的实例,该对象的成员可以返回System.Reflection命名空间下的类型,包括MethodInfo类型和ParameterInfo类型。在前面的示例中使用了对象的GetType方法获取类型名称,除此之外,还有多种获取Type对象的方法,如以下代码所示:
Type 对象引用变量 = 对象.GetType();
//调用Type类GetType静态方法的不同重载版本
Type 对象引用变量 = System.Type.GetType("类型的全饰名称");
Type 对象引用变量 = System.Type.GetType("类型的全饰名称",
是否抛出异常, 是否不区分大小写);
Type 对象引用变量 = System.Type.GetType("类型的全饰名称,
类型所属程序集的友好名称");
//使用C#的typeof运算符
Type 对象引用变量 = typeof(类型名称);
以上代码,调用Type类的GetType()静态方法,其中第3个重载版本用于获取外部私有程序集类型的Type对象。当获取指定类型的Type对象后,就可以通过其成员返回MethodInfo类型和ParameterInfo类型,以达到获取方法完整信息的目的,如以下代码所示:
using System.Reflection;
MethodInfo[] 方法数组 = Type.GetType("类型的全饰名称", false, false);
使用foreach语句遍历方法数组的每个子项,即可访问类型中方法的所有信息;
//在foreach语句中,调用方法数组子项的GetParameters方法,可返回ParameterInfo类型的参数数组
ParameterInfo参数数组 = 方法数组子项.GetParameters();
使用foreach语句遍历参数数组的每个子项,即可访问方法中参数的所有信息;
以上代码使用了foreach语句,可访问方法中参数的所有信息。
注意:默认情况下,反射只能获取公共成员的信息,而访问非公共成员可能会带来安全危险。因此,访问非公共成员的代码需要带有适当标志的 ReflectionPermission。此外,某些任务(例如执行非托管代码和序列化对象)还需要SecurityPermission。
面试例题7:如何利用反射获取当前程序集指定类型的信息?
考点:反射技术获取类型信息的方法以及获取类型成员集合的方法。
出现频率:★★
解答
获取指定类型的信息只需要获取该类型的Type对象,然后调用其成员即可。获取执行代码封装于ClassB类的静态方法Ref()中,用户输入不同的值,反射不同类型的详细信息。在目录下新建一个程序文件,并命名为ClassRef.cs,编写代码如代码7.7所示。
代码7.7 反射指定类型的信息:ClassRef.cs
//导入相应的命名空间
using System.Reflection;
class ClassRef
{
static void Main(string[] args)
{
while (true)
{
Console.Write("请输入所检测的类型名称:");
//接收用户输入值并赋值给input变量
string input = Console.ReadLine();
//如果用户输入"quit",则跳出循环
if (input == "quit")
{
break;
}
try
{
//调用Type类的静态方法GetType,并将Type对象引用返回给tp变量
Type tp = Type.GetType(input, false, false);
//调用ClassB的Ref静态方法,并传递tp对象
ClassB.Ref(tp);
}
//捕获空对象引用异常
catch (NullReferenceException e)
{
//输出异常信息
Console.WriteLine("异常信息:{0}", e.Message);
}
//捕获一般异常
catch (Exception e)
{
//输出异常信息
Console.WriteLine("异常信息:{0}", e.Message);
}
}
}
}
//定义2个接口类型IClassA和IClassB
public interface IClassA
{
string MethodA(string s);
}
public interface IClassB
{
string Name
{
get;
}
}
//定义ClassA,该类继承于接口类型IClassA和IClassB
class ClassA : IClassA,IClassB
{
public string _name;
public string Name
{
get
{
return _name;
}
}
//定义internal权限的MethodA方法
public string MethodA(string s)
{
_name = s;
return _name;
}
//定义public权限的MethodB方法
public ClassA(string s)
{
Console.WriteLine("所接收的参数是:{0}", s);
}
}
class ClassB
{
string _name;
//定义internal权限的MethodB方法
internal string MethodB(string s)
{
_name = s;
return _name;
}
//定义静态方法Ref,接收1个Type类型的参数
public static void Ref(Type tp)
{
//输出Type对象的基本属性
string FullName = tp.FullName;
Console.WriteLine("/n/t============={0}类型的信息=============", FullName);
Console.WriteLine("{0}是泛型类型吗?->{1}", FullName, tp.IsGenericType);
Console.WriteLine("{0}是接口类型吗?->{1}", FullName, tp.IsInterface);
Console.WriteLine("{0}是类类型吗?->{1}",FullName,tp.IsClass);
Console.WriteLine("{0}是COM对象吗?->{1}", FullName, tp.IsCOMObject);
Console.WriteLine("{0}是public访问类型吗?->{1}", FullName, tp.IsPublic);
Console.WriteLine("{0}是密封类型吗?->{1}", FullName, tp.IsSealed);
Console.WriteLine("{0}是值类型吗?->{1}", FullName, tp.IsValueType);
//获取Type对象的所有公共成员并保存到mi数组
MemberInfo[] mi = tp.GetMembers();
//遍历并输出mi数组所有的子项属性
foreach (MemberInfo m in mi)
{
Console.WriteLine("/t成员类别->{0},名称->{1}",m.MemberType, m.Name);
}
//获取Type对象所支持的接口并保存到Itp数组
Type[] Itp = tp.GetInterfaces();
//判断Itp数组是否有子项,如果有则输出子项属性
if (Itp.Length != 0)
{
foreach (Type t in Itp)
{
Console.WriteLine("{0}实现的接口类型->{1}", FullName, t.FullName);
}
}
else
{
Console.WriteLine("{0}不实现的任何接口类型", FullName);
}
}
}