如何给对象“赋值”?(100分)

  • 主题发起人 主题发起人 liuge
  • 开始时间 开始时间
L

liuge

Unregistered / Unconfirmed
GUEST, unregistred user!
把一个对象赋值给另一个对象实际是 指针赋值
比如对象a、b, 举简单例子来说:
procedure TForm1.Button1Click(Sender: TObject);
var
a,b:TLabel
//对象a,b
begin
a:=TLabel.Create(Self);
b:=TLabel.Create(Self);
b.Caption :='BBB';
<font color=red>a:=b
//“赋值” ,其实是地址赋值</font>
canvas.TextOut(10,10,a.Caption);
<font color=red>b.Caption :='CCC'
//改变b, 实际也改变a</font>
canvas.TextOut(10,30,a.Caption);
end;

现在我希望知道有没有一种方法<font color=red>将b的所有属性全部Copy到a中</font>,
这样当b的属性发生变化后,a不变,我可以在把a的所有属性全部Copy回b。
 
你所需要的方法由TPersistent提供了一个“接口”--TPersistent.Assign。
Procedure Assign(Source:TPersistent);virtual;
这个过程用于把Soruce参数指定的对象的的所有属性赋值给自己,它的功能实际上是由
AssignTo完成的。而TPersistent的AssignTo方法并未实现上述功能,只是由其派生
类重载的AssignTo方法实现。
Delphi中的持久性类(具有属性)都是从TPersistent继承下来的,对于这些标准VCL
类可使用Assign方法得到你所需的功能。
对于自定义的类,若是从TPersistent的派生类继承下来的,且定义了新的域,可重
载AssignTo方法:先inherited,再为新的域赋值。若未定义新的域,直接使用Assign即可。
否则,就要自己编程实现这个功能(很简单的,一个域一个域地依次赋值)。
 
敏哥,你说的标准VCL类可用,但我如下使用
var
a,b:TLabel
//对象a,b
begin
a:=TLabel.Create(Self);
b:=TLabel.Create(Self);
b.Caption :='BBB';
<font color=red> a.Assign(b)
//“赋值” </font>
end;
执行到<font color=red> a.Assign(b)
//“赋值” </font>时报错:
Cannot assign a TLabel to TLabel
我试过TEdit等其它控件也同样出错,难道只能一个域一个域地依次赋值吗?
 
换个思路,不要老想者给对象赋值,可以考虑成对象的继承
似乎我记得根据面向对象的思想
对象的继承有几种,其中一种就是简单复制,复制的新对象的属性当然可以再改。
 
这真是一个有趣的问题,
你试一下
move(a, b, SizeOf(a));
 
To liuge:
我一直以为大多数VCL类都重载了这两个虚拟方法,刚才看了一些VCL源代码,发现恰恰相反:
多数VCL类都没有重载Assign或AssignTo方法,所以一用就错。部分重载了Assign或AssignTo方法
的类如下:
TBrush
TFont
TIcon
TStrings
TCollection
TCollectionItem
TDragObject
TBaseDragControlObject
TDragDockObject
TControlScrollBar
...
这些类一般都用作其它类的属性。
"一个域一个域地依次赋值"其实是很快的,当然,写起来有点麻烦,不过对于一个类你只需
这样做一次——重载Assign或AssignTo方法。

To iie:
按照你的思路,应该这样写:
Move(b,a,TLabel.InstanceSize);
这个方法比"一个域一个域地依次赋值"写起来简单,但并不比"一个域一个域地依次赋值"快;
最可怕的是这种方法不能有效地复制出指针类型的域,特别是对于长串、动态数组、Variant、
OleVariant、Interface和dispinterface等生存期自管理的数据类型,这种方法将导致严重错误。
不信就上机试一试。
 
a,b:TLabel
//对象a,b
将b的所有属性全部Copy到a中,
CopyMemory(@a,@b,sizeof(a))
 
敏哥:我想对于你后面说的几句我是赞同的,Move is not cure-all
不过,你说move只比依次赋值写法简单,而不比它快,我不敢苟同,
我想编译器虽然有优化,但是总不至于把多条赋值语句合并起来。
因为我知道move的编译结果直接就是86指令movb
 
谢谢各位,似乎用assign才是可行的。

以下几种方法都有问题:
move(a, b, SizeOf(a));
Move(b,a,TLabel.InstanceSize);
CopyMemory(@a,@b,sizeof(a))

据敏哥说TCollection的Assign是重载的。
我正是想保存某个DBrid1.Columns(TDBGridColumns类,TCollection的子类)。

我希望用如下方法保存DBrid1.Columns:
var
MyColumns:TDBGridColumns;
begin
MyColumns:=TDBGridColumns.Create(nil,<font color=red>XXXXX</font>);
MyColumns.Assign(TheDBGrid.Columns);
....
end;

可是我不知道TDBGridColumns.Create()的第二个参数如何用,请各位在本版面回答。
我会再开一版为各位再加100分。
 
MyColumns.Assign(TheDBGrid.Columns)
//即想把 TheDBGrid的Columns属性保存到MyColumns中
 
//用法如下:
var
MyColumns:TDBGridColumns;
begin
MyColumns:=TDBGridColumns.Create(DBGrid1.Columns.Grid,
TColumnClass(DBGrid1.Columns.ItemClass));
MyColumns.Assign(DBGrid1.Columns);
...
end;
 
关于TDBGridColumns敏哥的答案可以的。
但我的问题没有解决。虽然MyColumns的域、属性与DBGrid1.Columns相同,但奇怪的是
我再 DBGrid1.Columns.Assign(MyColumns) 时DBGrid1.Columns的属性不能恢复到以前
的值,但我看了MyColumns确实与DBGrid1.Columns先前的属性一致;更奇怪的是我加了
一句mycolumns.Items[0].Title.Caption :=mycolumns.Items[0].Title.Caption;毫无
用处的语句,DBGrid1.Columns.Items[0].Title.Caption可以恢复,其特属性依旧不能
恢复。可能是delphi4的bug吧。

我另开了一个帖子,请敏哥去签到拿分。
 
怎么还不结束。
 
接受答案了.
 

Similar threads

D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
后退
顶部