在.NET运行时了解类型信息(2)

原创 2002年04月17日 09:03:00

反射类所使用的设计模式<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

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# 中)。

InvokeMember 和 CreateInstance

使用 Type.InvokeMember 可调用类型的成员。各个类(如 System.Activator 和 System.Reflection.Assembly)的 CreateInstance 方法是特殊形式的 InvokeMember,它们可新建特定类型的实例。Binder 类用于在这些方法中进行重载决策和参数强制。

以下代码示例显示参数强制(类型强制)和成员选择三种可能的组合。在第 1 种情况中,不需要任何参数强制或成员选择。在第 2 种情况中,只需要成员选择。在第 3 种情况中,只需要参数强制。

当多个成员具有相同的名称时,将需要重载决策。Binder.BindToMethod 和 Binder.BindToField 方法用于解析与单个成员的绑定。Binder.BindToMethod 还通过 getset 属性访问器提供了属性解析。

BindToMethod 返回要调用的 MethodBase,如果无法进行这样的调用,则返回 null。虽然 MethodBase 返回值通常是 match 参数中所包含的值之一,但它并不必如此。

当存在 ByRef 参数时,调用方可能需要取回这些参数。因此,如果 BindToMethod 已经操作参数数组,Binder 会允许将参数数组映射回它的初始形式。为了实现这一目的,必须向调用方保证参数的顺序不会改变。当按名称传递参数时,联编程序将重新排列参数数组,这就是调用方所见的参数。

可用成员集包括在类型和任何基类型中定义的成员。如果指定 BindingFlags.NonPublic,将返回该成员集中具有任何可访问性的成员。如果未指定 BindingFlags.NonPublic,联编程序就必须强制可访问性规则。当指定 PublicNonPublic 绑定标志后,还必须指定 InstanceStatic 绑定标志,否则不会返回任何成员。

如果只有一个成员具有给定名称,则不必进行回调,而在该方法上进行绑定。代码示例的第 1 种情况说明了这一点:只有一个 PrintBob 方法可用,因此不需要进行回调。

如果可用集中有多个成员,所有这些方法都将传递给 BindToMethod,它将选择正确的方法并将其返回。在代码示例的第 2 种情况下,有两个名为 PrintValue 的方法。对 BindToMethod 的调用将选择正确的方法。

ChangeType 执行参数强制(类型强制),以便将实参转换为选定方法的形参的类型。即使类型精确匹配,仍会为每个参数调用 ChangeType

在代码示例的第 3 种情况下,类型为 String 值为“5.5”的实参传递给 Double 类型的形参的方法。要使调用成功,必须将字符串值“5.5”转换为 double 值。ChangeType 会执行此转换。

ChangeType 仅执行无损或扩大转换,如下表所示。

源类型

目标类型

任何类型

它的基类型

任何类型

它所实现的接口

Char

UInt16、UInt32、Int32、UInt64、Int64、Single、Double

Byte

Char、UInt16、Int16、UInt32、Int32、UInt64、Int64、Single、Double

SByte

Int16、Int32、Int64、Single、Double

UInt16

UInt32、Int32、UInt64、Int64、Single、Double

Int16

Int32、Int64、Single、Double

UInt32

UInt64、Int64、Single、Double

Int32

Int64、Single、Double

UInt64

Single、Double

Int64

Single、Double

Single

Double

非引用类型

引用类型

Type 类具有 Get 方法,这些方法使用 Binder 类型的参数来解析对特定成员的引用。Type.GetConstructor、Type.GetMethod 和 Type.GetProperty 通过提供成员的签名信息来搜索当前类型的特定成员。Binder.SelectMethod 和 Binder.SelectProperty 则被回调来选择相应方法的给定签名信息。

访问默认成员

任何类型都可以具有默认成员,即在未给定任何成员名称时调用的成员。以下代码示例调用 Class1 的默认成员,并将它返回的值赋给 i

默认成员用 System.Reflection.DefaultMemberAttribute 进行标记。以下代码示例显示如何通过检索默认成员的自定义属性来检索默认成员。

使用 Type.GetDefaultMembers 可能会简单一些,并且会生成完全相同的结果。但是,如果在类型上定义了多个默认成员,GetDefaultMembers 就会引发 InvalidOperationException。以下代码示例显示 GetDefaultMembers 的语法。

通过以 String.Empty ("") 为成员名称来调用 Type.InvokeMember,就可以调用默认成员。InvokeMember 将从类型中检索 DefaultMemberAttribute,然后调用它。

访问默认参数值

某些语言(如 C++ 托管扩展和 Microsoft Visual Basic .NET)支持将默认值赋给参数。例如,以下代码示例是一个合法的 Visual Basic .NET 声明,此声明将默认值赋给两个参数。

您可以使用参数属性来分配默认的参数值。

通过确切指定哪些参数是默认值或略去尾部的默认参数,可以声明参数的默认值。例如,以下所有代码示例都是对 MyMethod 的有效调用。

