作者:韩小明 来源:CSDN博客   酷勤网收集 2007-11-10

摘要
  我上面讲了半天,其实就是说Exe和Dll之间的同一个类型不能够完全等同看待。特别是在接口之间传递的时候。当然了,我们可以说约定好,类型传递,不允许使用非基本类型。但是传递基础类的诱惑确实不小,这样,就少了很多处理了。

虽然遭遇到很多人的批评,但还是最终坚持下来。在这个系列中,主要是针对平时使用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,呵呵,还不知道结果会是什么样呢。

评论

#   wr960204 发表于2007-02-08 09:18:21  IP: 61.235.75.*
BPL本来就是DLL啊。
楼主一定没有用过早期版本的Delphi。早期版本的Delphi包的扩展名不是BPL而就是DLL。后来为了和标准通用的DLL区分,才改名叫BPL的。
只可惜BPL不能通用。只能在Borland的开发工具上使用。就像VC的扩展DLL一样,只能在VC中使用。
。NET的DLL也和BPL类似,虽然导出了类型之类东西。但也只能在。NET中使用。
楼主说的问题不过通用和特化的关系而已。和生物进化一样,特化的种类往往更能适应某种特定的环境,但禁不起环境的变化。非特化种类对任何环境都适应性差一点,但是却是进化的主力。
#   BuilderChen 发表于2007-02-08 09:34:18  IP: 192.168.32.*
这也不能算Delphi的缺陷吧。不同语言的具体实现根本不同,怎么能保障相通呢?所以COM/DLL的设计思路就是通过接口来实现不同语言间的互通,必定要屏蔽不同语言的实现细节。你反过来又要用这个来传递Delphi的特定实现,这跟COM/DLL的思路根本相反了,那你还要用DLL干吗呢?直接用Bpl得了。反过来,Bpl建立在Delphi的实现上,提供了你想要的,但这也是Bpl无法代替DLL推广开来的原因。
象Lz的情况,如果要使用DLL,那么就应该使用通用的基本类型,复杂类型则用接口暴露出来,而不应该使用Delphi自己的类。

#   xiammy 发表于2007-02-08 10:22:24  IP: 218.249.220.*
早期版本的Delphi包的扩展名不是BPL而就是DLL。
——————————
这个确实不知道,学习!

#   wr960204 发表于2007-02-08 17:55:47  IP: 61.235.75.*
而且穿不透的类型应该是MS的DLL机制的问题。不应该算到VCL头上吧。

#   monkeyking1983 发表于2007-02-09 10:38:20  IP:
我觉得还应该考虑一些商业的东西。很多比微软好的产品都没有流行起来,为什么?不是Borland不想,而是他实在没办法。我也一直在看楼主的文章。感觉楼主虽然一直坚持评论,但是很多看问题的角度欠缺公允(如果说得有些不恰当还望楼主海涵),有些问题可能得站在更高的角度去审视。我觉得Delphi的有些问题不见得是没有想到,而是不能为之。本来Delphi VCL的设计相对当时的软件业来说已经比较超前了,而且他又要考虑到很多兼容问题。还有一个很关键的问题,恐怕我们也不能忽略。在我看来,其实Delphi后续版本的开发是有断层的。还记得李维是如何解释为什么Delphi里独特的类实例创建语法么? AObject := TObject.Create;
最初Anders是想实现垃圾回收机制,看看,这又是一个相当超前的设计。但是,后来Anders走了。Chuck走了。连接手Anders的小Danny也走了。那么最初Turbo Pascal,以及由它延续下来的Delphi核心团队的设计和理想谁去实现?在这种复杂的人事变动面前,Borland公司还能如此保持Delphi的纯洁性,我觉得已经是一个很了不起的事情了,Borland的工程师确实很棒。在我用Delphi之前,一直都在使用Turbo Pascal,从Delphi那里可以看到很多在TP中就有了的思想甚至是相同的类型构架。也许,核心团队的散失,真的让Borland失去了太多。

#   xiammy 发表于2007-02-09 14:11:18  IP: 218.249.220.*
to monkeyking1983: 很好。至于我的角度是否欠缺公允,很有可能。不过我说缺点并不是批判缺点。事实上李维先生的Inside VCL中已经有很多夸奖VCL的话了。我这里只是希望能对VCL进行一个完整的认识做一些贡献。

#   aimingoo 发表于2007-02-09 14:40:52  IP: 61.172.241.*
1. 这个问题不是所谓的类型问题,也不应该是在DLL内去解决的问题。如同BuilderChen所说,不认当把一个特定的OOP实现框架暴露在DLL的接口层,这样会使得这个DLL失去了共用代码的性质。

2. BPL通过DLL导出函数的方式使得不同的模块间可以使用同一个RTL。从本质上来说,它就是一种失去了通用性质的DLL。如同第一条所述的,它们的应用场合、背景和功能都有了差异。不能苛求说:要公用代码,还要公用OOP框架。这一点,Borland做不到,M$也做不到,因为没有哪两个OOP框架的实现方案是一样的。

3. “没有哪两个OOP框架的实现方案是一样的”的原因,基本上可以归结为共用代码的级别是在语言层,而非二进制代码层。所以COM宣称它的代码共用是在二进制代码层,就得到了更多的支持。同样,你也会发现COM的代码互用产生了比DLL更多的价值。——Windows的应用层基本上都构架在COM之上。

4. 二进制代码层的共用仍是有限的,因为它仍然受到硬件系统和编译系统的约束。所以COM的支持有几家,但也不是说就大一统了。更进一步的共用是在描述层,也就是说:与具体的语言和编译环境无关层面的共用。这表现在两个方面,一是接口,二是XML。接口是对代码功能的约束,XML是对代码交互的约束。你会看到,更多的厂商支持XML和接口(我这里不是指COM接口),就是因为大家都看到了这样做的价值:足够的抽象,以及没有强制性的实现要求。

5. 绕了一个圈子,我想说的是,楼主用一种纯为技术实现和使用的方式来考虑和评估架构的设计,用现在的环境下的需求来评估十年前的架构的设计。前者会使看问题不够深入,后者会有失公允。同样的一个“共用代码”和“OOP体系设计”的问题,在当前的理论研究和技术实现下的确可以有更佳的方案,但这并不是旧的架构的弊误或疏漏,而是新的应用环境产生的需求。

6. 楼主所谈论到的一些(我是说包括其它的几篇文章中的)问题,的确是DELPHI自身的局限,但大多数算不上问题。尤其是在DELPHI所适用的应用环境中,更是算不上问题。DELPHI的OOP、VCL以及COM实现方面的架构,能被广泛地使用上十年八年,不是没有它的道理的。而我们自身的架构设计能力,能保证一套系统(哪怕仅是一个OA)或者一个架构(哪怕仅是一个插件机制)能有效地使用上一年两年的,又有几个呢?因此我们大多数时候如同以管窥豹,所见者,斑斑点点而已。

7. 楼主现在的评论,有点为苛评而苛评的意思了。

#   eminemlhp 发表于2007-09-13 12:36:36  IP: 10.40.50.*
呵,参见《Delphi技术手册》第13页“共享对象”节,精辟

来自:http://blog.csdn.net/xiammy/archive/2007/02/08/1504961.aspx

分类: Borland技术 windows开发 开发工具

上一篇:苛评VCL: 孤独的Application   下一篇:苛评VCL: Amingoo的留言