反射发出动态方法的情况
更新:2007 年 11 月
使用 DynamicMethod 类(.NET Framework 2.0 版中的新类)创建的动态方法为在运行时发出静态方法提供了增强的功能。动态方法通过多种方式扩展 System.Reflection.Emit 命名空间中的类型的功能:
因为不需要生成动态程序集、模块和类型以包含方法,所以它们的系统开销较小。
在长时间运行的应用程序中,它们可提供更好的资源占用状况,因为方法体使用的内存在无需再使用该方法时可以回收。
如果具有足够的安全权限,它们就能够使代码与现有程序集或类型关联,而该代码能够具有与内部类型和私有成员相同的可见性。
如果具有足够的安全权限,它们允许代码跳过实时 (JIT) 可见性检查并访问对象的私有的和受保护的数据。
动态方法可以使用 ILGenerator 对象发出 Microsoft 中间语言 (MSIL)。此外,动态方法还可以使用 DynamicILInfo 对象处理元数据标记和允许复杂客户端执行自己的 MSIL 生成的范围。
动态方法对需要生成运行时代码以保证性能的情形十分有用。本节中讨论的示例包括序列化、对象和关系数据库之间的映射、正则表达式、分部计算,以及用于需要运行库的语言的编译器。
有关动态方法生成的简单示例,请参见如何:定义和执行动态方法。
支持后期绑定调用的语言
当在编译时一个对象的类型未知时,动态方法对编译器编写器十分有用。对该对象的成员的调用必须在运行时解决,这通常会因操作参数列表产生附加的系统开销。下面的 Visual Basic 代码提供了一个这样的示例。
Sub Example(ByVal obj as Object)
' ...
obj.SomeMethod(x, y, z)
' ...
End Sub
编译器必须生成代码以查找 SomeMethod,准备对象数组形式的参数,并调用该方法。使用 InvokeMember 方法通过反射执行此类调用的性能不是很好。通过使用 System.Reflection.Emit 命名空间的成员创建动态程序集、模块、类型和方法可提高性能,但这样会导致工作集增大,代码复杂性增加。因为不需要创建动态程序集、模块或类型,动态方法可在动态方法签名与现有委托类型匹配时提供更有效的实现策略。此方法与使用 InvokeMember 方法相比效果更好。其性能不如虚调用,但因为不创建新类型,所以所需的工作集较小。另外,生成的 MSIL 和关联的本机代码在不需要时可以回收。
序列化
使用动态方法时不需要编写自定义序列化和反序列化代码。您可以根据简单规则标记一个可序列化类型,然后使用序列化引擎检查该类型的元数据,根据需要生成合适的序列化程序和反序列化程序,并针对该类型的实例运行生成的代码。
如果具有足够的安全权限,使用动态方法实现的序列化引擎可访问私有的和受保护的数据,以启用并非由引擎创建者创作的对象的序列化。
生成的方法在需要频繁使用时可以缓存,也可以简单地将其释放。
分部计算
分部计算(也称为程序专用化)是一种用于优化算法的技术,使用这种技术时一种或多种输入变量变化的速度会比其他输入的变化速度慢。分部计算生成专用的方法调用,这种方法调用将缓慢变化的输入当作常数处理,从而能够将其他优化作为一个整体应用于该算法。
使用此技术时,可能常常要将低性能的通用算法转换为高性能的专用算法。下面是一些示例:
编译一个 Regex 对象,以生成专用于匹配某个特定模式的程序。
将元数据驱动的序列化引擎编译成为专用于序列化和反序列化特定类型或类型集的程序。
编译一个 XML 架构,以生成专用于验证特定架构的程序。
将一个 XSLT 转换编译为专用于以特定方式转换 XML 文档的程序。
编译一个一般加密程序,该程序将使用任何指定密钥的数据加密为为特定密钥优化的程序。
动态方法可通过在运行时生成专用方法来实现分部计算。除性能改进外,动态方法还允许回收 MSIL 方法体和 JIT 编译器产生的相关机器码。这在长时间运行的程序中可能非常重要。
有关这些情形的更详细的说明,请参见反射发出应用程序方案。
在运行时生成自定义用户代码
许多应用程序或平台都具有扩展性机制,使得用户能够在应用程序正在运行时编写和执行自定义代码(通常是通过使用预定义函数)。通过使用动态方法生成此代码,应用程序或平台设计器可以在不影响性能的情况下减少所需函数的数目(从而减少内存占有量)并提供更大的灵活性。