How to use DefineProperties in a custom Class Object for Arrays - Delphi

做~自己de王妃 提交于 2019-12-02 07:39:57

HOW do you actually try to read and write it ? I think you're trying to make complex incompatible things when there instead of using standard methods.

Why not to use standard VCL streaming procedures?

procedure TMyDataSet.SaveToStream(const Stream: TStream);
begin
   Stream.WriteComponent(self);
end;

procedure TMyDataSet.LoadFromStream(const Stream: TStream);
begin
   Stream.ReadComponent(self);
end;

However if instead of using TFiler and standard VCL streamer you make your custom code using RTTI (GetPropList) - then it would not call those virtual properties APi custom to TFiler and would only show real properties.

So my advice is just to use standard emthods like shown above and to streamline and harden the code.

And since RegisterClass works by the classname you'd better choose another name, not clashing with a real TDataSet from stock DB unit.

Fix the name and do register the class, so VCL streamer could find it by name! For example:

procedure TMyDataSet.ReadArray(Reader: TReader);
var
  N: Integer; S: String;
begin
  N := Low(FArrayToSave);
  Reader.ReadListBegin;
  while not Reader.EndOfList do begin
    S := Reader.ReadString; // even if we would not save it - we should remove it from the input
    if N <= High(FArrayToSave) then
       FArrayToSave[N] := S;
    Inc(N);
  end;
  Reader.ReadListEnd;
end;

procedure TMyDataSet.WriteArray(Writer: TWriter);
var
  I: Integer;
begin
  Writer.WriteListBegin;
  for I := Low(FArrayToSave) to High(FArrayToSave) do begin
    Writer.WriteString(FArrayToSave[I]);
  end;
  Writer.WriteListEnd;
end;

initialization
  DataSet := TMyDataSet.Create(Nil);
  RegisterClasses([TMyDataSet]);

finalization
  DataSet.Free;
end.

Additionally, i think you'd better - for future extensibility - save the array length in DFM.

procedure TMyDataSet.WriteArray(Writer: TWriter);
var
  I: Integer;
begin
  Writer.WriteInteger(Length(FArrayToSave));
  Writer.WriteListBegin;
  for I := Low(FArrayToSave) to High(FArrayToSave) do begin

....

procedure TMyDataSet.ReadArray(Reader: TReader);
var
  N: Integer;  S: String;
begin
  for N := Low(FArrayToSave) to High(FArrayToSave) do begin
      FArrayToSave := ''; // in case DFM would have less elements than 50
  N := Reader.ReadInteger;
  if N <> Length(FArrayToSave) then... recovery from unexpected DFM version error

  N := Low(FArrayToSave);
  Reader.ReadListBegin;
  while not Reader.EndOfList do begin

PS. you do not need {$M+} there since TComponent already is derived from TPersistent

PPS. Wanted to comment upon update in the question, but the phone refuses to do (too long?) so putting it here.

1: since we moved away from using RTTI, the Typinfo unit no more needed in uses. 2: if N = Length(FFullArray) then lacks ELSE path. Okay, now we learned that DFM is broken or incompatible, what then? I think we better raise some error. Or try to remove list of N strings, so next property could be read. Or even remove the list of elements of any type/quantity until list end. Future compatibly is never warranted, but at least some attempt can be done, even just to explicitly halt with error. Skipping reading and silently leaving the reader inside middle of property, so next properties would get crazy, I think is not the way to do it.

And generally David is correct about ignoring incorrect indices in the setter and getter. Unless you would intentionally come with some unusual pattern of implicit item creation from default template in sparse array by setting or getting with "free" "unbound" index (which is no code for either) the better approach at least in Delphi would be "fail early". That is what users of your class would expect by default. So kinda

  Procedure class.CheckArrayIdx(const i: integer);
  Var mx, mn : integer;
  Begin 
       Mn := low(myarray) ; Mx := high(myarray);
       If (i <= mx) and (I >= mn) then exit;
       Raise ERangeError.CreateFmt('%s.Items index should be %d <= %d <= %d',  [
             Self.ClassName, mn, I, mx]) ;
   End;

This procedure can be called as 1st line in both setter and getter. Then you can just work with surely correct index value.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!