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--
對於Delphi的TDateTime類型來說,它在實現上是以一個Double即8字節浮點數存儲的,
兼容OLE自動化中的時間格式。在Delphi.NET中繼承了這一存儲方式,而沒有直接使用BCL
提供的System.DateTime結構,不過仍然可以使用DateTime.FromOADate和
DateTime.ToOADate方法在System.DateTime和TDateTime之間雙向轉換。
格式存儲說明如下(from MSDN)
OLE 自動化日期以浮點數形式實現,其值爲距 1899 年 12 月 30 日午夜的天數。
例如,1899 年 12 月 31 日午夜表示爲 1.0;1900 年 1 月 1 日早晨 6 點表示爲 2.25;
1899 年 12 月 29 日午夜表示爲 -1.0;1899 年 12 月 29 日早晨 6 點表示爲 -1.25。
只有刻度值大於或等於正的或負的 31241376000000000 的 DateTime 對象纔可以表示爲
OLE 自動化日期。未初始化的 DateTime(即刻度值爲 0 的實例)將轉換爲等效的未初始化
OLE 自動化日期(即值爲 0.0 的日期,它表示 1899 年 12 月 30 日午夜)。
而Extended和Comp則只是一個簡單的別名。TGUID也只是一個簡單的重定義。
值得注意的是這裏的packed關鍵字。在CLR中,類的成員的物理位置對程序本身是沒有意義的,
CLR可以任意安排字段的位置以進行字節對齊等等優化操作。而爲了與現有代碼進行交互,CLR提供了
StructLayoutAttribute屬性允許限定類型的內部物理結構。在Delphi.NET中可以通過packed
關鍵字定義此結構的成員必須按定義的次序在內存中排列,即LayoutKind.Sequential的形式。
而在Delphi.NET中,所有的record在實現上都是ValueType的子類,即值類型,直接在堆棧上操作。
2.5.2 異常
同TObject一樣,Delphi中異常類繼承鏈的根Exception在Delphi.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一樣弱智,
簡單的功能還湊合,BCL的System.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.NET中RTL其它單元大多隻是對原有Delphi代碼的BCL封裝移植
技術難度並不大,對Delphi熟悉的讀者直接閱讀源程序可能比看我的文章更容易一些。
因此在分析完涉及到一些底層只是的Borland.Delphi.System後就此打住,
雖然有些虎頭蛇尾之嫌,但總免得背畫蛇添足之罵名 :)
至於構建在Delphi.NET的RLT之上的應用層架構VCL和以後可能要支持的CLX,
我就沒有太多精力寫文章介紹了。因爲就目前實現的VCL代碼來看,只是將以前的VCL代碼
managed化而已,實現上還是使用Windows那套傳統API管理窗口,與BCL的
System.Windows.Forms.Form根本不搭界。這樣一來在Delphi.NET中又多了一個選擇
VCL or CLX or System.Windows.Forms.Form...sigh,是好是壞只能待時間評判。
文中如果有解釋不夠清楚的地方,大家可以跟貼提出。也歡迎來信
於我討論Delphi.NET和CLR相關問題。 再次感謝大家的支持!:)