反射(或称 内省)是运行期间向一个程序询问其自身情况的过程。即便你可以在不 使用反射的情况下编写不少有用的程序,一些诸如元编程(code_generation)等技术 从深刻了解系统中的实体获益良多。
Class::MOP
(class_mop)简化了许多对象系统中的反射任务,但是很多有用的程序 并非彻底面向对象,很多程序也不会用到 Class::MOP
。因为没有一个正规的系统,Perl 中存在着一些有效进行反射的惯用语(idioms)。
为检查一个包是否存在在系统之中────即,如果一段代码在某一点执行了一条带包名称的 package
指令────就是检查它是否从 UNIVERSAL
继承下来,这可以通过检查该包是 否能够执行 can()
方法来实现:
虽然你 可以 使用名为 0
或 ''
……仅仅在你符号化地定义它们时,因为这些 并非 Perl 5 语法分析器禁止的标识符。 的包,can()
方法会在你将其作为调用 物时抛出一个方法调用异常。eval
代码块可以捕获这类异常。
你也 可以 对符号表的条目进行一一礼拜,但上述方法更加快速同时也更易理解。
由于 Perl 5 对包和类不加以严格区分,检查包存在性的技巧同时可用于检查一个类是否 存在。尚没有方法可以判断一个包是否是一个类。你 可以 用 can()
来检查一个包 是否可以执行 new()
,但这样无法保证任何找到的 new()
是方法,更不要说是构造 器了。
如果知道模块的名称,你可以通过查看 %INC
哈希来确定 Perl 是否相信它已经从 磁盘加载了这个模块。这个哈希和 @INC
是对应的;当 Perl 5 用 use
和 require
加载代码时,会在 %INC
中存储一个条目,其中键是欲加载模块的路径化名称,值是 模块完整的磁盘路径。就是说,加载 Modern::Perl
等效于:
路径的细节大部分取决于安装,但出于测试 Perl 是否成功加载一个模块目的,你可以把 模块名转换为正规文件形式并测试在 %INC
内是否存在:
没有什么阻止其它代码修改 %INC
。按你偏执的程度,你可以自行检查路径和预期的包 内容,但是拥有充足利用修改此变量的模块(诸如 Test::MockObject
和 Test::MockModule
) 可以这样做。修改 %INC
但理由不足的代码应该被替换。
无法保证某给定的模块是否提供版本号。即便如此,所有模块都继承自 UNIVERSAL
(universal) ,因此它们全含有 VERSION()
方法以供调用:
如果给定的模块不覆盖 VERSION()
或不包含包变量 $VERSION
,这个方法将返回一个未定义值。 类似地,如果该模块不存在,则对方法的调用将会失败。
确定函数是否存在最简单的机制就是对包名使用 can()
方法:
除非 $pkg
是合法的调用物,否则 Perl 将会抛出异常;如果对其合法性有任何怀疑, 可以将此方法调用包装在一个 eval
块内。注意,若函数所在的包没有正确地覆盖 can()
, 使用 AUTOLOAD()
(autoload)实现的函数可能会导致错误结果。这在其他包里便会 导致代码缺陷。
你可以用这个技巧决定一个模块的 import()
是否向当前名称空间导入某函数:
你也可以检查符号表和类型团来决定函数是否存在,但这种机制更简单也更好解释。
没有检查某给定函数究竟是函数还是方法的通用办法。一些函数身兼双职,既是函数 也是方法,尽管听上去过于复杂并且同时是失误,但这确实是一个允许的特性。
Perl 5 符号表是一个种类特别的哈希,其中的键是包全局符号的名称,值则是类型团。 类型团 是一种核心数据结构,它可以包含一个标量、一个数组、一个哈希、一个文 件句柄和一个函数。Perl 5 内部在查找这些变量时使用类型团。
通过在包名末尾添加双冒号,你可以将符号表当作哈希访问。例如,MonkeyGrinder 包 的符号表可以通过 %MonkeyGrinder::
访问。
你 可以 用 exists
操作符检查特定的符号名是否存在于符号表中(或随你喜好 修改符号表来 添加 或 or 删除 符号)。还应注意到一些对 Perl 5 核心的变更 已经修改了默认出现的类型团条目。特别是,Perl 5 早期版本总是为每个类型团默认提 供一个标量变量,现代化的 Perl 5 已经取消。
参见 perldoc perlmod
中的“Symbol Tables”小节以获取更多细节,最好采用不同于 本章介绍的反射技巧。
Hey! The above document had some coding errors, which are explained below:
- Around line 5:
-
A non-empty Z<>
- Around line 30:
-
Deleting unknown formatting code N<>