私はwriteComponent()を使用してオブジェクトとサブオブジェクトを直列化しています。これは問題なく動作するようです。次の例では、私はTConfigDataの対象とTFooのサブオブジェクトをシリアル化:私は後ろにそれを読むしようとすると、readComponent()を使用してサブオブジェクトからフィールドを逆シリアル化する方法
object TConfigData
myInteger = 999
object TFoo
value = 777
end
end
しかし、readCompontentが唯一のルートオブジェクト内の値のmyIntegerを回復し、それが値を回復するために失敗しましたそれはゼロに設定するサブオブジェクトTFoo内にある。私は以下のコード全体を囲みます。 2つの主なクラスはTConfigDataとそのTFoo内です。私はインターネットの広範な検索を行ったが、なぜそれがTFoo.valueを読むことに失敗しているのか理解できない。
読書の仕組みを教えてください。 (XE6を使用)。私は単純な説明があると確信していますが、それは私の瞬間を避けています。
unit ufMain;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, V
Vcl.StdCtrls;
type
TFoo = class (TComponent)
private
fValue : integer;
published
property value : integer read fValue write fValue;
end;
TConfigData = class (TComponent)
private
fInteger : integer;
fFoo : TFoo; // <- Subobject
function ComponentToStringProc(Component: TComponent): string;
class function StringToComponentProc(Value: string): TComponent;
protected
procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
function GetChildOwner: TComponent; override;
published
property myInteger : Integer read fInteger write fInteger;
property foo : TFoo read fFoo write fFoo;
public
procedure save (fileName : string);
class function load (fileName : string) : TConfigData;
function getConfigStreamString : string;
constructor Create (AOwner : TComponent); override;
destructor Destroy; override;
end;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
Uses IOUTils;
procedure TConfigData.GetChildren(Proc: TGetChildProc; Root: TComponent);
var i : Integer;
begin
inherited GetChildren(Proc, Root);
for i := 0 to ComponentCount - 1 do
Proc(Components[i]);
end;
function TConfigData.GetChildOwner: TComponent;
begin
result := Self;
end;
constructor TConfigData.Create (AOwner : TComponent);
begin
inherited;
fFoo := TFoo.Create (self);
// foo.SetSubComponent (True); <- I don't want to use this because it flattens the dfm file.
end;
destructor TConfigData.Destroy;
begin
fFoo.Free;
inherited;
end;
function TConfigData.getConfigStreamString : string;
begin
result := ComponentToStringProc (self);
end;
procedure TConfigData.save (fileName : string);
var configStr : string;
begin
configStr := ComponentToStringProc (self);
TFile.WriteAllText (fileName, configStr);
end;
class function TConfigData.load (fileName : string) : TConfigData;
var configStr : string;
begin
configStr := TFile.ReadAllText (fileName);
result := StringToComponentProc (configStr) as TConfigData;
end;
function TConfigData.ComponentToStringProc(Component: TComponent): string;
var
BinStream:TMemoryStream;
StrStream: TStringStream;
s: string;
begin
BinStream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
BinStream.WriteComponent(Component);
BinStream.Seek(0, soFromBeginning);
ObjectBinaryToText(BinStream, StrStream);
StrStream.Seek(0, soFromBeginning);
Result:= StrStream.DataString;
finally
StrStream.Free;
end;
finally
BinStream.Free
end;
end;
class function TConfigData.StringToComponentProc(Value: string): TComponent;
var
StrStream:TStringStream;
BinStream: TMemoryStream;
begin
StrStream := TStringStream.Create(Value);
try
BinStream := TMemoryStream.Create;
try
ObjectTextToBinary(StrStream, BinStream);
BinStream.Seek(0, soFromBeginning);
Result:= BinStream.ReadComponent(nil);
finally
BinStream.Free;
end;
finally
StrStream.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var config : TConfigData;
configStr : string;
begin
config := TConfigData.Create (nil);
config.myInteger := 999;
config.foo.value := 777;
config.save('c:\\tmp\\config.dat');
Memo1.text := config.getConfigStreamString;
end;
procedure TForm1.Button2Click(Sender: TObject);
var config : TConfigData;
begin
config := TConfigData.load ('c:\\tmp\\config.dat');
Memo1.Clear;
Memo1.Lines.Add(inttostr (config.myInteger));
Memo1.Lines.Add(inttostr(config.foo.value));
end;
initialization
RegisterClasses([TConfigData, TFoo]);
end.
返信いただきありがとうございます。あなたが与えたものは働きます。私の特定のアプリケーションでは、私はサブサブジェクトを持たないでしょうが、オブジェクトの複数のレイヤーを持つ場合、どのように動作するのだろうかと思います。 – rhody
@rhodyそれはうまくいくでしょう。これを実装するには、抽象クラスTStreamingClassをTConfigDataのように実装されたGetChildrenとGetChildOwnerメソッドで導入します。そのため、TConfigInfoとTFooなどのコンポーネントはすべてTStreamingClassの子孫である可能性があります。そして、あなたは、ほとんど違いはありませんが、フォームを記述するdfmのように、階層構造を必要に応じて複雑にすることができます。ビジュアルコントロールでは、フォームはすべてのコンポーネントの所有者ですが、TConfigDataではownerはparentと同じです。 –
@rhodyそのため、Panel1にButton1がある場合は、Panel1を何も言わずにform1.button1またはbutton1と書くだけです。あなたのコードで、ConfigDataにサブオブジェクトBarがあるサブオブジェクトFooがある場合は、ConfigData.Foo.Barを記述する必要があります。私は、それは親愛子と所有者/そのコンポーネントとして2つの異なる階層の必要はないと思う、それはうまくいく、それは複雑すぎるだろう。 –