Delphi.NET 内部实现分析(2)
来源:百度文库 编辑:神马文学网 时间:2024/04/28 22:17:28
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在类的内存布局上的不同。
类似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在类的内存布局上的不同。
Delphi.NET 内部实现分析(2)
Delphi.NET 内部实现分析(1)
Delphi.NET 内部实现分析(3.1)
Delphi.NET 内部实现分析(3.2)
Delphi.NET 内部实现分析(3.3)
Delphi.NET 内部实现分析(3.4)
Delphi.NET 内部实现分析(4)
Delphi.NET 内部实现分析(5)
Delphi.NET 内部实现分析(5)
Delphi.NET 内部实现分析(4)
Delphi.NET 内部实现分析(3.4)
Delphi屏幕截图技术实现2
PAM 的应用开发和内部实现源码分析
Delphi中PING的实现
Delphi和Java实现webservice
用DELPHI实现为NT添加用户
Delphi中PING的实现ewe
Delphi中PING的实现a
Delphi屏幕截图技术实现d
使用.NET实现断点续传
delphi多线程编程2
在Delphi中实现VFP6的查询速度 - Delphi - 文档中心 - 源码天空
浅析多层结构及其在Delphi中的实现<一>
Delphi中不使用第三方控件实现XP风格