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

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

 

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

 

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

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

Indexed properties型的屬性

你可以將許多不同的屬性對映到一個相同的讀或寫的方法,只需為每個屬性指定一個索引值。這個索引值將被傳遞給讀或寫的方法以區別不同屬性。

你甚至可以將陣列型和索引型混合使用。讀者和寫者的方法將會區分他們——將陣列的索引作為第一個引數,接著才是索引值。

Default values預設值

屬性還用到stored和default兩個指示符。這裡的資訊與 Delphi的Object Pascal語言的特性關係不大,但是Delphi的將其用作儲存Form的描述。stored指示符的值可以是一個Boolean型別的常量,也可以是一個Boolean型的欄位,或是一個不需任何引數直接返回Boolean值方法。default指示符的值應當是與該屬性的型別相同的一個常量。只有列舉型,整形以及集合型別的屬性可以有一個預設(default)的值。stored和default指示符只對釋出的屬性才有意義。

為了將預設的陣列數型與預設的值區別開來,預設陣列的指示符以一個分號跟在屬性的宣告後面。預設值指示符則直接置於屬性宣告以後。參見第五章瞭解有關default指示符的內容。

Using properties使用屬性

通常情況下定義一個類時,我們將所有的欄位設定為私有的,然後宣告許多公開的屬性來訪問他們。然而Delphi並未對屬性直接訪問欄位方式表示不滿。但是使用屬性,你可以在未來的某個時刻改變屬性的實現,比如在欄位值發生改變是增加校驗等。你也可以使用屬性來強制訪問級別,比如當一個欄位的之不能被改變時使用一個只讀的屬性。例 2-11顯示的是宣告和使用屬性的幾種方式。

例 2-11:宣告和使用屬性

type


  TCustomer = record


  Name: string;


  TaxIDNumber: string[9];


  end;


  TAccount = class


  private


  fCustomer: TCustomer;


  fBalance: Currency;


  fNumber: Cardinal;


  procedure SetBalance(NewBalance: Currency);


  published


  property Balance: Currency read fBalance write SetBalance;


  property Number: Cardinal read fNumber; //不可改變


  property CustName: string read fCustomer.Name;


  end;


  TSavingsAccount = class(TAccount)


  private


  fInterestRate: Integer;


  published


  property InterestRate: Integer read fInterestRate


  write fInterestRate default DefaultInterestRate;


  end;


  TLinkedAccount = class(TObject)


  private


  fAccounts: array[0..1] of TAccount;


  function GetAccount(Index: Integer): TAccount;


  public


//兩種屬性訪問陣列的方法:使用索引或者引用一個陣列元素


  property Checking: TAccount index 0 read GetAccount;


  property Savings:  TAccount read fAccounts[1];


  end;


  TAccountList = class


  private


  fList: TList;


  function GetAccount(Index: Integer): TAccount;


  procedure SetAccount(Index: Integer; Account: TAccount);


  function GetCount: Integer;


  protected


  property List: TList read fList;


  public


  property Count: Integer read GetCount;


  property Accounts[Index: Integer]: TAccount read GetAccount


  write SetAccount; default;


  end;



procedure TAccount.SetBalance(NewBalance: Currency);


begin


  if NewBalance < 0 then


  raise EOverdrawnException.Create;


  fBalance := NewBalance;


end;



function TLinkedAccount.GetAccount(Index: Integer): TAccount;


begin


  Result := fAccounts[Index]


end;



function TAccountList.GetCount: Integer;


begin


  Result := List.Count


end;



function TAccountList.GetAccount(Index: Integer): TAccount;


begin


  Result := List[Index]


end;



procedure TAccountList.SetAccount(Index: Integer; Account: TAccount);


begin


  fList[Index] := Account


end; 


Class-type properties物件型別的屬性

物件型別的屬性需要引起格外的關注。使用物件型別的時候,最好由物件的擁有者負責管理物件屬性。也就是說,單單儲存一個物件引用是不夠的,需要保留一分該物件屬性的一個副本。是用一個寫者方法來做到這一點。Delphi的IDE要求所有釋出的屬性滿足這個要求,同時也對未釋出的屬性也產生影響。

此規則的唯一的例外是,屬性儲存的是對Form上的的引用。這種情況下,屬性必須儲存物件引用而非元件的副本。

Delphi的IDE只在.dfm中存放元件名稱以儲存元件引用的值。當.dfm被裝載時,Delphi查詢元件名以恢復物件引用的值。如果你必須要在一個元件內部儲存一個完整的元件,則你必須實現對內部元件的屬性的訪問委託。

確認屬性的類繼承自TPersistent而來,並且該類覆蓋了Assign方法。透過Assign來實現屬性的寫方法。(TPersistent,在Classes單元中定義,並不是必須的,但確是一個最簡單的方法——複製一個物件。否則,你將花費兩倍的代價在任何其他用到的類中書寫Assigned方法。)讀方法可以提供對欄位的直接訪問。如果該物件有一個OnChange的事件,你最好將設定其值以瞭解物件何時作了改變。例子 2-12顯示了一個典型的使用物件屬性的方法。例子中定義了一個圖形,用於在需要時在其範圍內以平鋪的方式顯示點陣圖。屬性Bitmap存放了一個TBitmap物件。

例 2-12:宣告和使用物件型別的屬性

unit Tile;


interface


uses SysUtils, Classes, Controls, Graphics;


type


// Tile a bitmap


TTile = class(TGraphicControl)


private


fBitmap: TBitmap;


procedure SetBitmap(NewBitmap: TBitmap);


procedure BitmapChanged(Sender: TObject);


protected


procedure Paint; override;


public


constructor Create(Owner: TComponent); override;


destructor Destroy; override;


published


property Align;


property Bitmap: TBitmap read fBitmap write SetBitmap;


property OnClick;


property OnlClick;


//還有許多有用的方法,限於空間不一一列出。詳見TControl。


end;


 


implementation


{ TTile }


// Create the bitmap when creating the control.


constructor TTile.Create(Owner: TComponent);


begin


inherited;


fBitmap := TBitmap.Create;


fBitmap.OnChange := BitmapChanged;


end;


 


// Free the bitmap when destroying the control.


destructor TTile.Destroy;


begin


FreeAndNil(fBitmap);


inherited;


end;


 


// When the bitmap changes, redraw the control.


procedure TTile.BitmapChanged(Sender: TObject);


begin


Invalidate;


end;


 


// Paint the control by tiling the bitmap. If there is no


// bitmap, don't paint anything.


procedure TTile.Paint;


var X, Y: Integer;


begin


if (Bitmap.Width = 0) or (Bitmap.Height = 0) then


Exit;


Y := 0;


while Y < ClientHeight do


begin


X := 0;


while X < ClientWidth do


begin


Canvas.Draw(X, Y, Bitmap);


Inc(X, Bitmap.Width);


end;


Inc(Y, Bitmap.Height);


end;


end;


 


//透過複製TBitmap物件的方式設定新值


procedure TTile.SetBitmap(NewBitmap: TBitmap);


begin


fBitmap.Assign(NewBitmap);


end;


end.


 

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

PartII

PartIII

PartIV

PartV

PartVI

更多文章


 


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

相關文章