分享
 
 
 

Delphi.NET 内部实现分析(2)

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Delphi.NET 内部实现分析(2)

Unit类中剩下三个方法$WakeUp()由Delphi.NET内部使用;Finalization()完成

类似Delphi单元中finalization节的功能;最后一个HelloWorld()函数也是自动生成,

使用Unit类所在名字空间名称命名,如这里的HelloWorld,完成类似Delphi单元中

initialization节的功能,如在.dpr中则完成begin end.之间的程序功能。

从CLR角度来看,在载入Delphi.NET编译的配件后,通过Metadata定位到缺省指向的类

调用类的静态构造函数(即本例中的HelloWorld.Unit..cctor()函数),调用类的Main函数

(即本例中的HelloWorld.Unit.HelloWorld()方法)。

在程序入口类的静态构造函数.cctor中,要完成挂接Unit析构函数(本例中

HelloWorld.Unit.Finalization()函数)到系统一级终结调用列表上的工作。

HelloWorld.Unit..cctor中的伪代码如下

unit HelloWorld

type

Unit = class

procedure .cctor();

proceudre HelloWorld();

...

implementation

procedure Unit..cctor();

begin

Borland.Delphi.System.Unit._AddFinalization(

new Borland.Delphi.System._FinalizeHandler(

null, HelloWorld.Unit.Finalization));

HelloWorld.Unit.HelloWorld();

end;

HelloWorld.Unit.Finalization方法是HelloWorld.Unit类的finalization节

代码所在,用于在单元卸载时析构;Borland.Delphi.System._FinalizeHandler是

Borland.Delphi.System单元中定义的一个事件委托类型(Delegate);

Borland.Delphi.System.Unit._AddFinalization则是Borland.Delphi.System单元

的一个全局函数_AddFinalization。

在Borland.Delphi.System单元中可以看到其实现代码

//-----------------------------------------Borland.Delphi.System.pas--

type

_FinalizeHandler = procedure of object;

var

OnProcessExit: _FinalizeHandler;

procedure _AddFinalization(f: _FinalizeHandler);

begin

OnProcessExit := _FinalizeHandler(System.Delegate.Combine(

System.Delegate(@f), System.Delegate(@OnProcessExit)));

end;

{$IF SimpleFinalizer}

type

TFinalObject = class

public

procedure Finalize; override;

end;

procedure TFinalObject.Finalize;

begin

OnProcessExit;

inherited;

end;

{$ELSE}

procedure ProcessExitHook(sender: System.Object; eventArgs: System.EventArgs);

begin

OnProcessExit;

end;

{$IFEND}

{$IF SimpleFinalizer}

var

_GlobalFinalObject: TObject;

{$IFEND}

initialization

{$IF SimpleFinalizer}

{$MESSAGE WARN 'Using simple finalizer'}

_GlobalFinalObject := TFinalObject.Create;

{$ELSE}

System.AppDomain.CurrentDomain.add_ProcessExit(ProcessExitHook);

// System.AppDomain.CurrentDomain.add_ProcessExit(

// System.EventHandler.Create(nil, IntPtr(@ProcessExitHook)));

{$IFEND}

//-----------------------------------------Borland.Delphi.System.pas--

首先事件委托类型_FinalizeHandler的定义,和Delphi中定义类成员函数指针语法相同。

在Delphi中,此类指针实现上是以一个TMethod结构存在的,分别保存对象实例和成员函数的指针,

这与普通C/C++语言中的函数指针大相径庭。

//-----------------------------------------System.pas--

TMethod = record

Code, Data: Pointer;

end;

//-----------------------------------------System.pas--

而在CLR中事件的实现与Delphi非常类似(毕竟是同一个人设计的:),只不过用类包装了一下罢了,

具体讲解参见牛人Jeffrey Richter的《MS .NET Framework 程序设计》一书。

因此在Delphi.NET中对事件处理函数的定义可以原封不动。

与Delphi不同的是,CLR中的Deltegate可以同时由多个处理函数订阅,在C#一类直接支持事件的

