delphi - 我可以使用默认的 WriteComponent 操作将 Delphi TPersistent 序列化为 TComponent 的字段吗?

标签 delphi serialization tpersistent tcomponent

我对如何从具有 TPersistent 字段的 TComponent 中写出属性感到非常困惑。例如我有:

  TChildObj = class( TPersistent )
  PRIVATE
    FVisible: boolean;
    FColor: TColor;
  PUBLIC
  PUBLISHED
    property Visible : boolean
               read FVisible
               write FVisible;
    property Color : TColor
               read FColor
               write FColor;
  end;


  TTest = class( TComponent )
    constructor Create( AOwner : TComponent ); override;
    destructor Destroy; override;
  private
    FChildObj : TChildObj;
    FOne: integer;
  published
    property One : integer
               read FOne
               write FOne;
    property ChildObj : TChildObj
               read FChildObj;
  end;

当我使用以下编写器代码时:
procedure TForm1.Button5Click(Sender: TObject);
var
  MS : TMemoryStream;
  SS : TStringStream;
  Test : TTest;
begin
  Test := TTest.Create( Self );
  MS := TMemoryStream.Create;
  SS := TStringStream.Create;
  try
    MS.WriteComponent( Test );
    MS.Position := 0;
    ObjectBinaryToText( MS, SS );
    SS.SaveToFile( 'c:\scratch\test.txt' );
  finally
    MS.Free;
    SS.Free;
  end;

end;

我只得到以下内容:
object TTest
  One = 0
end

即缺少 TPersistent TChildObj。

