为什么不会调用已发布的 Int64 属性编写器方法 - 组件流
这是一个简单的测试,演示了我在使用 Delphi 2007 的项目中遇到的问题。我使用 TComponent 类来存储组件的各种状态。但 Int64 属性编写器方法永远不会被调用(仅设置目标字段)。因此,不可能依赖作者来更新 GUI、TList 或类似的东西...
例如:
TTestClass = Class(TComponent)
Private
Fb: Int64;
Fa: Integer;
Procedure SetFa(Const Value: Integer);
Procedure SetFb(Const Value: Int64);
Published
Property a: Integer Read Fa Write SetFa;
Property b: Int64 Read Fb Write SetFb;
Public
Procedure SaveInstance(Var Str: TStream);
Procedure LoadInstance(Var Str: TStream);
Procedure ReallyLoadInstance(Var Str: TStream);
Procedure Assign(Source: TPersistent); Override;
End;
TForm1 = Class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Procedure Button1Click(Sender: TObject); // test: 1st step, save the class
Procedure Button2Click(Sender: TObject); // test: 2nd step, try and fail to reload
Procedure Button3Click(Sender: TObject); // test: 3rd step, successfull reloading
Private
TestClass: TTestClass;
Str: TStream;
Public
Constructor Create(AOwner: TComponent); Override;
Destructor Destroy; Override;
End;
Var
Form1: TForm1;
Implementation
{$R *.dfm}
Procedure TTestClass.SetFa(Const Value: Integer);
Begin
Fa := Value;
ShowMessage('ok for "simple types"....');
End;
Procedure TTestClass.SetFb(Const Value: Int64);
Begin
Fb := Value;
ShowMessage('and for the others');
End;
Procedure TTestClass.SaveInstance(Var Str: TStream);
Begin
Str.Position := 0;
Str.WriteComponent( Self );
End;
Procedure TTestClass.Assign(Source: TPersistent);
Begin
If Not (Source Is TTestClass) Then Inherited
Else
Begin
b := TTestClass(Source).Fb;
End;
End;
Procedure TTestClass.LoadInstance(Var Str: TStream);
Begin
Str.Position := 0;
// this will work for fa and not fb.
Str.ReadComponent(Self);
End;
Procedure TTestClass.ReallyLoadInstance(Var Str: TStream);
Begin
Str.Position := 0;
Assign( Str.ReadComponent(Nil));
End;
Constructor TForm1.Create(AOwner: TComponent);
Begin
RegisterClasses([TTestClass]);
Inherited;
TestClass := TTestClass.Create(Self);
Str := TmemoryStream.Create;
End;
Destructor TForm1.Destroy;
Begin
Str.Free;
Inherited;
End;
Procedure TForm1.Button1Click(Sender: TObject);
Begin
Str.Size := 0;
TestClass.SaveInstance(Str);
End;
Procedure TForm1.Button2Click(Sender: TObject);
Begin
If Str.Size = 0 Then Exit;
TestClass.LoadInstance(Str);
// guess what...only first message
End;
Procedure TForm1.Button3Click(Sender: TObject);
Begin
If Str.Size = 0 Then Exit;
TestClass.ReallyLoadInstance(Str);
End;
在 TypInfo.pas 中,有一个“tkInt64”案例(似乎调用“SetProc”过程),应该”是否使用“Writer”设置published-Int64-props(就像通常对其他“常见”类型所做的那样)?
Here is a simple test demonstrating the issue I encounter in a project, using Delphi 2007. I use a TComponent class for storing various states of a component. But the Int64 property writer methods are never called (only the destination field is set). So it's not possible to rely on the writer to update a GUI a TList or such things...
For example:
TTestClass = Class(TComponent)
Private
Fb: Int64;
Fa: Integer;
Procedure SetFa(Const Value: Integer);
Procedure SetFb(Const Value: Int64);
Published
Property a: Integer Read Fa Write SetFa;
Property b: Int64 Read Fb Write SetFb;
Public
Procedure SaveInstance(Var Str: TStream);
Procedure LoadInstance(Var Str: TStream);
Procedure ReallyLoadInstance(Var Str: TStream);
Procedure Assign(Source: TPersistent); Override;
End;
TForm1 = Class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Procedure Button1Click(Sender: TObject); // test: 1st step, save the class
Procedure Button2Click(Sender: TObject); // test: 2nd step, try and fail to reload
Procedure Button3Click(Sender: TObject); // test: 3rd step, successfull reloading
Private
TestClass: TTestClass;
Str: TStream;
Public
Constructor Create(AOwner: TComponent); Override;
Destructor Destroy; Override;
End;
Var
Form1: TForm1;
Implementation
{$R *.dfm}
Procedure TTestClass.SetFa(Const Value: Integer);
Begin
Fa := Value;
ShowMessage('ok for "simple types"....');
End;
Procedure TTestClass.SetFb(Const Value: Int64);
Begin
Fb := Value;
ShowMessage('and for the others');
End;
Procedure TTestClass.SaveInstance(Var Str: TStream);
Begin
Str.Position := 0;
Str.WriteComponent( Self );
End;
Procedure TTestClass.Assign(Source: TPersistent);
Begin
If Not (Source Is TTestClass) Then Inherited
Else
Begin
b := TTestClass(Source).Fb;
End;
End;
Procedure TTestClass.LoadInstance(Var Str: TStream);
Begin
Str.Position := 0;
// this will work for fa and not fb.
Str.ReadComponent(Self);
End;
Procedure TTestClass.ReallyLoadInstance(Var Str: TStream);
Begin
Str.Position := 0;
Assign( Str.ReadComponent(Nil));
End;
Constructor TForm1.Create(AOwner: TComponent);
Begin
RegisterClasses([TTestClass]);
Inherited;
TestClass := TTestClass.Create(Self);
Str := TmemoryStream.Create;
End;
Destructor TForm1.Destroy;
Begin
Str.Free;
Inherited;
End;
Procedure TForm1.Button1Click(Sender: TObject);
Begin
Str.Size := 0;
TestClass.SaveInstance(Str);
End;
Procedure TForm1.Button2Click(Sender: TObject);
Begin
If Str.Size = 0 Then Exit;
TestClass.LoadInstance(Str);
// guess what...only first message
End;
Procedure TForm1.Button3Click(Sender: TObject);
Begin
If Str.Size = 0 Then Exit;
TestClass.ReallyLoadInstance(Str);
End;
As in TypInfo.pas there is a 'tkInt64' case (which seems to call a "SetProc" procedure), Shouldn't published-Int64-props be set using the "Writer" ( as done usually with other "common" types) ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是因为您从未给属性
b
赋值。因此它具有默认值(零)并且流系统不会将其保存到流中。而且由于它不在流中,因此在读回它时您不会看到调用的 setter...实际上,由于您也没有为属性
a
赋值,所以应该会发生同样的事情与它。看起来像流系统中的一个错误(或者至少是不一致):default
说明符,因此应假定nodefault
,因此值始终会被流式传输。因此,回顾一下:在调用
TestClass.SaveInstance(Str);
之前添加TestClass.b := 1;
并且您应该看到从流加载对象时调用的 setter ,但是当属性具有该类型的默认值时,您无法中继流系统来调用设置器。That's because you never assign a value to property
b
. Thus it has the default value (zero) and the streaming system won't save it to the stream. And since it isn't in the stream, you won't see the setter called when reading it back...Actually, since you don't assign value to property
a
either, same thing should happen with it. Looks like a bug (or at least inconsistency) in the streaming system:Integer
property with zero value to the stream too,default
specifier in the properties definition and thusnodefault
should be assumed and thus the value always to be streamed.So, to recap: add
TestClass.b := 1;
before callingTestClass.SaveInstance(Str);
and you should see the setter called when loading the object back from stream, but you can't relay on the streaming system to call the setter when property has the default value of the type.这似乎是 Int64 作为属性的错误。
作为解决方法,您可以使用其他数据类型,例如 Integer,或者,如果不够大,请使用 DefineProperties 和 TFiler.DefineProperty、TFiler .DefineBinaryProperty 等
This seems to be a bug with Int64 as a property.
As a workaround you could either use another data type, like Integer, or, if that is not big enough, use DefineProperties and TFiler.DefineProperty, TFiler.DefineBinaryProperty, etc.