Delphi物件模型(Part IV) (轉)

worldblog發表於2007-12-10
Delphi物件模型(Part IV) (轉)[@more@]

 

 

模型 (PART IV) :namespace prefix = o ns = "urn:schemas--com::office" />

 

Delphi對於物件導向的支援豐富而且強大。除了傳統的類和物件,Delphi還提供了介面,異常處理,多執行緒程式設計等特性。這一章節深入講解了Delphi的物件模型。讀者應當對標準的Pascal比較熟悉,並且對有關物件導向程式設計的基本法則有一定了解。

(本文的英文原文將Delphi與 Pascal統一表述為Delphi,可能有概念不清之嫌疑。但在大多數情況下,相信讀者能夠根據上下文來判定文中所述之Delphi的具體含義——譯者注。)

Object Life Cycle物件的生命週期

對於絕大多數物件,你構造器建立它,使用它,然後呼叫Free將它釋放。Delphi替你處理了所有其他細節。或許,有時,你需要了解更多有關Delphi物件模型內部的實現機制。例2-8顯示的是用於模擬Delphi建立和釋放一個物件的方法。

例 2-8:物件的生命週期

type


  TSomething = class


  procedure DoSomething;


  end;


var


  Ref: TSomething;


begin


  Ref := TSomething.Create;


  Ref.DoSomething;


  Ref.Free;


end;


//在構造器中被隱藏程式碼類似於這樣:


function TSomething.Create(IsClassRef: Boolean): TSomething;


begin


  if IsClassRef then


  try


  //獲得新的物件例項


  Self := TSomething.NewInstance;



 //NewInstance初始化物件,功能與InitInstance類似。假如你覆蓋了NewInstance,那麼,


//不要呼叫inherited NewInstance,而應當呼叫InitInstance。


//這個呼叫正如下面所示,因此你可以知道究竟發生了什麼,


//但是請記住通常Delphi實際上並不呼叫InitInstance。


  InitInstance(Self);



//做構造器真正要做的工作,無需用到類引用。


//注意,Delphi並不真的對構造器進行遞迴呼叫。


  Self.Create(False);



 Self.AfterConstruction;


  except


//若有任何異常發生,Delphi自動呼叫物件的析構器。


  Self.Destroy;


  end


  else


  Self.Create(False);


  Result := Self;


end;



//構造器中被隱藏的程式碼類似於這樣:


procedure TSomething.Destroy(Deallocate: Boolean);


begin


  if Deallocate then


  Self.BeforeDestruction;



 // Delphi並不真的遞迴呼叫析構器,但這裡是析構器真正發生作用的地方。


  Self.Destroy(False);



 if Deallocate then


  begin


// Delphi並不真的呼叫CleanupInstance而是呼叫FreeInstance來做清理的工作。


// 假如覆蓋了FreeInstance,不要呼叫inherited FreeInstance,而應當呼叫CleanupInstance


// 來清理字串、動態陣列以及可變型別欄位。


  Self.CleanupInstance;


//呼叫FreeInstance來釋放物件佔用的。


  Self.FreeInstance;


  end;


end;


 

Access Levels訪問級別

類似於C++和,Delphi提供了不同的訪問級別控制機制,來決定一個物件是否能其他物件的欄位,方法,以及屬性等。訪問級別分為以下幾種:

private 私有的
私有的方法,只能被該類自己的方法以及在同一單元的實現部分中定義的方法、過程和訪問。Delphi沒有C++風格的友類宣告也沒有Java風格的包級別上的訪問控制(package level access)。在Delphi中與之等同的是將包或者友類在同一單元宣告,這樣該單元中的所有類的私有及受保護部分都可以被訪問。

protected 受保護的
受保護的方法,可以被該類以及派生類的任何方法訪問。派生的類可以處於不同的單元中。

public 公用的
公開的方法沒有訪問限制。任何方法,函式,或者過程都可以訪問公用宣告的部分。除非使用$M+編譯指示符,否則預設的訪問級別是公用的。

published釋出的
釋出的宣告與公用的宣告基本上相同,唯一的不同是Delphi為釋出的宣告儲存執行時間資訊。有些宣告不能是釋出的;詳見第三章內容。如果類或者基類使用$M+指示符,則預設的訪問級別為釋出的。

提示:
 Delphi的在Form最開始沒有命名的部分宣告欄位和方法。因為TForm繼承自TPersistent,而在TPersistent中使用了$M+指示符,因此最開始部分的訪問級別為釋出的published。換句話說,IDE將欄位和方法宣告為釋出的。當Delphi裝載一個Form描述(.dfm檔案)時,它依賴於釋出的資訊來建立該Form物件。IDE依賴於Form類的最開始未命名部分的宣告。假如你修改那一部分的東西,可能會有導致IDE的Form編輯器不可用的危險。

automated 自動的
自動的宣告與公開的宣告很接近,唯一的不同就是Delphi將儲存更多的執行時間資訊以支援OLE自動化服務。自動化的宣告已經成陳舊了;你可以轉而使用Delphi的型別庫編輯器,但現在,他們為了向後相容而保留了一部分。Delphi的下一版本可能會將這些部分徹底根除。第三章更進一步的闡述有關自動型別的宣告。

派生的類可以提高屬性的訪問級別。透過在新的訪問級別上重新宣告屬性來做到這一點(比如將首受保護的改為公開的)。但是不能降低一個屬性的訪問級別,並且你不能改變一個欄位或者方法是否可見。你可以透過覆蓋一個虛方法,或者宣告一個覆蓋方法在同樣或者更高一級的訪問級別上,但你不能降低訪問級別。

 

