重载(overloading)让你就特殊类型的强制转换和访问给予类自定义行为。有一种让变量 表现如同内置类型(标量、数组和哈希)的机制,它还带有更多特别的行为。这种机制使用 tie
关键字;它就是 捆绑(tying)。
最初对 tie
的使用是为生成存储在磁盘上而非内存的哈希。这允许从 Perl 中使用 dbm 文件, 同时也能够访问大于内存尺寸的文件。核心模块 Tie::File
提供了一个相似的系统,通过它 便可以处理过大的数据文件。
用于将 tie
变量的类必须遵从特定数据类型的特定的、有良好文档的接口。虽然核心模块 Tie::StdScalar
、Tie::StdArray
和 Tie::StdHash
在实践中更加有用,perldoc perltie
仍是这些接口的主要资源。从继承这些模块为开始,并仅重载你需要修改的特定方法。
给出一个要捆绑的变量,可以用如下语法捆绑它:
……其中第一个参数是要捆绑的变量,第二个是用于捆绑变量的类名,@args
是由捆绑函数 要求的可选参数列表。以 Tie::File
为例,就是要捆绑到数组上的文件名。
捆绑函数重组了构造器:TIESCALAR
、TIEARRAY()
、TIEHASH()
和 TIEHANDLE()
分别对应标量、数组、哈希和文件句柄。这些函数返回 tie()
关键字返回的相同对象。大 多数人会忽略它。
tied()
操作符在用于捆绑变量时返回相同对象,否则返回 undef
。再一次,很少有人 会使用返回的对象。相反,他们在使用 tied()
来决定一个变量是否捆绑时只检查对象的 布尔值。
要实现一个捆绑变量的类,从诸如 Tie::StdScalar
等核心模块继承,接着为要更改的操 作覆盖特定方法。就捆绑标量来说,你很可能需要覆盖 FETCH
和 STORE
,可能需要覆 盖 TIESCALAR()
,很可能忽略 DESTROY()
。
你可以用很少的代码创建一个记录对某标量读写的类:
假设 Logger
的类方法 log()
接受一个字符串和一个通过调用栈栈帧号报告位置的 数字。注意 Tie::StdScalar
并没有独立的 .pm 文件,因此你必须使用 Tie::Scalar
让其可用。
在 STORE()
和 FETCH()
方法内部,$self
如 bless 后的标量一般工作。向此标 量引用赋值改变标量的值而从中读取则返回它的值。
类似的,Tie::StdArray
和 Tie::StdHash
的方法对应地作用于 bless 后的数组和 哈希引用。perldoc perltie
文档解释了其支持的大量方法,除其他操作外,你可以从 中读取写入多个值。
捆绑变量也许像是为编写机灵的代码提供了一个有趣的机会,但是它们在几乎所有情况下 会导致令人迷惑的接口,很大一部分归因于它们非常罕见。除非你有充足的理由创建行为 和内置数据类型一致的对象,否则避免创建你自己的捆绑类。
充足的理由包括方便调试(使用记录标量来帮助你理解它的值在何处改变)和使得一些不 可能的操作变得可能(用节省内存的方式访问大文件)。捆绑变量作为对象的主要接口时 不太有用;用它配合你的整个接口来实现 tie()
接口通常太过困难和勉强。
最后的警语既悲哀又具说服力;相当多代码并不指望和捆绑变量一起工作。违反封装的代 码也许会妨碍对小聪明的正当且合法的使用。这是一种不幸,但是违反库代码的期待常常 引发缺陷,并且一般你没有能力修复这样的代码。
Hey! The above document had some coding errors, which are explained below:
- Around line 5:
-
A non-empty Z<>