Delphi.NET 內部實現分析(5)

Delphi.NET 內部實現分析(5)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

2.5 其它

  
在瞭解了Borland.Delphi.System中的幾個重要部分之後,剩下的就是一些零零碎碎的掃尾工作。

2.5.1 
類型別名

  
爲兼容Delphi中的特有類型,Borland.Delphi.System單元中定義了很多類型別名。
如我們前面分析過的TObject就是System.Object的別名。
//-----------------------------------------Borland.Delphi.System.pas--
type
  TDateTime = type Double;
  Extended = type Double;   // 80 bit reals are unique to the Intel x86 architecture
  Comp = Int64 deprecated;

  TGUID = packed record
    D1: LongWord;
    D2: Word;
    D3: Word;
    D4: array[0..7] of Byte;
  end;
//-----------------------------------------Borland.Delphi.System.pas--
  
對於DelphiTDateTime類型來說,它在實現上是以一個Double8字節浮點數存儲的,
兼容OLE自動化中的時間格式。在Delphi.NET中繼承了這一存儲方式,而沒有直接使用BCL
提供的System.DateTime結構,不過仍然可以使用DateTime.FromOADate
DateTime.ToOADate
方法在System.DateTimeTDateTime之間雙向轉換。
格式存儲說明如下(from MSDN)

  OLE 
自動化日期以浮點數形式實現,其值爲距 1899  12  30 日午夜的天數。
例如,1899  12  31 日午夜表示爲 1.01900  1  1 日早晨 6 點表示爲 2.25
1899 
 12  29 日午夜表示爲 -1.01899  12  29 日早晨 6 點表示爲 -1.25
  
只有刻度值大於或等於正的或負的 31241376000000000  DateTime 對象纔可以表示爲
OLE 
自動化日期。未初始化的 DateTime(即刻度值爲 0 的實例)將轉換爲等效的未初始化
OLE 
自動化日期(即值爲 0.0 的日期,它表示 1899  12  30 日午夜)。

  
ExtendedComp則只是一個簡單的別名。TGUID也只是一個簡單的重定義。
  
值得注意的是這裏的packed關鍵字。在CLR中,類的成員的物理位置對程序本身是沒有意義的,
CLR
可以任意安排字段的位置以進行字節對齊等等優化操作。而爲了與現有代碼進行交互,CLR提供了
StructLayoutAttribute
屬性允許限定類型的內部物理結構。在Delphi.NET中可以通過packed
關鍵字定義此結構的成員必須按定義的次序在內存中排列,即LayoutKind.Sequential的形式。
而在Delphi.NET中,所有的record在實現上都是ValueType的子類,即值類型,直接在堆棧上操作。

2.5.2 
異常

  
TObject一樣,Delphi中異常類繼承鏈的根ExceptionDelphi.NET中,也只是BCL的異常類
System.Exception
的一個別名,而只是通過class helper提供源代碼級兼容性。
//-----------------------------------------Borland.Delphi.System.pas--
  Exception = System.Exception;
  ExceptionHelper = class helper for Exception
  private
    class function CreateMsg(const Msg: string): Exception;
    function GetHelpContext: Integer;
    procedure SetHelpContext(AHelpContext: Integer);
  public
    // Doc: The help context return zero(0) if exception's helplink property
    //  cannot be parsed into an integer.
    property HelpContext: Integer read GetHelpContext write SetHelpContext;

    // constructor Create(const Msg: string) is provided by the CLR class
    class function CreateFmt(const Msg: string; const Args: array of const): Exception;
    class function CreateHelp(const Msg: string; AHelpContext: Integer): Exception;
    class function CreateFmtHelp(const Msg: string; const Args: array of const;
      AHelpContext: Integer): Exception;
  end;
  ExceptionClass = class of Exception;

  EConvertError = class(Exception);

threadvar
  _ExceptObject: TObject;

function ExceptObject: TObject;
//-----------------------------------------Borland.Delphi.System.pas--
  
幾個類函數Create*(...)都只是對System.Exception構造函數的包裝而已,
用於保存異常相關幫助文件路徑的System.Exception.HelpLink屬性,則被Delphi.NET
用於保存HelpContext,因爲在VCL中,所有的異常都是共用一個幫助文件TApplication.HelpFile
  
ExceptObject函數則由編譯器支持,提供訪問當前被拋出的異常的手段。此函數在Delphi
通過VCL維護的SEH異常鏈獲取,而在Delphi.NET中只好由編譯器在異常被截獲後手動賦值給線程局部存儲變量
_ExceptObject
,然後再由ExceptObject函數讀出,這只是語法一級兼容Delphi而已。
  
不過這個預覽版好像沒有提供threadvar的支持,只是把它簡單的放到Borland.Delphi.System單元的
全局變量中,作爲自動生成Unit類的一個靜態成員變量而已,並非線程安全!在Borland.Delphi.Classes
中甚至直接把TThread的定義註釋掉,實現不提供。估計還在開發中……sigh

2.5.3 
斷言(Assert)

  
斷言負責在調試模式下檢測一個條件是否成立,失敗則引發異常。
//-----------------------------------------Borland.Delphi.System.pas--
interface

{ debugging functions }

procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);

type
  EAssertionFailed = class(Exception)
  public
    ShortMessage: string;
    Filename: string;
    LineNumber: Integer;
  end;

resourcestring
  SAssertionFailed = '%s (%s at %d)';

implementation

procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
var
  LException: EAssertionFailed;
