後一頁
前一頁
回目錄
回首頁
第二十章 開發Delphi物件式數據管理功能(三)

20.2.1.1 TFiler物件的屬性和方法 

  1. Root屬性

  聲明:property Root: TComponent;

Root 屬性給Filer物件指出被讀寫的物件中哪一個物件是根或主要擁有者。RootComponentWriteRootComponent方法在讀和寫部件及其擁有的部件前先設定Root的值。

  2. Ancestor屬性

  聲明:property Ancestor: TPersistent;

Ancestor屬性用於往繼承下來的窗體中寫部件,因為當寫部件時,Write物件只需要寫入與所繼承的部件不同的屬性,所以在寫之前要跟蹤每個繼承的部件,並且比較它們的屬性。

  如果Ancestornil,就表示沒有相應的繼承的部件,Writer物件應當將部件完全寫入流。Ancestor一般為nil,只有當呼叫WriteDescendantWriteDescendantRes時,才給賦值。當編寫和覆蓋DefineProperties時,必須設定Ancestor的值。

  3. IgnoreChildren屬性

  聲明:property Ignorechildren: Boolean;

IgnoreChildren屬性使一個Writer物件存儲部件時可以不存儲該部件擁有的部件。如果IgnoreChildren屬性為True,則Writer物件存儲部件不存它擁有的子部件。否則,Writer物件將所有其擁有的物件寫入流。

  4. Create方法

  聲明:constructor Create(Stream: TStream; BufSize: Cardinal);

 Create方法建立一個新的Filer物件,建立它和流Stream的聯繫;並且給它分配一個緩衝區BufferBuffer的大小由BufSize指定。

  5. Defineproperty方法

  聲明:procedure Defineproperty(const Name: String; ReadData: TReaderProc;

WriteData: TWriterProc; HasData: Boolean); virtual; abstract;

Defineproperty方法定義Filer物件將作為屬性存儲的數據。Name參數描述接受的屬性名,該屬性不在published部分定義。ReadDataWriteData參數指定在存取物件時讀和寫所需數據的方法。HasData參數在執行時決定了屬性是否有數據要存儲。

  只有當物件有數據要存儲時,才在該物件的DefineProperties中呼叫DefinePropertyDefineProperties有一個Filer物件作為它的參數,呼叫的就是該Filer物件的DefinePropertyDefineBinaryProperty方法。當定義屬性時,Writer物件應當引用Ancestor屬性,如果該屬性非空,Writer物件應當只寫入與從Ancestor繼承的不同的屬性的值。

  一個最簡單的例子是TComponentDefineProperties方法。儘管TComponent 沒有在published中定義LeftTop屬性,但該方法存儲了部件的位置資訊。

 

procedure TComponent.DefineProperties(Filer: TFiler);

begin

Filer.DefineProperty('Left', ReadLeft, WriteLeft, LongRec(FDesignInfo).Lo <> 0);

Filer.DefineProperty('Top', ReadTop, WriteTop, LongRec(FDesignInfo).Hi <> 0);

end;

 

6. DefineBinaryproperty方法

  聲明:procedure DefineBinaryproperty(const Name: String;

ReadData, WriteData: TStreamProc;

HisData: Boolean); virtual; abstract;

DefineBinaryProperty方法定義Filer物件作為屬性存儲的二進制數據。Name參數描述屬性名。ReadDataWriteData參數描述所存儲的物件中讀寫所需數據的方法。HasData參數在執行時決定屬性是否有數據要存。

  DefineBinaryPropertyDefineProperty方法的不同之處在於,二進制型的屬性直接用Stream物件讀寫,而不是通過Filer物件。通過ReadDataWriteData傳入的方法,直接將物件數據寫入流或從流讀出。

  DefineBinaryProperty屬性用得較少。只有標準的VCL物件定義了象圖形、圖像之類的二進制屬性的部件中才用它。

  7. FlushBuffer方法

  聲明:procedure FlushBuffer; virtual: abstract;