要使用反射检索参数的默认值,请获取该参数的 ParameterInfo 对象,然后使用 ParameterInfo.DefaultValue 属性检索默认值。如果不存在默认值,该属性将返回 Value.DBNull。

以下代码示例向控制台显示 MyMethod 的默认值。

要调用包含具有默认值的参数的方法,请使用 Type.Missing 作为 InvokeMember 方法的参数值。这样,晚期绑定服务就能够为指定的参数值使用默认值。如果为不带默认值的参数传递 Type.Missing,则将引发 ArgumentException。有一点务必要注意,并非所有编译器的绑定机制都会遵守 Type.Missing 的这些规则。有些联编程序可能不支持此功能,或者可能以不同的方式来处理 Type.Missing。当使用 Type.Missing 时,默认值不必是结尾的参数值。

C# 语言不支持默认参数。

以下 Visual Basic .NET 代码示例显示如何调用具有默认参数的方法。

当使用上述方法时,即使调用方未指定任何值,仍会考虑尾部的默认参数。这是调用具有默认参数的方法时最常用的方式。

如果是使用 MethodBase.Invoke 来调用方法,则需要显式指定哪些参数是默认值,指定的方法是为所有没有值的参数传递一个包含 Type.Missing 的对象数组。

在.NET运行时了解类型信息(3)

访问自定义属性当属性与程序元素相关联后,可以使用反射来查询它们是否存在以及它们的值。用于查询属性的主要反射方法包含在 System.Reflection.MemberInfo.GetCustomAtt...
  • Paul_Ni
  • Paul_Ni
  • 2002年04月17日 09:03
  • 740

在.NET运行时了解类型信息(1)

通过反射命名空间中的类以及 System.Type,您可以获取有关已加载的程序集和在其中定义的类型(如类、接口和值类型)的信息。您也可以使用反射在运行时创建类型实例,然后调用和访问这些实例。反射概述公...
  • Paul_Ni
  • Paul_Ni
  • 2002年04月18日 09:26
  • 1318

运行时类型信息

RTTI(在运行时,识别一个对象的类型)可以在程序运行时发现和使用类型信息,这就打破了只能在编译期执行面向类型的操作的限制。使用它,可以查询某个Shape引用所指向的对象的确切类型,然后选择或者剔除特...
  • u012135300
  • u012135300
  • 2016年05月04日 22:30
  • 761

[面试] C/C++ 语法(六)—— RTTI(运行时类型信息)

RTTI(RunTime Type Information),顾名思义,对象运行时类型信息,以便在运行时进行类型识别。C++ 的对象识别可通过以下三个技术得以实现: (1)dynamic_cast 运...
  • lanchunhui
  • lanchunhui
  • 2016年03月27日 12:19
  • 516

了解运行时类型信息(RTTI)

翻阅近期的帖子,发现类似如下的问题被提及多次:如何根据名称找到控件如何根据名称找到对应的属性如何根据名称执行某个方法或事件那我们能不能做到呢,当然可以,那就让我们了解一下RTTI吧.运行时类型信(以下...
  • bdmh
  • bdmh
  • 2009年04月27日 13:29
  • 1779

运行时类型信息RTTI

我们在写C++代码的时候经常碰到使用dynamic_cast进行类型转换的情况,也都知道经过dynamic_cast的转换更加安全,因为dynamic_cast进行了类型检查。 但是可能很多人不知道d...
  • techx
  • techx
  • 2015年03月19日 18:45
  • 674

java编程思想读书笔记 第十四章 类型信息(上)

运行时类型信息可以使得在程序运行时发现和使用类型信息。Java在运行时识别对象和类信息的两种方式:一是RTTI,假定我们在编译时就知道了所有类型;二是“反射”机制,允许在运行时发现和使用类信息。 1...
  • abc709272013
  • abc709272013
  • 2016年10月17日 21:44
  • 226

MFC运行时类型信息(RTCI)

使用RTCI,需要满足两个条件:1、从CObject派生类。 2、在类声明中添加DECLARE_DYNAMIC宏,在实现文件中添加IMPLEMENT_DYNAMIC宏。// 代码使用静态库MFC. ...
  • hyhnoproblem
  • hyhnoproblem
  • 2011年01月21日 16:12
  • 7838

Java Base —— 运行时类型信息

RTTI: 在运行时,识别一个对象的类型JVM在加载*.class文件的时候会产生一个Class对象,JVM会检查该Class对象是否有语法错误并以其作为模板创建新的对象 通过Class对象可以管理...
  • zonlogic
  • zonlogic
  • 2015年12月08日 10:35
  • 99

RTTI 运行时类型信息

转载自: http://www.vckbase.com/document/viewdoc/?id=653如何在运行时确定对象类型(RTTI)作者:NorthTibet    RTTI 是“Runtim...
  • miyunhong
  • miyunhong
  • 2010年02月15日 14:21
  • 643
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:在.NET运行时了解类型信息(2)
举报原因:
原因补充:

(最多只允许输入30个字)