日期:2009-06-23  浏览次数:20482 次

反射类所使用的设计模式


System.Reflection 类中最常用的方法都使用统一的模式。Module、Type 和 MemberInfo 类的成员使用下表中所示的设计模式。

成员签名

说明

MyInstance[] FindXxx(filter, filterCriteria)

查找并返回经过筛选的类型列表,或者在当前类型没有实现任何匹配筛选器的类型的情况下返回空数组。
例如:Type.FindInterfaces

MyInstance GetXxx(<parameters>)

返回由 <parameters> 唯一指定的类型。如果不存在这样的类型,成员将返回 null(在 Microsoft Visual Basic .NET 中为 Nothing)。请注意,<parameters> 唯一地指定一个实例。
例如:Type.GetInterface

MyInstance[] GetXxxs()

返回所有公共类型。如果不存在公共类型,成员将返回空数组。
例如:Type.GetFields

MyInstance[] GetXxxs(<parameters>)

返回由 <parameters> 指定的所有类型。如果不存在这样的类型,成员将返回空数组。请注意,<parameters> 并不一定指定唯一的实例。

另一个常用的设计模式是使用委托。它们通常在反射中用来支持对返回对象数组的方法的结果集进行筛选。

反射的安全注意事项


如果提供对非公共信息的访问,将带来安全风险。如果允许代码了解某类型的非公共信息,那么该代码就有可能会访问您需要保密的代码、数据和其他信息。因此,.NET 框架安全性强制实施了一些规则,以确定可以使用哪种程度的反射来了解类型信息和访问类型。根据所执行的操作,可能会需要用于序列化的 ReflectionPermission 或 SecurityPermission。
在未经许可的情况下,所有代码都可以使用反射来执行以下任务:
  • 获取有关公共类型及其公共成员的信息。
  • 了解代码所在的模块和程序集。
  • 枚举公共类型。
  • 枚举与使用反射的代码位于同一程序集中的非公共类型。
  • 枚举程序集和模块。
  • 调用公共成员。
  • 对调用代码基类的族访问成员进行调用。
  • 对调用代码程序集的程序集访问成员进行调用。

若要了解有关非公共成员的信息,调用方必须具有 ReflectionPermission,此权限表示可以获取类型信息。如果不具有此权限,代码将无法使用反射通过 TypeAssemblyModule 上的 Get 方法来获取有关非公共成员(即使属于它自己的类)的信息。
要使用反射来调用无法按照通用类型系统可访问性规则访问的方法或访问这样的字段,必须向代码赋予成员访问的 ReflectionPermission
注意 建议使安全策略拒绝向源自 Internet 的代码授予 ReflectionPermission
序列化的 SecurityPermission 提供了获取和设置可序列化类型的任何非瞬态数据字段(即不仅存在于内存中的成员)的能力,而不论是否可以访问这些字段。此权限使代码能够了解并更改实例的私有状态。(除了授予正确的权限之外,还必须在元数据中将类型标记为可序列化。)

链接请求检查


如果方法或委托对某一 P 权限具有 LinkDemand,运行库将对该方法或委托的调用方执行链接请求检查,以验证已经向调用方授予 P 权限。在了解类型信息和进行调用时,都会执行此链接请求检查。
应避免编写采用 MethodInfo 参数的公共 API,尤其是对于高度信任的代码。否则,调用权限可能会很容易被恶意代码绕过。例如,设想在高度信任的代码中有一个采用 MethodInfo 参数的公共 API。假设此公共 API 对所提供的参数间接地调用 MethodInfo.Invoke。如果公共 API 没有执行必要的权限检查,由于安全性系统断定调用方受到高度信任,对 Invoke 方法的调用将始终会成功。即使恶意代码无权直接调用该方法,它仍然能够通过调用公共 API 来间接地调用该方法。

动态加载和使用类型


反射提供了由语言编译器(例如 Microsoft Visual Basic .NET 和 JScript)用来实现隐式晚期绑定的基础结构。绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。由于此过程在运行时而不是在编译时发生,所以称作晚期绑定。Visual Basic .NET 允许您在代码中使用隐式的晚期绑定;Visual Basic 编译器将调用一个帮助器方法,该方法使用反射来获取对象类型。传递给帮助器方法的参数有助于在运行时调用正确的方法。这些参数包括对其调用方法的实例(对象)、被调用方法的名称(字符串)和传递给被调用方法的参数(对象数组)。
在以下代码示例中,Visual Basic 编译器使用反射隐式地对其类型在编译时未知的对象调用方法。HelloWorld 类具有一个 PrintHello 方法,它输出与传递给 PrintHello 方法的某些文本串联的“Hello World”。在该示例中调用的 PrintHello 方法实际上是 Type.InvokeMember;Visual Basic 代码允许按照对象 (helloObj) 的类型在编译时已知(早期绑定)而不是在运行时已知(晚期绑定)的方式来调用 PrintHello 方法。

自定义绑定


除了由编译器隐式地用来进行晚期绑定之外,反射还可以在代码中显式地用来完成晚期绑定。
公共语言运行库支持多种编程语言,但这些语言的绑定规则各不相同。在早期绑定的情况下,代码生成器可以完全控制此绑定。但是,当通过反射进行晚期绑定时,必须用自定义绑定来控制绑定。Binder 类提供了对成员选择和调用的自定义控制。
利用自定义绑定,您可以在运行时加载程序集,获取有关该程序集中类型的信息,然后对该类型调用方法或访问该类型的字段或属性。如果您在编译时(例如当对象类型依赖于用户输入时)不知道对象的类型,就可以使用这种方法。以下代码示例显示在 HelloWorld.dll 程序集中使用反射动态调用的方法(首先在 Visual Basic .NET 中,然后在 C# 中)。
 [C#]
// This class is deployed as an assembly consisting of one DLL, 
// called HelloWorld.dll.
using System;
public class HelloWorld {
   // Constant Hello World string.
   private const String m_helloWorld = "Hello World";
   // Default public constructor.
   public HelloWorld() {
   }
  &nb