Sign in Join
Wiki copytemporary

Streaming components/ja: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## はじめに
22
33通常、ハードディスクやネットワークストリームにデータを蓄えておく場合は、それらの特性に応じてロードやセーブ(読み込みや書き込み)のためのコードを書かなければなりません。このチュートリアルでは、余分な load/save コードを書くことなく、RTTI を使用して、ストリームから読み込みや書き込みを行うクラスの書き方について述べます。
44
55lazarus コードでのサンプルや TCheckBox を含んだ TGroupBox をストリームに保存したり、両方のコンポーネントのコピーを作成してストリームから戻すといったデモを説明します。
66
7-` <lazaruspath>/examples/componentstreaming/ を参照してください。`
7+```text
8+ <lazaruspath>/examples/componentstreaming/ を参照してください。
9+```
810
911RTTI コントロールとのコンビネーションで、GUI と ディスク/ネットワークとの間でプログラムデータをやり取りする際に必要なコードの量を最小限にすることができます。
1012
1113## TComponent / TPersistent
1214
1315**TPersistent** クラスは、Classes ユニット内で定義され、**{\$M+}**というコンパイラスイッチで使用されます。このスイッチは定義されたクラスに対して、 Run Time Type Information(**RTTI**)(訳注:実行時型情報)を付加するコンパイラ指令です。 つまり、TPersistentクラスと、その派生クラスは、実行時型情報をもつクラス群として機能することを意味します。'Published' のプロパティは'public'のプロパティと同様に使用することができますが、実行時型情報が付加されていることで、これらのクラスに対しては、実行時にプロパティへの動的なアクセスが可能になります。
1416
1517すべてのPublished のプロパティが、読み書きできるだけでなく、実行時に、どのような名前で、どのような型になっているかをリストアップすることができます。 IDE では、ユーザーが生成したコンポーネントを、(今つくられたコンポーネントがどのようなものか、過去につくられたIDEが知る由もありませんが、しかし..)IDEがそのクラスをプロパティを通して自由に扱えるようにするための優れた機能です。
1618
1719**TComponent**はTPersistentから派生し、子コンポーネントを持てるようになっています。これはストリーミングで大変重要な事項です。1つのコンポーネントが**ルートコンポーネント**になると、子コンポーネントのリストから**lookup root**によって参照されます。
1820
1921(訳注:1つのクラスがストリーミング or シリアライズされる時は、メモリ上での参照問題をなんらかの形で解決する必要がある。ルートはストリーミングの基点となるクラスであり、参照解決の重要な1つである。)
2022
2123## TReader / TWriter
2224
2325TReaderとTWriterは、ストリームにたいしてTComponentを読み書きするためのワーカークラスです。(訳注:ストリームクラスにたいして補助的に便利な関数の集まりになっています。) CreateLRSReaderとCreateLRSWriterを見てください。 これらは、特別なフォーマットを読み書きする場合**Driver**を使います。 このとき、LResourcesユニットで定義されるバイナリオブジェクトフォーマットのためのreader(TLRSObjectReader)とwriter(TLRSObjectWriter)があり、Laz_XMLStreamingで定義されるTDOMDocumentのためのwriter (TXMLObjectWriter)があります。
2426
2527LResourcesユニットは、バイナリフォーマットからテキストに変換する関数とその逆の関数を含みます。 (LRSObjectBinaryToText, LRSObjectTextToBinary). DelphiではWidestringでしたが、LCLは文字列に対してはUTF8を好みます。 そのための変換関数も同様にいくつかあります。
2628
2729## 独自コンポーネントの書き方 - Part 1
2830
2931カスタムコンポーネントは次のようにシンプルです:
3032
31-`type`\
32-` TMyComponent = class(TComponent)`\
33-` private`\
34-`   FID: integer;`\
35-` published`\
36-`   property ID: integer read FID write FID;`\
37-` end;`
33+``` pascal
34+type
35+ TMyComponent = class(TComponent)
36+ private
37+ FID: integer;
38+ published
39+ property ID: integer read FID write FID;
40+ end;
41+```
3842
3943## ストリームにコンポーネントを書き出す
4044
4145[[doc:lcl/lresources|LResources]] ユニットには次のような関数があります:
4246
43-` procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);`
47+``` pascal
48+ procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
49+```
4450
4551これは、コンポーネントをバイナリフォーマットでストリームに書き出します。
4652
4753たとえば次のようにします:
4854
4955 procedure TForm1.Button1Click(Sender: TObject);
5056 var
5157 AStream: TMemoryStream;
5258 begin
5359 AStream:=TMemoryStream.Create;
5460 try
5561 WriteComponentAsBinaryToStream(AStream,AGroupBox);
5662 ... save stream somewhere ...
5763 finally
5864 AStream.Free;
5965 end;
6066 end;
6167
6268(訳注:この例では、メモリ上にグループボックスのすべてのプロパティがバイナリ形式で保管される。  一般的には、バッファとして使う以外には、メモリ上に入れる例は少ない。ファイルストリームをつかって、それをファイルに保存して、後日読み出したり、ソケットストリームをつかって、ソケットで別のマシンに転送したりして、オブジェクトの状態を「保存」「転送」することができる。完全に保存するためには、オブジェクトの状態がすべてプロパティでpublishされていなくてはならないことに注意しよう。 Lazarusでは、フォームのデザイン等(=つまりプロパティの設定値)を設計時(コンパイル時)にリソースに保存し、実行時にリソースからプロパティを復元して実行するメカニズムがある。 他の言語では、一般的には、シリアライズと呼ばれることがある)
6369
6470## ストリームからコンポーネントを読み出す
6571
6672LResources には次のような関数があります:
6773
68-` procedure ReadComponentFromBinaryStream(AStream: TStream;`\
69-`   var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);`
74+``` pascal
75+ procedure ReadComponentFromBinaryStream(AStream: TStream;
76+ var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
77+```
7078
7179- AStream はコンポーネントをバイナリフォーマットで含んでいるストリームを指定します。
7280- RootComponent は存在しているコンポーネントを指定した場合はそのプロパティは上書きされます。nilを指定した場合は、新しいコンポーネントがストリームから生成されます。
7381- OnFindComponentClass はTReaderによって、ストリーム内のクラス名から、クラスを取得するのに使われる、イベント関数です。(訳注:クラス参照型を取得することで、TReaderは名前文字列からそのクラスのコンストラクタを呼ぶことができる)
7482
7583たとえば、次のように、書くことができます:
7684
7785 procedure TCompStreamDemoForm.OnFindClass(Reader: TReader;
7886 const AClassName: string; var ComponentClass: TComponentClass);
7987 begin
8088 if CompareText(AClassName,'TGroupBox')=0 then
8189 ComponentClass:=TGroupBox
8290 else if CompareText(AClassName,'TCheckBox')=0 then
8391 ComponentClass:=TCheckBox;
8492 end;
8593
8694- コンポーネントを新規に生成した場合、Owner にはそのコンポーネントのオーナーとなります。
8795
8896## ストリーム可能なプロパティについて
8997
9098TReaderやTWriterがストリームできるプロパティには、いくつかの条件があります。
9199
92100- 基本型はストリームできます。すなわち、integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, などなど. .
93101- TPersistent とその派生クラスはストリームできます。
94102
95103- 構造体、object型、TPersistentから派生しないクラス型は、ストリームできません。
96104
97105それらをストリームするには、TReader/TWriterにその方法を教える必要があります。次を見てください。#Streaming custom Data - DefineProperties.
98106
99107## カスタムデータをストリームする - DefineProperties
100108
101109DefinePropertiesをオーバーライドすることで、通常ではストリームできないデータをストリームすることができるようになります。この方法は、すべてのデータに対して、たとえ基本型をもたないものでも、ストリームすることを可能にします。たとえば、あなたが作ったコンポーネントの変数 **FMyRect: TRect**をストリームしたい場合、次のような3つのメソッドをコンポーネントに加えます。
102110
103111 procedure DefineProperties(Filer: TFiler); override;
104112 procedure ReadMyRect(Reader: TReader);
105113 procedure WriteMyRect(Writer: TWriter);
106114
107115続けて次のように実装してください:
108116
109117 procedure TMyComponent.DefineProperties(Filer: TFiler);
110118 var
111119 MyRectMustBeSaved: Boolean;
112120 begin
113121 inherited DefineProperties(Filer);
114122 MyRectMustBeSaved:=(MyRect.Left<>0)
115123 or (MyRect.Top<>0)
116124 or (MyRect.Right<>0)
117125 or (MyRect.Bottom<>0);
118126 Filer.DefineProperty('MyRect',@ReadMyRect,@WriteMyRect,MyRectMustBeSaved);
119127 end;
120128
121129 procedure TMyComponent.ReadMyRect(Reader: TReader);
122130 begin
123131 with Reader do begin
124132 ReadListBegin;
125133 FMyRect.Left:=ReadInteger;
126134 FMyRect.Top:=ReadInteger;
127135 FMyRect.Right:=ReadInteger;
128136 FMyRect.Bottom:=ReadInteger;
129137 ReadListEnd;
130138 end;
131139 end;
132140
133141 procedure TMyComponent.WriteMyRect(Writer: TWriter);
134142 begin
135143 with Writer do begin
136144 WriteListBegin;
137145 WriteInteger(FMyRect.Left);
138146 WriteInteger(FMyRect.Top);
139147 WriteInteger(FMyRect.Right);
140148 WriteInteger(FMyRect.Bottom);
141149 WriteListEnd;
142150 end;
143151 end;
144152
145153This will save MyRect as a property 'MyRect'. これで、MyRectをプロパティとして保存できます。 しかし、もし、たくさんのTRectをストリームする必要がある時、このようなコードで何度も書きたくないでしょう。 LResourcesユニットには、rectプロパティを定義する手続きを書く例があります。
146154
147-` procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);`\
148-` `
155+``` pascal
156+ procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
157+```
149158
150159さきほどの方法はこれを使うと非常に短く記述することができます:
151160
152161 procedure TMyComponent.DefineProperties(Filer: TFiler);
153162 begin
154163 inherited DefineProperties(Filer);
155164 DefineRectProperty(Filer,'MyRect',@FMyRect,nil);
156165 end;
157166
158167## 独自コンポーネント - Part 2
159168
160169今回の例では、たった数行のコードで、クラスを拡張して任意のプロパティを使うことができるか、を示します。
161170
162171 type
163172 TMyComponent = class(TComponent)
164173 private
165174 FID: integer;
166175 FRect1: TRect;
167176 FRect2: TRect;
168177 protected
169178 procedure DefineProperties(Filer: TFiler); override;
170179 public
171180 property Rect1: TRect read FRect1 write FRect1;
172181 property Rect2: TRect read FRect2 write FRect2;
173182 published
174183 property ID: integer read FID write FID;
175184 end;
176185
177186 procedure TMyComponent.DefineProperties(Filer: TFiler);
178187 begin
179188 inherited DefineProperties(Filer);
180189 DefineRectProperty(Filer,'Rect1',@FRect1,nil);
181190 DefineRectProperty(Filer,'Rect2',@FRect2,nil);
182191 end;
183192
184193このコンポーネントは [[RTTI controls/ja|RTTI コントロール]] を使用して、保存、読み込み利用ができます。これ以上、詳細なコードを書く必要はありません。
185194
186195## コンポーネントをXMLへストリームする
187196
188197XML形式でコンポーネントをストリームさせるのはとても簡単です。 lazarus/examples/xmlstreaming/ の例を見てください。
189198
190199## 結論
191200
192201RTTI は強力なメカニズムです。これは全てのクラスでストリームを簡単に取り扱うことができ、また膨大で退屈な load/save コードの記述を回避するのに役立ちます。
193202
194203こちらも参照してください。 [[RTTI controls/ja|RTTI コントロール]]
195204
196205---
197206*From the Free Pascal wiki, page "Streaming components/ja", as it stood on 2007-04-12 after a change by Vincent, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history