This article关于组件序列化状态“默认情况下,组件将流式传输任何非 TComponent 类型的 TPersistent 属性。我们的 TPersistent 属性像组件一样被流式传输,并且它可能具有其他将被流式传输的 TPersistent 属性。”但是,当我进入 System.Classes 时,在第 12950 行(XE3)附近进行了测试:
  if (PropInfo^.GetProc <> nil) and
     ((PropInfo^.SetProc <> nil) or
     ((PropInfo^.PropType^.Kind = tkClass) and
      (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
      (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then

这似乎表明只有组件和子组件被序列化。如果我让 TChildObj 从 TComponent 下降(并给它一个名字),我会得到它的名字出现在书面文件中(但仍然没有属性)。

我真正不明白的是,TControl(一个组件)具有 Font 属性(TPersistent),例如,当您编写 TLabel 时,它会很好地流出。

还是这与默认属性有关?

任何帮助表示赞赏。

最佳答案

当 RTL 决定是否需要流式传输 TPersistent 时,更仔细地查看需求列表。属性(property):

if (PropInfo^.GetProc <> nil) and
 ((PropInfo^.SetProc <> nil) or
 ((PropInfo^.PropType^.Kind = tkClass) and
  (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
  (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then

您的 ChildObj属性是只读属性,所以它不满足 PropInfo^.SetProc <> nil要求,它不是 TComponent -派生的子组件,所以它不满足是 is TComponentcsSubComponent要求。这就是 DFM 中缺少您的属性(property)的原因。

最简单的解决方案是让您的ChildObj属性是读/写而不是只读的(不要使用 TComponent 除非你必须这样做,在这种情况下你不需要这样做)。

您还缺少 TTest 中的析构函数释放TChildObj目的。为了更好的衡量,你应该给TChildObj一个 OnChange事件 TTest可以分配一个处理程序,因此它可以对 TChildObj 的更改使用react子属性。

试试这个:
type
  TChildObj = class(TPersistent)
  private
    FVisible : Boolean;
    FColor : TColor;
    FOnChange : TNotifyEvent;
    procedure Changed;
    procedure SetVisible(Value : Boolean);
    procedure SetColor(Value : TColor);
  public
    procedure Assign(Source : TPersistent); override;
    property OnChange : TNotifyEvent read FOnChange write FOnChange;
  published
    property Visible : Boolean read FVisible write SetVisible;
    property Color : TColor read FColor write SetColor;
  end;

  TTest = class(TComponent)
  private
    FChildObj : TChildObj;
    FOne : integer;
    procedure ChildObjChanged(Sender : TObject);
    procedure SetChildObj(Value : TChildObj);
  protected
    procedure Loaded; override;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  published
    property One : integer read FOne write FOne;
    property ChildObj : TChildObj read FChildObj write SetChildObj;
  end;

.
procedure TChildObj.Assign(Source: TPersistent);
begin
  if Source is TChildObj then
  begin
    FVisible := TChildObj(Source).Visible;
    FColor := TChildObj(Source).Color;
    Changed;
  end else
    inherited;
end;

procedure TChildObj.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TChildObj.SetVisible(Value : Boolean);
begin
  if FVisible <> Value then
  begin
    FVisible := Value;
    Changed;
  end;
end;

procedure TChildObj.SetColor(Value : TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    Changed;
  end;
end;

constructor TTest.Create(AOwner : TComponent);
begin
  inherited;
  FChildObj := TChildObj.Create;
  FChildObj.OnChange := ChildObjChanged;
end;

destructor TTest.Destroy;
begin
  FChildObj.Free;
  inherited;
end;

procedure TTest.ChildObjChanged(Sender : TObject);
begin
  if csLoading in ComponentState then Exit;
  // use ChildObj values as needed...
end;

procedure TTest.Loaded;
begin
  inherited;
  ChildObjChanged(nil);
end;

procedure TTest.SetChildObj(Value : TChildObj);
begin
  if FChildObj <> Value then
    FChildObj.Assign(Value);
end;

如果你去 TComponent方法,然后试试这个:
type
  TChildObj = class(TComponent)
  private
    FVisible : Boolean;
    FColor : TColor;
    FOnChange : TNotifyEvent;
    procedure Changed;
    procedure SetVisible(Value : Boolean);
    procedure SetColor(Value : TColor);
  public
    procedure Assign(Source : TPersistent); override;
    property OnChange : TNotifyEvent read FOnChange write FOnChange;
  published
    property Visible : Boolean read FVisible write SetVisible;
    property Color : TColor read FColor write SetColor;
  end;

  TTest = class(TComponent)
  private
    FChildObj : TChildObj;
    FOne : integer;
    procedure ChildObjChanged(Sender : TObject);
    procedure SetChildObj(Value : TChildObj);
  protected
    procedure Loaded; override;
  public
    constructor Create(AOwner : TComponent); override;
  published
    property One : integer read FOne write FOne;
    property ChildObj : TChildObj read FChildObj write SetChildObj;
  end;

.
procedure TChildObj.Assign(Source: TPersistent);
begin
  if Source is TChildObj then
  begin
    FVisible := TChildObj(Source).Visible;
    FColor := TChildObj(Source).Color;
    Changed;
  end else
    inherited;
end;

procedure TChildObj.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TChildObj.SetVisible(Value : Boolean);
begin
  if FVisible <> Value then
  begin
    FVisible := Value;
    Changed;
  end;
end;

procedure TChildObj.SetColor(Value : TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    Changed;
  end;
end;

constructor TTest.Create(AOwner : TComponent);
begin
  inherited;
  FChildObj := TChildObj.Create(Self);
  FChildObj.SetSubComponent(True);
  FChildObj.OnChange := ChildObjChanged;
end;

procedure TTest.ChildObjChanged(Sender : TObject);
begin
  if csLoading in ComponentState then Exit;
  // use ChildObj values as needed...
end;

procedure TTest.Loaded;
begin
  inherited;
  ChildObjChanged(nil);
end;

procedure TTest.SetChildObj(Value : TChildObj);
begin
  if FChildObj <> Value then
    FChildObj.Assign(Value);
end;

关于delphi - 我可以使用默认的 WriteComponent 操作将 Delphi TPersistent 序列化为 TComponent 的字段吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14763635/

相关文章:

c++ - 拆分大文件而不复制?

arrays - 引用 Delphi 变体数组而不进行复制

java - Jackson 反序列化有界类型

c# - DataContractJsonSerializer 抛出异常预期状态 'Element' .. 遇到 'Text' 名称 '',命名空间 ''

delphi - 编写 C++ Builder/Delphi 组件并将二进制属性保存到 DFM 文件

delphi - 如何在主窗体之前显示用于设置配置的窗体?

delphi - Rad Studio XE4和Delphi XE4

C# Xml Serializer 将列表反序列化为 0 而不是 null

delphi - 运行时设计 - 存储和加载表单布局(递归?)