FlushBuffer方法用於使Filer物件的緩衝區與相聯的Stream物件同步。對Reader物件來說,是通過重新分配緩衝區;對於Writer物件是通過寫入目前緩衝區。

  FlushBuffer是一個抽象方法,TReaderTWriter都覆蓋了它,提供了具體實現。

 

20.2.1.2 TFiler物件的實現原理

 

  TFiler物件是Filer物件的基礎類,它定義的大多數方法都是抽象型式的,沒有具體實現它,這些方法要在TReaderTWrite中覆蓋。但它們提供了Filer物件的框架,了解它無疑是很重要的。

  1. TFiler物件屬性的實現

  TFiler物件定義了三個屬性:RootAncestorIgnoreChildren。正如定義物件屬性通常所採用的方法那樣,要在private部分定義存儲屬性值的數據欄位,然後在publicPublished部分定義該屬性,並按需要增加讀寫控制。它們的定義如下:

  

TFiler = class(TObject)

private

FRoot: TComponent;

FAncestor: TPersistent;

FIgnoreChildren: Boolean;

public

property Root: TComponent read FRoot write FRoot;

property Ancestor: TPersistent read FAncestor write FAncestor;

property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren;

end;

 

  它們在讀寫控制上都是直接讀寫私有的數據欄位。

  在介紹TReaderTWriter的實現,我們還會看到這幾個屬性的原理介紹。

  2. TFiler物件方法的實現

  在TFiler物件定義的眾多方法中很多都是抽象類方法,沒有具體實現。在TFiler 的後繼物件TReader中覆蓋了這些方法。在後面章節,會介紹這些方法的實現。

  在TFiler物件中有具體實現的有兩個方法CreateDestroy

  ぇ Create方法的實現

  Create方法是TFiler的構造方法,它有兩個參數StreamBufSizeStream是指定與TFiler物件相聯繫的Stream物件,Filer物件都是用Stream物件完成具體的讀寫。BufSizeTFiler物件內部開設的緩衝區的大小。Filer物件內部開設緩衝區是為了加快數據的讀寫,它的實現如下:

 

constructor TFiler.Create(Stream: TStream; BufSize: Integer);

begin

FStream := Stream;

GetMem(FBuffer, BufSize);

FBufSize := BufSize;

end;

 

  FStreamFBufferFBufSize都是TFilerprivate部分定義的數據欄位。FStream表示與Filer物件相聯的Stream物件,FBuffer指向Filer物件內部開設的緩衝區,FBufSize是內部緩衝區的大小。Create方法用Stream參數值給FStream賦值,然後用GetMem分配BufSize大小的動態記憶體作為內部緩衝區。

  え Destroy方法的實現

  Destroy方法是TFiler物件的析構函數,它的作用就是釋放動態記憶體。

 

destructor TFiler.Destroy;

begin

if FBuffer <> nil then FreeMem(FBuffer, FBufSize);

end;

 

20.2.2 TWriter物件

 

  TWriter 物件是可實例化的,往流中寫數據的Filer物件。TWriter物件直接從TFiler繼承而來,除了覆蓋從TFiler繼承的方法外,還增加了大量的關於寫各種數據型式(IntegerStringComponent)的方法。TWriter物件和TReader 物件配合使用將使物件讀寫發揮巨大作用。

 

20.2.2.1 TWriter物件的屬性和方法

 

  1. Position屬性

  聲明:property Position: Longint;

TWriter物件的Position屬性表示相關聯的流中的目前要寫的位置,TReader 物件也有這個屬性,但與TReader物件不同的是TWriter物件的Position的值比流的Position值小,這一點一看屬性實現就清楚了。

  2. RootAncesstor屬性

  聲明:property RootAncestor: TComponent;

RootAncestor屬性表示的是Root屬性所指的部件的祖先。如果Root 是繼承的窗體,Writer物件將窗體擁有部件與祖先窗體中的相應部件依次比較,然後只寫入那些與祖先中的不同的部件。

  3. Write方法

  聲明:procedure Write(const Buf; Count: Longint);

