淺拷貝與深度拷貝(原型模式)
Delphi的VCL從TPersistent開始支援RTTI(RuntimeTypeInfo)執行時型別資訊,它是通過{$M+}編譯指令提供了RTTI的功能.M開啟以後,Delphi在編譯該物件時,會把物件的型別資訊編譯進可執行檔案,這樣在執行時就可以動態地獲得物件的屬性和方法等資訊.因為所有的VCL視覺化元件都是從TPersistent派生的,因此可以將元件資訊儲存到dfm也可以動態載入.
Delphi還定義了一個虛方法Assign,
procedure Assign(Source:TPersistent);virtual;
這個方法就是用來把源物件屬性複製到目標物件中的.預設的TPersistent物件的Assign方法只是簡單的呼叫源物件的AssignTo方法來複制屬性,而TPersistent的AssignTo方法只是丟擲一個異常,沒有實現有意義的功能,那麼派生自TPersistent的物件要想提供克隆的功能都需要過載TPersistent的Assign或AssignTo來實現自定義的複製功能.
獲取類的屬性列表:要引用TypInfo//rtti,contnrs,classes,sysutils;//{$M+}要開啟.
procedure GetPropNames(AObject:TObject;var List:TStringList); var I,Count:Integer; PropList:PPropList;//陣列型指標,陣列又是指向屬性的紀錄型資訊的指標型陣列. PKinds:TTypeKinds;//型別資訊的集合 begin List.Clear; PKinds := [tkUnknow,tkInteger,tkChar,tkEnumeration,tkFloat,tkString,tkSet,tkClass,tkMethod,tkWChar,tkLString,tkWString, tkVariant,tkArray,tkRecord,tkInterface,tkInt64,tkDynArray]; Count := GetPropList(AObject.ClassInfo,pKinds,nil); GetMem(PropList,Count*SizeOf(Pointer)); GetPropList(AObject.ClassInfo,PKinds,PropList); for I :=0 to count-1 do List.Add(PropList^[i].Name); FreeMem(PropList,Count*SizeOf(Pointer)); end; procedure CloneObject(SrcObj,DesObj:TPersistent); var NameList:TStringList; I:Integer; V:Variant; begin if srcObj.ClassName<>DesObj.ClassName then raise Exception.Create('不同型別的物件,無法克隆'); if (not Assigned(SrcObje)) or not (Assigned(DesObj)) then raise Exception.Create('物件不能為空'); NameList := TStringlist.create; GetPropNames(SrcObj,NameList); try for I:= 0 to Namelist.Count-1 do begin V:= GetPropValue(SrcObj,Namelist.Strings[I]); SetPropValue(DesObj,NameList.Strings[I],V); finally Namelist.free; end; end;
其中GetPropName函式呼叫Delphi的TypeInfo單元的Rtti函式獲得要克隆物件的保護級別為Published的屬性名稱字串列表.而CloneObject則遍歷物件的屬性列表,使用RTTI函式GetPropValue通過屬性名獲得物件的屬性值,然後通過RTTI函式的SetPropValue將獲得源物件值賦值給目標物件.注意RTTI函式只對Published屬性有效,其它保護級別的屬性無效.
上面的物件複製函式對於複合的物件如下級物件的TreeView,TStrings是無效的,對於這類物件還必須手工完成.procedure TStrings.Assign(Source:TPersistent);
begin
if Source is TStrings then
begin
Beginupdate;
try
Clear;
FDefined:=TStrings(Source).FDefined;
FNameValueSeparator := TStrings(Source).FNameValueSeparator;
FQuoteChar := TStrings(Source).FQuoteChar;
FDelimiter := TStrings(Source).FDelimiter;
AddStrings(TStrings(Source));
finally
EndUpdate;
end;
Exit;
end;
inherited Assign(Source);
end;
delphi的Assign方法除了可以實現同樣型別物件的克隆之外,還可以實現不同物件之間的克隆,最典型的就是剪貼簿TClipBoard了,Windows的剪貼簿可以存放很不同型別的資料,如文字,點陣圖,圖元等,為了實現將剪貼簿中的點陣圖資料直接複製給對應的TBitmap或者TMetafile類,VCL過載了TClipboard類的AssignTo方法來實現將資料複製給不同的物件:
procedure TClipboard.AssignTo(Dest: TPersistent);
begin
if Dest is TPicture then
AssignToPicture(TPicture(Dest))
else if Dest is TBitmap then
AssignToBitmap(TBitmap(Dest))
else if Dest is TMetafile then
AssignToMetafile(TMetafile(Dest))
else inherited AssignTo(Dest);
end;
procedure TClipboard.AssignToBitmap(Dest: TBitmap);
var
Data: THandle;
Palette: HPALETTE;
begin
Open;
try
Data := GetClipboardData(CF_BITMAP);
Palette := GetClipboardData(CF_PALETTE);
Dest.LoadFromClipboardFormat(CF_BITMAP, Data, Palette);
finally
Close;
end;
end;
procedure TClipboard.AssignToMetafile(Dest: TMetafile);
var
//省略…
begin
//省略…
end;
procedure TClipboard.AssignToPicture(Dest: TPicture);
var
//…
Begin
//省略…
end;
Delphi的VCL從TPersistent開始支援RTTI(RuntimeTypeInfo)執行時型別資訊,它是通過{$M+}編譯指令提供了RTTI的功能.M開啟以後,Delphi在編譯該物件時,會把物件的型別資訊編譯進可執行檔案,這樣在執行時就可以動態地獲得物件的屬性和方法等資訊.因為所有的VCL視覺化元件都是從TPersistent派生的,因此可以將元件資訊儲存到dfm也可以動態載入.
Delphi還定義了一個虛方法Assign,procedure Assign(Source:TPersistent);virtual;
這個方法就是用來把源物件屬性複製到目標物件中的.預設的TPersistent物件的Assign方法只是簡單的呼叫源物件的AssignTo方法來複制屬性,而TPersistent的AssignTo方法只是丟擲一個異常,沒有實現有意義的功能,那麼派生自TPersistent的物件要想提供克隆的功能都需要過載TPersistent的Assign或AssignTo來實現自定義的複製功能.
獲取類的屬性列表:要引用TypInfo//rtti,contnrs,classes,sysutils;//{$M+}要開啟.
procedure GetPropNames(AObject:TObject;var List:TStringList);
var
I,Count:Integer;
PropList:PPropList;//陣列型指標,陣列又是指向屬性的紀錄型資訊的指標型陣列.
PKinds:TTypeKinds;//型別資訊的集合
begin
List.Clear;
PKinds := [tkUnknow,tkInteger,tkChar,tkEnumeration,tkFloat,tkString,tkSet,tkClass,tkMethod,tkWChar,tkLString,tkWString,
tkVariant,tkArray,tkRecord,tkInterface,tkInt64,tkDynArray];
Count := GetPropList(AObject.ClassInfo,pKinds,nil);
GetMem(PropList,Count*SizeOf(Pointer));
GetPropList(AObject.ClassInfo,PKinds,PropList);
for I :=0 to count-1 do
List.Add(PropList^[i].Name);
FreeMem(PropList,Count*SizeOf(Pointer));
end;
procedure CloneObject(SrcObj,DesObj:TPersistent);
var
NameList:TStringList;
I:Integer;
V:Variant;
begin
if srcObj.ClassName<>DesObj.ClassName then
raise Exception.Create('不同型別的物件,無法克隆');
if (not Assigned(SrcObje)) or not (Assigned(DesObj)) then
raise Exception.Create('物件不能為空');
NameList := TStringlist.create;
GetPropNames(SrcObj,NameList);
try
for I:= 0 to Namelist.Count-1 do
begin
V:= GetPropValue(SrcObj,Namelist.Strings[I]);
SetPropValue(DesObj,NameList.Strings[I],V);
finally
Namelist.free;
end;
end;
其中GetPropName函式呼叫Delphi的TypeInfo單元的Rtti函式獲得要克隆物件的保護級別為Published的屬性名稱字串列表.而CloneObject則遍歷物件的屬性列表,使用RTTI函式GetPropValue通過屬性名獲得物件的屬性值,然後通過RTTI函式的SetPropValue將獲得源物件值賦值給目標物件.注意RTTI函式只對Published屬性有效,其它保護級別的屬性無效.
上面的物件複製函式對於複合的物件如下級物件的TreeView,TStrings是無效的,對於這類物件還必須手工完成.
procedure TStrings.Assign(Source:TPersistent);
begin
if Source is TStrings then
begin
Beginupdate;
try
Clear;
FDefined:=TStrings(Source).FDefined;
FNameValueSeparator := TStrings(Source).FNameValueSeparator;
FQuoteChar := TStrings(Source).FQuoteChar;
FDelimiter := TStrings(Source).FDelimiter;
AddStrings(TStrings(Source));
finally
EndUpdate;
end;
Exit;
end;
inherited Assign(Source);
end;
delphi的Assign方法除了可以實現同樣型別物件的克隆之外,還可以實現不同物件之間的克隆,最典型的就是剪貼簿TClipBoard了,Windows的剪貼簿可以存放很不同型別的資料,如文字,點陣圖,圖元等,為了實現將剪貼簿中的點陣圖資料直接複製給對應的TBitmap或者TMetafile類,VCL過載了TClipboard類的AssignTo方法來實現將資料複製給不同的物件:
procedure TClipboard.AssignTo(Dest: TPersistent);
begin
if Dest is TPicture then
AssignToPicture(TPicture(Dest))
else if Dest is TBitmap then
AssignToBitmap(TBitmap(Dest))
else if Dest is TMetafile then
AssignToMetafile(TMetafile(Dest))
else inherited AssignTo(Dest);
end;
procedure TClipboard.AssignToBitmap(Dest: TBitmap);
var
Data: THandle;
Palette: HPALETTE;
begin
Open;
try
Data := GetClipboardData(CF_BITMAP);
Palette := GetClipboardData(CF_PALETTE);
Dest.LoadFromClipboardFormat(CF_BITMAP, Data, Palette);
finally
Close;
end;
end;
procedure TClipboard.AssignToMetafile(Dest: TMetafile);
var
//省略…
begin
//省略…
end;
procedure TClipboard.AssignToPicture(Dest: TPicture);
var
//…
Begin
//省略…
end;