前几天做试着做了一个很简单的控件(是按 d5 开发人员指南来做的)
这个控件用到了一个从 TPersistent 继承下来的类
type
TSomeObject = class(TPersistent)
private
FProp1:integer;
FProp2:string;
public
procedure Assign(source:TPersistent);override;
published
property Prop1:integer read FProp1 write FProp1;
property Prop2:string read FProp2 write FProp2;
end;
.
.
.
.
procedure TSomeObject.Assign(source:TPersistent);
begin
if source is TSomeObject then
begin
self.FProp1:=TSomeObject(source).FProp1;
self.FProp2:=TSomeObject(source).FProp2;
end;
inherited assign(source);
end;
按 d5中的例子打的,发现一用到 Assign 时总会提示 TSomeObject can not Assing To TSomeObject 这是
怎么回事呢?为什么会出现这个错误,看了看 TPersistent 别的派生类也是这样实现的呀,都 inherited
Assign(source); 我的怎么会错呢?后来试着加了一个 exit;
procedure TSomeObject.Assign(source:TPersistent);
begin
if source is TSomeObject then
begin
self.FProp1:=TSomeObject(source).FProp1;
self.FProp2:=TSomeObject(source).FProp2;
exit;
end;
inherited assign(source);
end;
错误没了,为什么 inherited assign(source) 会出错呢?于是查看源码终于找到答案
由于 TPersistent 中的 assign 是一个virtual 方法,没有具体的实现
<----- TPersistent 中的源码 ------>
procedure TPersistent.Assign(Source: TPersistent);
begin
if Source <> nil then Source.AssignTo(Self) else AssignError(nil);
end;
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;
procedure TPersistent.AssignTo(Dest: TPersistent);
begin
Dest.AssignError(Self);
end;
<----------------------------------->
从源码中可以看到
Assign -> AssignTo -> AssignError
不管 是 assign 还是 assignTo 最终要执行 AssignError 方法,所以这就要求继承自 TPersistent 的类要覆
盖 TPersistent 的 Assign 方法
<----- TCollection ----->
procedure TCollection.Assign(Source: TPersistent);
var
I: Integer;
begin
if Source is TCollection then
begin
BeginUpdate;
try
Clear;
for I := 0 to TCollection(Source).Count - 1 do
Add.Assign(TCollection(Source).Items[I]);
finally
EndUpdate;
end;
Exit;
// 这里用到了 exit
end;
inherited Assign(Source);
end;
<------------------------->
同样 TStrings 类也是
<----- TStrings ----->
procedure TStrings.Assign(Source: TPersistent);
begin
if Source is TStrings then
begin
BeginUpdate;
try
Clear;
FDefined := TStrings(Source).FDefined;
FQuoteChar := TStrings(Source).FQuoteChar;
FDelimiter := TStrings(Source).FDelimiter;
AddStrings(TStrings(Source));
finally
EndUpdate;
end;
Exit;
// 同样这里也是 exit
end;
inherited Assign(Source);
end;
<---------------------->
通过以上可以看出 TPersistent 的派生类在覆盖 Assign 时,满足条件的的处理过程完成后要退出处理过程
,不要去 inherited Assign(source) , 因为这样最终会执行到 AssignError