Write方法從Buf中往與Writer相關聯的流中寫入Count個位元群組。

  4. WriteListBegin方法

  聲明:procedure WriteListBegin;

WriteListBegin方法往Write物件的流中寫入項目清單開始標誌,該標誌意味著後面存儲有一連串的項目。Reader物件,在讀這一連串項目時先呼叫ReadListBegin方法讀取該標誌位,然後用EndOfList判斷是否清單結束,並用回圈語句讀取項目。在呼叫WriteListBegin方法的後面必須呼叫WriteListEnd方法寫清單結束標誌,相應的在Reader物件中有ReadListEnd方法讀取該結束標誌。

  5. WriteListEnd方法

  聲明:procedure WriteListEnd;

WriteListEnd方法在流中,寫入項目清單結束標誌,它是與WriteListBegin相匹配的方法。

  6. WriteBoolean方法

  聲明:procedure WriteBoolean(Value: Boolean);

WriteBoolean方法將Value傳入的布爾值寫入流中。

  7. WriteChar方法

  聲明:procedure WriteChar(Value: char);

WriteChar方法將Value中的字元寫入流中。

  8. WriteFloat方法

  聲明:procedure WriteFloat(Value: Extended);

WriteFloat方法將Value傳入的浮點數寫入流中。

  9. WriteInteger方法

  聲明:procedure WriteInteger(Value: Longint);

WriteInteger方法將Value中的整數寫入流中。

  10. WriteString方法

  聲明:procedure WriteString(const Value: string);

WriteString方法將Value中的字元串寫入流中。

  11. WriteIdent方法

  聲明:procedure WriteIdent(const Ident: string);

WriteIdent方法將Ident傳入的標識符寫入流中。

  12. WriteSignature方法

  聲明:procedure WriteSignature;

WriteSignature方法將Delphi Filer物件標籤寫入流中。WriteRootComponent方法在將部件寫入流之前先呼叫WriteSignature方法寫入Filer標籤。Reader物件在讀部件之前呼叫ReadSignature方法讀取該標籤以指導讀操作。

  13. WritComponent方法

  聲明:procedure WriteComponent(Component: TComponent);

WriteComponent方法呼叫參數ComponentWriteState方法將部件寫入流中。在呼叫WriteState之前,WriteComponent還將ComponentComponetnState屬性置為csWriting。當WriteState返回時再清除csWriting.

14. WriteRootComponent方法

  聲明:procedure WriteRootComponent(Root: TComponent);

WriteRootComponent方法將Writer物件Root屬性設為參數Root帶的值,然後呼叫WriteSignature方法往流中寫入Filer物件標籤,最後呼叫WriteComponent方法在流中存儲Root部件。

 

20.2.2.2 TWriter物件的實現

 

  TWriter物件提供了許多往流中寫各種型式數據的方法,這對於程式員來說是很重要的功能。TWrite物件往流中寫數據是依據不同的數據採取不同的格式的。 因此要掌握TWriter物件的實現和應用方法,必須了解Writer物件存儲數據的格式。

  首先要敘述的是,每個Filer物件的流中都包含有Filer物件標籤。該標籤佔四個位元群組其值為“TPF0”。Filer物件為WriteSignatureReadSignature方法存取該標籤。該標籤主要用於Reader物件讀數據(部件等)時,指導讀操作。

  其次,Writer物件在存儲數據前都要留一個位元群組的標誌位,以指出後面存放的是什麼型式的數據。該位元群組為TValueType型式的值。TValueType是枚舉型式,佔一個位元群組空間,其定義如下:

 

  TValueType = (VaNull, VaList, VaInt8, VaInt16, VaInt32, VaEntended, VaString, VaIdent,

VaFalse, VaTrue, VaBinary, VaSet, VaLString, VaNil, VaCollection);

 