语言中直接表述为 OnProcessExit += new _FinalizeHandler(...) 即可,而在Delphi.NET中

只好用_AddFinalization函数中这类精确的函数调用,希望Borland能在以后给Delphi.NET加上类似

C#语言中的表述语法,这样跟清晰明了一些,要是有运算符重载就跟爽了,反正底层都是用CLR实现。

接着Delphi.NET提供了两种实现单元一级finalization功能的方法

定义SimpleFinalizer的话,就使用较为简单的方法,直接由_GlobalFinalObject对象管理生命周期。

因为_GlobalFinalObject对象是一个全局对象,其生命期贯串整个程序,当其被释放时整个程序也就结束了。

而TFinalObject重载了Finalize方法,此方法如果被重载,则GC 垃圾回收在释放对象之前,会调用此方法。

这样就保证所有单元的finalization节在Borland.Delphi.System单元卸载之前,通过注册的析构事件

OnProcessExit被依次调用。

如果不定义SimpleFinalizer的话,则使用较复杂的方法。通过ProcessExitHook函数挂接到当前

AppDomain 应用程序域的进程结束事件上,在进程结束之前依次调用。

在挂接完析构处理函数后,.cctor会调用HelloWorld()指向单元初始化代码或程序执行代码。

如在本例中调用HelloWorld.Unit.HelloWorld()函数

public static void HelloWorld() {

Borland.Delphi.System.Unit.$WakeUp();

Borland.Delphi.System.Unit._WriteLn(

Borland.Delphi.System.Unit._Write0WString(

Borland.Delphi.System.Unit.Output, "Hello Delphi!"));

Borland.Delphi.System.Unit.__IOTest();

}

前后的$WakeUp()和__IOTest()分别负责唤醒和IO测试,目前没有什么作用。

中间的代码就是Writeln('Hello Delphi!');这行代码的实现,等我们具体解析

Borland.Delphi.System单元时再作评述。

1.3 类的实现

在分析了一个最简单的Delphi.NET程序后,我们来看看复杂一些的例子。

这个例子中定义了一个TDemo类,完成和上个例子相同的功能,只不过在类中完成。

//-----------------------------------------HelloWorld2.dpr--

Program HelloWorld;

{$APPTYPE CONSOLE}

type

TDemo = class

public

procedure Hello;

end;

{ TMemo }

procedure TDemo.Hello;

begin

Writeln('Hello Delphi!');

end;

begin

TDemo.Create.Hello;

end.

//-----------------------------------------HelloWorld2.dpr--

用ILDASM打开HelloWorld2.exe,可以发现在HelloWorld名字空间中增加了

一个TDemo类,HelloWorld.Unit.HelloWorld()函数中的代码也改为了

public static void HelloWorld() {

Borland.Delphi.System.Unit.$WakeUp();

new HelloWorld.TDemo().Hello();

}

接着我们来看看TDemo这个类的实现。

我们会发现TDemo是直接从System.Object类继承出来的。

在传统的Delphi语言中,如果在定义一个类的时候不显式指定其父类,则隐式将其父类

指定为TObject类;而在Delphi.NET中,因为要让系统架构融入CLR的标准类库的架构中,

不可能再为Delphi.NET定义一套继承树,所以所有TObject都变为了System.Object。

为最大限度兼容原有代码中的TObject,Delphi.NET中引入了class helper这个重要概念。

class helper这个概念可以说是一种非常巧妙的妥协,它允许用户向现有类树的结点

从外部添加新功能,但限定不能增加数据成员。因为Borland要将其VCL架构移植到CLR的BCL上,

虽然BCL和VCL结构上非常类似(本来就是一个人设计的),但从名字到功能都有一些细节上的差异,

而Borland没有BCL的源代码,M$也不可能允许其它厂商修改其源代码。这就造成了Borland的悖论,

要在无法修改BCL架构的情况下修改其架构来支持VCL,呵呵。

妥协的结果就是class helper这种补丁语法的出现。

之所以说是补丁语法,是因为class helper允许在不修改现有类的基础上,将新功能添加到其上。

而class helper又限定不能增加数据成员,这样就不会因为改变原有类的物理结构导致程序变动。

