| CnPackTip#6 关于 Assign 与 Assignto 的 FAQ 
http://www.cnpack.org 
CnPack IV  QQ Group: 130970 
感谢: JingYu, 小冬, 小峰, 小辉, 没人认识我, 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 
begin end 
else 
  inherited; 
Q: 如果是两个不同对象呢? 比如 TBitMap 和 TString 如果想实现类似 lst.Assign(BitMap)呢? 
A: 
只要有这个需求,你也可以写个 TMyBitmap 来支持 Assign(TString) 和 AssignTo(TString) 
都是判断 Source 的类型 
lst.Assign(BitMap),只能是 lst.Assign(YourBitmap), 
除非你 hook 掉 bitmap.AssignTo 或类似的地方。  |