虽然遭遇到很多人的批评,但还是最终坚持下来。在这个系列中,主要是针对平时使用VCL的过程中,感觉Delphi没有处理好的地方。偶尔能提出一些自己的看法,权当谈资。更重要地是,我们可以借此学习到一些思想。
Delphi比起其他语言,有一个非常明显的特点,那就是类引用。典型的声明就是:
TComponentClass = class of TComponent;
可不要小看这个简单的声明,他可以让你直接访问到类的VMT。在李维先生的《Inside VCL》中谈到Delphi.NET的时候,也对此有特别的说明。对象和对象的差异,从一定意义上讲,就在于其所指向的VMT的不一样。
在VCL中,每个类的VMT都有自己的地址空间。于是VCL在判断两个类类型是否一致的时候,就是简单使用地址相等的方式。这里面最常用的就是Is操作符的实现了。遍历所有父类型,判断是否一致。
与VMT类似的,Delphi中的一些基本类型也是有自己的类型地址空间。这些类型的保存如果在C#中,你要说是类型反射。但在Delphi中,你更可以直接说成是为了实现published属性的类型检查。他们的类型检查也同样是使用地址空间来判断的。比如,判断一个属性是否为Boolean。你可以在TypInfo单元中找到相对应的实现代码。
我在实现一个TObjectGrid的时候,使用了RTTI信息来获取对象中属性的值。有一个需求是,判断某一个格子Cell所对应的属性是否为Boolean,如果是的话,使用CheckBox模式进行编辑。最初的实现代码是这样的:
if GetTypeData(PropInfo^.PropType^)^.BaseType^ = TypeInfo(Boolean) then
这段代码一直执行得很正常,直到有一天,我们的业务数据对象是从Dll返回。这个判断一下子实效了!
能够理解这个问题的人,应该能看出来,这是由于这个类型判断是使用地址空间引起的。Exe和Dll里有着自己独立的类型地址空间。当Dll加载到Exe中时,Dll中的类型地址空间经过重定位,因此Dll中的Boolean类型地址,和Exe中的类型地址不在时一个地址空间。我后来不得以,使用了名称的方式来判断类型是否一致。
如果你在Dll的接口中定义了TStrings类型的属性,那么在Exe中使用了TStrings类的Assign功能时会发生以外。这是因为TStrings实现Assign功能的时候,有这样一个判断:
if Source is TStrings then
经过上面的讲解,这段代码的判断显然返回False,那么其下面的赋值操作必然不会被执行。当然了,说到Is,应该能够想到,As操作符也会有类似的表现。因为As调用Is判断啊。
我上面讲了半天,其实就是说Exe和Dll之间的同一个类型不能够完全等同看待。特别是在接口之间传递的时候。当然了,我们可以说约定好,类型传递,不允许使用非基本类型。但是传递基础类的诱惑确实不小,这样,就少了很多处理了。
不过,正如LSuper大侠曾经提到的,在Delphi的Bpl之中就没有这个问题,因为他们的类型都是共享的。
应该说,针对VCL中的这个弱点,好的设计并不容易寻找。.NET中的Dll,已经实现了类型共享,这个Delphi的Bpl思想一致。想起来,应该是Dll的规范早于面向对象语言流行的原因。在早先的Dll定义中,并没有考虑复杂类型的接口传递。因此Delphi在上面实现会遇到很多问题。最大的就是语言和版本的问题。
解决了这两个问题,类型就可以自由穿过应用程序边界了。我并不是希望Delphi重新设计这个缺陷。不过,可惜的是,Delphi并没有将自己的Bpl推广开来。就如.NET修改了Dll的规范一样。如果Delphi的Bpl也直接变成Dll,呵呵,还不知道结果会是什么样呢。
BPL本来就是DLL啊。 楼主一定没有用过早期版本的Delphi。早期版本的Delphi包的扩展名不是BPL而就是DLL。后来为了和标准通用的DLL区分,才改名叫BPL的。 只可惜BPL不能通用。只能在Borland的开发工具上使用。就像VC的扩展DLL一样,只能在VC中使用。 。NET的DLL也和BPL类似,虽然导出了类型之类东西。但也只能在。NET中使用。 楼主说的问题不过通用和特化的关系而已。和生物进化一样,特化的种类往往更能适应某种特定的环境,但禁不起环境的变化。非特化种类对任何环境都适应性差一点,但是却是进化的主力。 |