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

类别:Delphi 点击:0 评论:0 推荐:

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 [email protected];
  begin
    FInstance := @MetaTDemo.Create; // normal constructor
  end;

  constructor [email protected];
  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在类的内存布局上的不同。

 

 

本文地址:http://com.8s8s.com/it/it5156.htm