CnPackTip#6 关于 assign 与 assignto 的 FAQ
http://www.cnpack.org
CnPack IV QQ Group: 130970
感谢: JingYu, Passion, 小冬, 小峰, 小辉, 没人认识我, Bahamut ...
整理: SkyJacker
2007.04.25
FAQ: assign 与 assignto
Q:
TPersistent 的 assign 与 assignto 有什么区别?
如下 vcl 源码:
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;
A:
Assign是给子类重载使用的。
子类都可以重载以实现子类不同实例间的互相赋值。
单纯的TPersistent实例是不支持Assign的,一Assign就会按代码中所说那样弹出AssignError。
但子类是可以重载的。TStringList等,都重载实现了这些方法。
Q: AssignError 为什么每次都要 raise ?
A: TPersistent本类确实会 raise,子类一般override了,就没了。
Q: assign 与 assignto 为什么要这样设计呢?
A:
AssignTo 是非常有作用的,设计 Assign 和 Assignto 两个函数,是很有必要的。
Assign 是控制从其它对象给自身赋值
Assignto 是控制自身给其它对象赋值
这两个函数的区别就是主动与被动的关系
举个例子
Bitmap.Assign(JpegImage);
如果只提供 Assign 的话,Bitmap 不可能再去判断 Source 是否一个 JpegImage
而如果有 AssignTo 的话,Jpeg 就可以在这里给 Bitmap 传递数据
这样当 Bitmap.Assign(Jepg) 时,Bitmap 发现 Source 是个不支持的 Jpeg 对象,
就会调用 Jpeg.AssignTo(Self)。
这样就可以实现 对象.Assign(将来扩展的对象) 这个功能,
同样,如果我们要增加一个 TCnBitmap 这个扩展类,只要实现 AssignTo(Tbitmap),
就可以让 Bitmap.Assign(CnBitmap) 的功能了。
代码本身很简单,关键是要理解代码背后的思想。
Q: 能否解释一下源码
A:
procedure TPersistent.Assign(Source: TPersistent);
begin
if Source <> nil then Source.AssignTo(Self) else AssignError(nil);
end;
这个缺省实现的意思是,如果将一个未知的对象赋值给自己,就调用未知对象的“给对方赋值”函数
Q:为什么 bitmap和jpeg可以assign
A:
在 assign 里面可以做数据格式转换,
Assign 就是提供给对象之间赋值数据的机制。
在 TPersistent 里,AssignTo 是 protected 方法,用户不需要调用的
对用户来说,assign 是用来在相同或不同类型的对象之间传递数据的
对组件开发者来说,assign 可以用来支持这种传递
TPersistent 是 vcl 体系的基石。意思是,可持久化的对象
这种持久化是通过将对象流化和反流化来实现的
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+} 这个开关是用来为类生成 rtti 信息
想系统地理解,还是找本书来看吧
介绍 vcl 体系的书挺多的
对 assign 和 assignto 来说,两个函数都有用
如果你想让你写类能够 Assign 其它对象,需要 override Assign,
如果想让其它对象 Assign 你自己,需要 override AssignTo
所以一般子类重载的 Assign 实现体通常是:
if Source is TXXX then
begin
end
else if Source is XXX then
beginend
else
inherited;
Q: 如果是两个不同对象呢? 比如 TBitMap 和 TString 如果想实现类似 lst.assign(BitMap)呢?
A:
只要有这个需求,你也可以写个 TMyBitmap 来支持 Assign(TString) 和 AssignTo(TString)
都是判断 Source 的类型
lst.assign(BitMap),只能是 lst.Assign(YourBitmap),
除非你 hook 掉 bitmap.AssignTo 或类似的地方。