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.Se开发者_StackOverflowtFa(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) ?
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:
- either it shouldn't save/load the
Integer
property with zero value to the stream too, - or it should save/load both of them as there is no
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 calling TestClass.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.
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.
精彩评论