Hiding a Constructor隱藏構造器

有時,一個類不用做公用的用途,而只是作為其他類的一個輔助類。這種情況下,你可能希望該輔助類的Create是私有的或者是受保護的,但這需要技巧。TObject宣告瞭一個公開的構造器:Create.因此即使該輔助類的構造器是私有的或者受保護的,你依然可以呼叫自TObject繼承而來的Create構造器。

儘管你不能改變繼承來的Create構造器的訪問,你還是可以透過另外一個公開的構造器來隱藏它。因為假如派生的構造器被呼叫,將引發一個異常。如下例:

type


  TPublic = class;


  TPrivateHelper = class


  private


//TPublic是唯一一個被允許呼叫真正的構造器的類


  constructor Create(Owner: TPublic);


  overload;


  public


//隱藏TObject.Create防止有人意外呼叫以試圖建立TPrivateHelper的例項


  constructor Create;


  reintroduce; overload;


  end;


  TPublic = class


  private


  fHelper: TPrivateHelper;


  public


  constructor Create;


  destructor Destroy;


  end;



constructor TPrivateHelper.Create;


begin


  raise Exception.Create('Programming error')


end;



constructor TPublic.Create;


begin


  //這是唯一一個TPrivateHelper能被建立的地方


  fHelper := TPrivateHelper.Create(Self);


end;


Properties屬性

屬性看起來更象是欄位,但可以起到與方法一樣的作用。屬性替代了讀取者和設定者(有時也稱為getter和setter),但更加機動和強大。屬性對於Delphi的IDE而言非常關鍵,同時,我們也可以在其他許多場合使用屬性。

屬性由一個讀者和一個寫者來負責讀取和設定屬性的值。讀者(Reader)可以是一個欄位名,一個集合欄位的選擇器,或是返回該屬性值的一個方法。寫者(writer)可以是一個欄位名,一個集合欄位的選擇器或者可以設定該屬性值的一個方法。你可以省略寫者,那麼該屬性為只讀屬性。當然,也可以省略掉讀者以建立一個只寫的屬性,但是使用這麼一個怪怪的屬性將很受限制。同時省略讀者和寫者是沒有意義的,因此Delphi不允許你這麼做。

大多數的讀者和寫者是欄位名稱或方法名稱,你也可以將其引向部分集合欄位(記錄和陣列)。如果一個讀者或寫者指向一個陣列元素,那麼陣列的必須是常量,並且該欄位不能為動態陣列。紀錄和陣列可以巢狀,甚至你可以使用可變型別的記錄。例2-9展示了一個擴充套件的矩形型別,與的TRect型別相似,但它是一個類,有屬性和方法。

例2-9:屬性的讀者與寫者

TRectEx = class(TPersistent)


  private


  R: TRect;


  function GetHeight: Integer;


   function GetWidth: Integer;


  procedure SetHeight(const Value: Integer);


  procedure SetWidth(const Value: Integer);


  public


  constructor Create(const R: TRect); overload;


  constructor Create(Left, Top, Right, Bottom: Integer); overload;


  constructor Create(const TopLeft, BottomRight: TPoint); overload;



 procedure Assign(: TPersistent); override;


 


 procedure Inflate(X, Y: Integer);


  procedure Intersect(const R: TRectEx);


  function IsEmpty: Boolean;


  function IsEqual(const R: TRectEx): Boolean;


  procedure Offset(X, Y: Integer);


  procedure Union(const R: TRectEx);



 property TopLeft: TPoint read R.TopLeft write R.TopLeft;


  property BottomRight: TPoint read R.BottomRight write R.BottomRight;


  property Rect: TRect read R write R;


  property Height: Integer read GetHeight write SetHeight;


  property Width: Integer read GetWidth write SetWidth;


  published


  property Left: Integer read R.Left write R.Left default 0;


  property Right: Integer read R.Right write R.Right default 0;


  property Top: Integer read R.Top write R.Top default 0;


  property Bottom: Integer read R.Bottom write R.Bottom default 0;


  end;


Array properties陣列型屬性

陣列型屬性總是與數量有關,並且帶有陣列的特性。陣列型屬性不能被髮布,但有許多其他用途。陣列的索引可以是任何型別的,並且你可以使用多維的陣列。對於陣列型屬性而言,你必須使用讀者和寫者的方法,因為你沒有辦法將一個陣列型的屬性直接對映到一個陣列型的欄位上。

可以將其中的一個陣列型屬性指定為預設的。則你可以直接使用物件引用以及一個陣列標號來訪問該項屬性而無需指明屬性名稱,如例子 2-10所示。

例 2-10:使用預設的陣列屬性

type


  TExample = class


  ...


  property Items[I: Integer]: Integer read GetItem write SetItem;


  property Chars[C: Char]: Char read GetChar write SetChar; default;


  end;


var


  Example: TExample;


  I: Integer;


  C: Char;


begin


  Example := TExample.Create;


  I := Example.Items[4];  //必須顯式的指明屬性名稱


  C := Example['X'];  //該陣列型屬性時預設的


  C := Example.Chars['X'];  //效果如前


 


/develop/read_article.?id=10403">PartI

PartII

PartIII

PartIV

PartV

PartVI

更多文章

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990740/,如需轉載,請註明出處,否則將追究法律責任。

相關文章