因此,對Writer物件的每一個寫數據方法,在實現上,都要先寫標誌位再寫相應的數據;Reader物件的每一個讀數據方法都要先讀標誌位進行判斷,如果符合就讀數據,否則產生一個讀數據無效的異常事件。VaList標誌有著特殊的用途,它是用來標識後面將有一連串型式相同的項目,而標識連續項目結束的標誌是VaNull。因此,在Writer物件寫連續若干個相同項目時,先用WriteListBegin寫入VaList標誌,寫完數據項目後,再寫出VaNull標誌;而讀這些數據時,以ReadListBegin開始,ReadListEnd結束,中間用EndofList函數判斷是否有VaNull標誌。

  下面就介紹它們的實現。

  1. TWriter物件屬性的實現

  TWriter物件直接從TFiler物件繼承,它只增加了PositionRootAncestor屬性。

RootAncestor屬性在private部分有數據欄位FRootAncestor存入其值。在屬性定義的讀與控制上都是直接讀取該值。

  Position屬性的定義中包含了兩個讀寫控制方法:GetPositionSetPosition

 

TWriter = class(TFiler)

private

FRootAncestor: TComponent;

function GetPosition: Longint;

procedure SetPosition(Value: Longint);

public

property Position: Longint read GetPosition write SetPosition;

property RootAncestor: TComponent read FRootAncestor write FRootAncestor;

end;

 

GetPositionSetPosition方法實現如下:

 

function TWriter.GetPosition: Longint;

begin

Result := FStream.Position + FBufPos;

end;

 

procedure TWriter.SetPosition(Value: Longint);

var

StreamPosition: Longint;

begin

StreamPosition := FStream.Position;

{ 只清除越界的緩衝區 }

if (Value < StreamPosition) or (Value > StreamPosition + FBufPos) then

begin

WriteBuffer;

FStream.Position := Value;

end

else FBufPos := Value - StreamPosition;

end;

 

  WriteBufferTWriter物件定義的私有方法,它的作用是將Writer 物件內部緩衝區中的有效數據寫入流中,並將FBufPos置為0Writer物件的FlushBuffer物件就是用WriteBuffer方法刷新緩衝區。

  在SetPosition方法中,如果Value值超出了邊界(FStream.PositionFStream.Position + FBufPos),就將緩衝區中的內容寫入流,重新設定緩衝區在流中的相對位置;否則,就只是移動FBufPos指標。

  2. TWriter方法的實現

  ぇ WriteListBeginWriteListEnd的實現

  這兩個方法都是用於寫連續若干個相同型式的值。WriteListBegin寫入VaList標誌,WriteListEnd寫入VaNull標誌。

 

procedure TWriter.WriteListBegin;

begin

WriteValue(vaList);

end;

 

procedure TWriter.WriteListEnd;

begin

WriteValue(vaNull);

end;

 

  這兩個方法都呼叫TWriter物件的WriteValue方法,該方法主要用於寫入TValueType型式的值。

 

procedure TWriter.WriteValue(Value: TValueType);

begin

Write(Value, SizeOf(Value));

end;

 

  え 簡單數據型式的寫入

  簡單數據型式指的是整型、字元型、字元串型、浮點型、布爾型等。TWriter物件都定義了相應的寫入方法。

  WriteInteger方法用於寫入整型數據。

 

procedure TWriter.WriteInteger(Value: Longint);

begin

if (Value >= -128) and (Value <= 127) then

begin

WriteValue(vaInt8);

Write(Value, SizeOf(Shortint));

end else

if (Value >= -32768) and (Value <= 32767) then

begin

WriteValue(vaInt16);

Write(Value, SizeOf(Smallint));

end else

begin

WriteValue(vaInt32);

Write(Value, SizeOf(Longint));

end;

end;

 

  WriteInteger方法將整型數據分為8位、16位和32位三種,並分別用vaInt8vaInt16VaInt32

  WriteBoolean用於寫入布爾型數據:

 

procedure TWriter.WriteBoolean(Value: Boolean);

begin

if Value then

WriteValue(vaTrue) else

WriteValue(vaFalse);

