TPersistent類來源TObject類,在Delphi中的定義如下:
{$M+}
TPersistent = class(TObject)
private
procedure AssignError(Source: TPersistent);
protected
procedure AssignTo(Dest: TPersistent); virtual;
procedure DefineProperties(Filer: TFiler); virtual;
function GetOwner: TPersistent; dynamic;
public
destructor Destroy; override;
procedure Assign(Source: TPersistent); virtual;
function GetNamePath: string; dynamic;
end;
{$M-}
Vcl的基類TObject本身不支持RTTI(運行時類型信息),TPersistent類通過{$M+}編譯指令提供了RTTI的功能,打開了M開關後,Delphi在編譯該對象時,會把對象的類型信息也編譯進可執行文件,這樣在運行時就可以動態的獲得對象的屬性,方法等信息,所有的VCL可視化組件都是從TPersistent派生出來的,因此可以將組件信息保存成DFM文件,可以在運行時加載。
在TPersistent的聲明中,有兩個Public的方法(Destroy在此不討論),其中GetNamePath是Delphi的集成開發環境內部使用的,VCL不推薦直接對它的調用。而Assign方法則是爲完成對象複製而存在的,就是用來把一個源對象的屬性複製到目標對象中,並且被聲明爲虛方法,以允許每個派生類定義自己的複製對象的方法。Assign方法只是簡單的調用源對象的AssignTo方法來複制屬性,而TPersistent的AssignTo虛方法只是簡單的拋出一個異常。也就是說TPersistent方法並沒有實現任何有意義的功能,那麼對於派生自TPersistent類的對象要想提供克隆的功能都需要重載TPersistent的Assign或者AssignTo方法來實現自定義的複製功能,在Vcl中很多的類都實現了定製的Assign方法,比如最常見的TStrings類就重載了Assign方法提供了字符串列表的複製功能,在程序開發中經常會有需要將一個列表框的選項全部移動到另外一個列表中表示選擇了全部的內容,這個過程其實就是一個克隆的過程,使用Assign方法來實現就非常簡單,代碼、效果圖示意如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox2.Items.Assign(ListBox1.Items);
end;
如果沒有重寫Assign方法,則TPersistent的Assign方法會將複製動作交給源對象來 進行:
procedure TPersistent.Assign(Source: TPersistent);
begin
if Source <> nil then
Source.AssignTo(Self) // 調用源對象的AssignTo方法
Else
AssignError(nil);
end;
可以在TPersistent類的聲明的protected節中找到AssignTo方法的聲明,它也是一個虛方法。
如果將複製動作交給源對象來完成,那麼必須保證源對象的類已經重寫了AssignTo方法,否則將拋出一個“Assign Error”異常:
procedure TPersistent.AssignError(Source: TPersistent);
var
SourceName: string;
begin
if Source <> nil then
SourceName := Source.ClassName
else
SourceName := 'nil';
raise EConvertError.CreateResFmt(@SAssignError, [SourceName, ClassName]);
end;
AssignError是一個private方法,僅僅用於拋出賦值錯誤的異常。
在TPersistent的聲明中,GetOwner方法是被前面所述由Delphi內部使用的GetNamePath所調用。
最後還剩下一個虛方法DefineProperties(),它則是爲TPersistent的另一個使命而存在:對象持久。一個對象要持久存在,就必須將它流化(Streaming),保存到一個磁盤文件(.dfm文件)中。TPersistent也使得其派生類具有這種能力,但它作爲抽象類只是定義接口而並沒有給出實現。可以看到,DefineProperties是一個空的虛方法:
procedure TPersistent.DefineProperties(Filer: TFiler);
begin
end;
這留待其派生類來實現。對於對象持久的實現類,最典型的就是TComponent,每個組件都具有保存自己的能力。持久化的核心類:TFiler、TReader、TWriter,並使用繼承的方式來實現。TFiler抽象類,定義持久化基本的服務接口,同是TReader、TWriter的抽象父類。