CnPack Forum » 技术板块灌水区 » CnPackTip#6 关于 assign 与 assignto 的 FAQ


2007-4-25 19:35 skyjacker
CnPackTip#6 关于 assign 与 assignto 的 FAQ

CnPackTip#6 关于 assign 与 assignto 的 FAQ

[url=http://www.cnpack.org]http://www.cnpack.org[/url]
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
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 或类似的地方。

2007-4-25 22:54 Passion
一总结显得更有深度了。放入文档中心。

页: [1]


Powered by Discuz! Archiver 5.0.0  © 2001-2006 Comsenz Inc.