begin
  { TODO : Should we be using System.Diagnostics.Debug.Assert/Fail? }
  {$MESSAGE WARN 'Assert doesn''t use CreateFmt because it returns the wrong type'}
  LException := EAssertionFailed.Create(Format(SAssertionFailed, [Message, Filename, LineNumber]));
  LException.ShortMessage := Message;
  LException.Filename := Filename;
  LException.LineNumber := LineNumber;
  raise LException;
end;
//-----------------------------------------Borland.Delphi.System.pas--
  _Assert
函數的定義基本上是EAssertionFailed異常的一個簡單包裝。因爲Delphi沒有提供類似
C++
__FILE____LINE__之類的預定義宏,故而只能由編譯器在用戶使用到Assert函數時,
將當前文件名、行號等調試信息編譯進代碼中,即在編譯器一級提供斷言實現。

//-----------------------------------------Borland.Delphi.System.pas--
function Assigned(const AGCHandle: GCHandle): boolean;
begin
  Result := AGCHandle.IsAllocated;
end;
//-----------------------------------------Borland.Delphi.System.pas--

//-----------------------------------------GCHandle.cs--
namespace System.Runtime.InteropServices {
  public struct GCHandle {
    private IntPtr m_handle;

    public bool IsAllocated { get { return m_handle != IntPtr.Zero; } }
  }
}
//-----------------------------------------GCHandle.cs--
  Assigned
則是對一個引用類型變量進行檢測,與Delphi類似,Delphi.NET中直接通過檢測引用類型值
是否爲空(null)判斷是否有效,但對於值類型則將之與0進行比較。

2.5.4 
隨機數

  Delphi.NET
中的隨機數基本上是對BCL相關類的一個簡單包裝,而BCL的隨機數算法與VCL一樣弱智,
簡單的功能還湊合,BCLSystem.Security.Cryptography.RandomNumberGenerator相比之下
隨機性就好得多,不過要付出速度上的代價。

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

{ random functions }

var
  RandSeed: LongInt = 0;

procedure Randomize;

function Random(const ARange: Integer): Integer; overload;
function Random: Extended; overload;

implementation

var
  LastRandSeed: Integer = -1;
  RandomEngine: System.Random;

procedure InitRandom;
begin
  if LastRandSeed <> RandSeed then
  begin
    if RandSeed = 0 then
      RandomEngine := System.Random.Create
    else
      RandomEngine := System.Random.Create(RandSeed);
    LastRandSeed := RandSeed;
  end;
end;

procedure Randomize;
begin
  LastRandSeed := -1;
  RandSeed := 0;
end;

function Random(const ARange: Integer): Integer;
begin
  InitRandom;
  Result := RandomEngine.Next(ARange);
end;

function Random: Extended;
begin
  InitRandom;
  Result := RandomEngine.NextDouble;
end;
//-----------------------------------------Borland.Delphi.System.pas—

 

Delphi.NET 內部實現分析(6)

 

不好意思,實在想不到有什麼值得說的了,只好草草結束了 

2.5.5 
其它.其它

  Borland.Delphi.System
單元雖然比Delphi中的System單元小的多,
但其中也充斥着大量常用但是實現代碼枯燥的函數。如

  
數字處理函數集
  
字符串處理函數集
  
命令行信息獲取函數集(CmdLine/ParamCount/ParamStr)
  
格式化輸出函數集(Format)
  
文本文件(Text類型,而File類型文件不提供支持)///寫等函數集
  
動態數組管理(System.Array類型的簡單包裝)
  
當前路徑及目錄操作函數集
  
集合類型(CLR中並無集合概念,Set實現上是字節數組的簡單包裝)
  
其它一些雜項函數
  
等等等等

  
這些零散代碼基本上都是對BCL相應功能的簡單包裝,這裏就不一一詳述了。

2.5.6 
小結

  
至此,對Delphi.NET中核心單元Borland.Delphi.System單元的介紹
就告一段落了。通過對此單元的分析,我們大致瞭解了Delphi.NET中對於Delphi
一些核心概念的實現或模仿思路,但不排除在正式版中實現有所改變。


題外話:

  
首先感謝大家的熱心支持,這是督促我這個懶人寫完文章(哪怕是草草結束)的最大動力,
也希望這篇文章能夠對大家瞭解即將到來的Delphi.NET、迎接.NET時代有所幫助。
  
這個系列文章到這裏估計也就暫時告一段落了,因爲時間倉促、準備不足而且
筆者水平有限,只涉及到Delphi.NET在實現上與Delphi不同的部分內容,
Delphi.NET的改變來說只是冰山一角而已。本來還想擴大一點分析面,
但考慮到Delphi.NETRTL其它單元大多隻是對原有Delphi代碼的BCL封裝移植
技術難度並不大,對Delphi熟悉的讀者直接閱讀源程序可能比看我的文章更容易一些。
因此在分析完涉及到一些底層只是的Borland.Delphi.System後就此打住,
雖然有些虎頭蛇尾之嫌,但總免得背畫蛇添足之罵名 :)
  
至於構建在Delphi.NETRLT之上的應用層架構VCL和以後可能要支持的CLX
我就沒有太多精力寫文章介紹了。因爲就目前實現的VCL代碼來看,只是將以前的VCL代碼
managed
化而已,實現上還是使用Windows那套傳統API管理窗口,與BCL
System.Windows.Forms.Form
根本不搭界。這樣一來在Delphi.NET中又多了一個選擇
VCL or CLX or System.Windows.Forms.Form...sigh
,是好是壞只能待時間評判。
  
文中如果有解釋不夠清楚的地方,大家可以跟貼提出。也歡迎來信
於我討論Delphi.NETCLR相關問題。  再次感謝大家的支持!:)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章