开发者

Why a published Int64 property-writer method wouldn't be called - Component streaming

开发者 https://www.devze.com 2023-04-02 18:10 出处:网络
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

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 thus nodefault 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号