end;

 

  與其它數據型式不同的是布爾型數據只使用了標誌位是存儲布爾值,在標誌位後沒有數據。

  WriteFloat方法用於寫入浮點型數據。

 

procedure TWriter.WriteFloat(Value: Extended);

begin

WriteValue(vaExtended);

Write(Value, SizeOf(Extended));

end;

 

  字元串“True”、“False”和“nil”作為標識符傳入是由於Delphi的特殊需要。如果是“True”、“False”和“nil”則寫入VaTrueVaFalseVaNil,否則寫入VaIdent標誌,接著以字元串形式寫入標識符。

  WriteString方法用於寫入字元串

  

procedure TWriter.WriteString(const Value: string);

var

L: Integer;

begin

L := Length(Value);

if L <= 255 then

begin

WriteValue(vaString);

Write(L, SizeOf(Byte));

end else

begin

WriteValue(vaLString);

Write(L, SizeOf(Integer));

end;

Write(Pointer(Value)^, L);

end;

 

  Delphi的字元串型式有兩種。一種長度小於256個位元群組,另一種長度小於65536 個位元群組。WriteString方法區分這兩類情況存儲字元串,一種設定VaStirng標誌,另一種設定VaLString。然後存儲字元串的長度值,最後存儲字元串數據。

  WriteChar方法用於寫入字元。

  

procedure TWriter.WriteChar(Value: Char);

begin

WriteString(Value);

end;

 

  字元型式的讀寫是用讀寫字元串的方法,在讀的時候,判斷位元群組數為1時,則為字元型。

  ぉ 部件的寫入

  TWriter物件中與寫入部件有關的方法有WriteSignatureWritePrefixWriteComponentWriteDescendantWriteRootComponent

 WriteSignature方法用於往流中寫入Filer物件標籤。

 

procedure TWriter.WriteSignature;

begin

Write(FilerSignature, SizeOf(FilerSignature));

end;

 

  FilerStgnature是字元串常數,其值為“TPF0”,代表物件標籤。

  WritePrefix方法用於在寫入部件前寫入ffInheritedffChildPos標誌,這些標誌表示部件的繼承特征和建立序值特征。

 

procedure TWriter.WritePrefix(Flags: TFilerFlags; AChildPos: Integer);

var

Prefix: Byte;

begin

if Flags <> [] then

begin

Prefix := $F0 or Byte(Flags);

Write(Prefix, SizeOf(Prefix));

if ffChildPos in Flags then WriteInteger(AChildPos);

end;

end;

 

  如果ffChildPos置位,則存入部件在Owner中的建立序值。更詳細的資訊請參閱TReaderReadPrefix方法。

  WriteComponent方法往流中寫入部件。

 

procedure TWriter.WriteComponent(Component: TComponent);

 

function FindAncestor(const Name: string): TComponent;

begin

end;

 

begin

Include(Component.FComponentState, csWriting);

if Assigned(FAncestorList) then

Ancestor := FindAncestor(Component.Name);

Component.WriteState(Self);

Exclude(Component.FComponentState, csWriting);

end;

 

  方法中用ComponentWritState方法寫入部件的屬性。在寫入之前將Component.FComponentState置為csWriting寫入完後再將csWriting復位。

  WriteDescendant是根據祖先AAncestor的情況寫入部件Root

 

procedure TWriter.WriteDescendent(Root: TComponent; AAncestor: TComponent);

begin

FRootAncestor := AAncestor;

FAncestor := AAncestor;

FRoot := Root;

WriteSignature;

WriteComponent(Root);

end;

 

方法先呼叫WriteSignature方法寫入Filer物件標籤。然後呼叫WriteComponent將部件Root寫入流。

  WriteRootComponent方法則是呼叫WriteDescendant方法寫入部件,只是將後者的Ancestor參數以nil值傳入。 

procedure TWriter.WriteRootComponent(Root: TComponent);

begin

WriteDescendent(Root, nil);

end;


後一頁
前一頁
回目錄
回首頁