这样的效果形象地说来就是给原有类打上一个补丁,让原有的BCL的类无论看上去还是使用起来都很像

VCL的对应类。

例如在Delphi.NET中,TObject类型实际上就是System.Object的一个别名。

而TObjectHelper作为TObject的补丁,为TObject提供兼容VCL中TObject的函数,

这些函数实现上都是通过System.Object的方法完成的,只是名字和用法不同。

//-----------------------------------------Borland.Delphi.System.pas--

type

TObject = System.Object;

TObjectHelper = class helper for TObject

procedure Free;

function ClassType: TClass;

class function ClassName: string;

class function ClassNameIs(const Name: string): Boolean;

class function ClassParent: TClass;

class function ClassInfo: TObject;

class function InheritsFrom(AClass: TClass): Boolean;

class function MethodAddress(const Name: string): TObject;

class function SystemType: System.Type;

function FieldAddress(const Name: string): TObject;

procedure Dispatch(var Message);

end;

//-----------------------------------------Borland.Delphi.System.pas--

这样一来,Borland就简洁但并不完美的解决了这个悖论。不过可以预见,这种语法的出现,

必将在将来引发激烈的争论,因为无论如何,这种语法事实上冲击了OO设计思想的纯洁性。

后面我们分析Borland.Delphi.System单元时再详细讨论class helper的使用方法。

在TDemo类中,另一个值得注意的是名为@MetaTDemo的嵌套子类。

在Delphi中,每个类都有一个对应的元类 MetaClass,可以通过class of TMyClass定义

TMyClass的元类类型来访问,也可以从类方法中直接通过Self指针访问。元类在实现上就是在

此类对象所共有的VMT表。

而在Delphi.NET中,类的内存布局不再由Delphi完全控制,不大可能将VMT再绑定到每个对象上。

所以Borland通过一个以"@Meta+类名"作为类名称的嵌套子类来表示此类的元类。如TDemo的元类是

TDemo.@MetaTDemo类,从Borland.Delphi.System._TClass类继承出来。

//-----------------------------------------Borland.Delphi.System.pas--

_TClass = class;

TClass = class of TObject;

_TClass = class

protected

FInstanceType: System.RuntimeTypeHandle;

FClassParent: _TClass;

public

constructor Create; overload;

constructor Create(ATypeHandle: System.RuntimeTypeHandle); overload;

constructor Create(AType: System.Type); overload;

function ClassParent: TClass; virtual;

end;

TClassHelperBase = class(TObject)

public

FInstance: TObject;

end;

//-----------------------------------------Borland.Delphi.System.pas--

所有的元类如TDemo.@MetaTDemo类,都是继承自_TClass类,并使用类似TClassHelperBase的实现。

如TDemo.@MetaTDemo类就是以类似这样的伪代码定义的,只不过FInstance是静态成员变量

TDemo = class(TObject)

public

@MetaTDemo = class(_TClass)

public

FInstance: TObject; // static

class constructor StaticCreate;

constructor Create;

...

end;

class constructor TDemo.@MetaTDemo.StaticCreate;

begin

FInstance := @MetaTDemo.Create; // normal constructor

end;

constructor TDemo.@MetaTDemo.Create;

begin

inherited;

inherited FInstanceType := token of HelloWorld.TDemo;

end;

在@MetaTDemo的静态构造函数中,将@MetaTDemo.FInstance初始化为自身的实例;

在@MetaTDemo的构造函数中,将其表示类的Token放入_TClass.FInstanceType中,

我们后面分析Borland.Delphi.System单元时再详细解释。

这一小节我们大概了解了Delphi.NET是如何为原有Delphi类在源代码一级提供兼容性的,

分析了class helper和元类 MetaClass的实现原理。下一节我们将开始分析Delphi.NET的

核心单元Borland.Delphi.System,了解Delphi的基于TObject的单根结构是如何映射到

CLR的FCL基于System.Object的单根结构上,并看看几个我们熟悉的TObject方法的实现,

了解Delphi和Delphi.NET在类的内存布局上的不同。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有