B
Bahl
Unregistered / Unconfirmed
GUEST, unregistred user!
[h1]深入探索属性[/h1]
如果你用Delphi编程的话就不会不用到属性。他们无所不在。打开对象观察器,你能看见属性页。再看看帮助文件,每个组件都有它的属性列表。写代码时你很有可能要键入属性名称。但是你知道属性是如何工作的吗?你了解它们的力量吗?你有效地利用了属性吗?如果你的答案是“是”,那么就别往下读了。不过我从e-mail,新闻组和谈话中了解到,很少有人能做到这一点。[h2]最大限度的封装[/h2]
Object Pascal属性是对大多数面向对象语言中的类所提供的封装原型的一种扩展。纯面向对象主义者坚持认为方法应该是公共的而数据域则是私有的。许多程序员对此回答道:“那么其他的代码怎么办?当你仅仅是读或设置一个值时,它们既不好写,又会影响性能。”
Object Pascal中的属性提供了对这种困境的强有力的解决方法。你声明一个公共的属性,将它映射到一个私有域或访问方法。如果你直接映射到一个域时,你不用写额外的代码,也不会影响性能。但是,封装规则得到了完全的保持:组件的使用者不需要去了解属性是如何实现的,组件编写者可以自由地改变它的实现。事实上,即使你改变了属性,将其映射到一个访问方法,你所要做的全部工作仅仅是重新编译一下代码而已。
组件编写者也能为属性与内在的数据类型改变存储结构。许多属性甚至没有映射到数据域。比如说,Delphi最常用的属性之一 —— Caption,不被组件作为内在的字符串保存。作为替代,它被发送到相应的Windows API function 函数来改变窗口的标题。
Delphi属性与Java Beans与COM的相应概念是不同的。在那些组件模型中,属性是由方法来解决的。
[h2]并不仅仅是一种语言的特征[/h2]
属性不只是语言的一部分,它们也被Delphi IDE与运行时间系统支持。
在Delphi中,属性经常与运行期类型信息相互协作。当你声明一个published的属性时,编译器生成内部的类型信息结构。这些结构主要被可视化支持与流系统使用。你能使用TypInfo单元的数据结构与例程来直接访问这些信息。
Published型的属性定义了一个对象的持久状态。使用Delphi环境中的Object Inspector或你自己的工具,你可以为一个对象设置初始值。这些随后被保存在DFM文件中,然后被包括在可执行文件中以备在对象生成时重新载入。为了更精确起见,DFM文件包括了建立组件与恢复它们的初始值的所有所需要的信息。
我把这叫做“持久性”,以与一个组件的运行时期状态(它包括了额外的动态信息)作对比。一个组件的部分启动状态是由程序与操作系统在运行期决定的。一个很明显的例子就是窗口控件的句柄,它每次都由操作系统重新分配。
要注意的是当使用流操作一个对象时,Delphi并不保存它的内部结构,也不把对象内存放到磁盘上。这些没有什么用,因为一个对象经常包括对内部对象与字符串的引用,它们要使用不同的内存区域。作为对保存对象数据结构的替代,流系统靠查询对象的published接口的每个属性来工作。
Delphi的流系统十分精巧,为默认属性值与特殊的流条件供给甚多。它也处理其他的特殊情况,包括额外的不基于某个属性的值,一个组件可以覆盖DefineProperties方法来保存它。
[h2]但是这并非全部:[/h2]
属性提供了许多额外的特性。我在我最近的一篇文章中提及到Delphi5中可使用的新的动态属性访问函数。另一个诀窍是增强了的数组属性,它基于你在方括号中指明的索引。这个索引经常是数字型的,不过也可以基于其他数据类型。Table组件使用了一个基于字符串的默认属性——表字段的名称。一个组件的数组属性现在能被设置为默认值。你可以忽略属性名称,替原来的组件加上方括号。
TableCountry ['Capital'] := "Rome" ;
替代
TableCountry.Cities ['Capital'] := "Rome" ;
这确实是一条捷径。我认为它是Object Pascal中与操作符重载最接近的东西了。
[h2]最后的一个问题[/h2]
你经不经常使用Ctrl-Shift-C?这可以看出你对属性的使用情况。这个组合键引发Class Completion功能,使得使用属性变成一件极其简单的事情。当你要增加一个全局变量时,键入property关键字,按下Ctrl-Shift-C就行了。
属性是Delphi体系结构的一个关键部分,也是它的成功之处。其他的可视化环境不具备这种既强大又灵活的特性,即使它有着更多的程序员用户。