苛评VCL: 穿不透的类型

虽然遭遇到很多人的批评,但还是最终坚持下来。在这个系列中,主要是针对平时使用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在上面实现会遇到很多问题。最大的就是语言和版本的问题。

  1. 不同的语言实现的Dll,如何保障类型相通?
  2. 不同版本的VCL类型,如何做到一致?

解决了这两个问题,类型就可以自由穿过应用程序边界了。我并不是希望Delphi重新设计这个缺陷。不过,可惜的是,Delphi并没有将自己的Bpl推广开来。就如.NET修改了Dll的规范一样。如果Delphi的Bpl也直接变成Dll,呵呵,还不知道结果会是什么样呢。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值