Difference between revisions of "Streaming components/ja"
Line 14: | Line 14: | ||
== TComponent / TPersistent == | == TComponent / TPersistent == | ||
− | '''TPersistent''' クラスは、Classes ユニット内で定義され、'''{$M+}''' | + | '''TPersistent''' クラスは、Classes ユニット内で定義され、'''{$M+}'''というコンパイラスイッチで使用されます。このスイッチは定義されたクラスに対して、 Run Time Type Information('''RTTI''')(訳注:実行時型情報)を付加するコンパイラ指令です。 |
− | + | つまり、TPersistentクラスと、その派生クラスは、実行時型情報をもつクラス群として機能することを意味します。'Published' のプロパティは'public'のプロパティと同様に使用することができますが、実行時型情報が付加されていることで、これらのクラスに対しては、実行時にプロパティへの動的なアクセスが可能になります。 | |
+ | |||
+ | すべてのPublished のプロパティが、読み書きできるだけでなく、実行時に、どのような名前で、どのような型になっているかをリストアップすることができます。 | ||
+ | IDE では、ユーザーが生成したコンポーネントを、(今つくられたコンポーネントがどのようなものか、過去につくられたIDEが知る由もありませんが、しかし..)IDEがそのクラスをプロパティを通して自由に扱えるようにするための優れた機能です。 | ||
'''TComponent'''はTPersistentから派生し、子コンポーネントを持てるようになっています。これはストリーミングで大変重要な事項です。1つのコンポーネントが'''ルートコンポーネント'''になると、子コンポーネントのリストから'''lookup root'''によって参照されます。 | '''TComponent'''はTPersistentから派生し、子コンポーネントを持てるようになっています。これはストリーミングで大変重要な事項です。1つのコンポーネントが'''ルートコンポーネント'''になると、子コンポーネントのリストから'''lookup root'''によって参照されます。 | ||
+ | |||
+ | (訳注:1つのクラスがストリーミング or シリアライズされる時は、メモリ上での参照問題をなんらかの形で解決する必要がある。ルートはストリーミングの基点となるクラスであり、参照解決の重要な1つである。) | ||
== TReader / TWriter == | == TReader / TWriter == |
Revision as of 11:47, 26 October 2006
│
Deutsch (de) │
English (en) │
français (fr) │
日本語 (ja) │
polski (pl) │
português (pt) │
はじめに
通常、ハードディスクやネットワークストリームにデータを蓄えておく場合は、それらの特性に応じてロードやセーブ(読み込みや書き込み)のためのコードを書かなければなりません。このチュートリアルでは、余分な load/save コードを書くことなく、RTTI を使用して、ストリームから読み込みや書き込みを行うクラスの書き方について述べます。
lazarus コードでのサンプルや TCheckBox を含んだ TGroupBox をストリームに保存したり、両方のコンポーネントのコピーを作成してストリームから戻すといったデモを説明します。
<lazaruspath>/examples/componentstreaming/ を参照してください。
RTTI コントロールとのコンビネーションで、GUI と ディスク/ネットワークとの間でプログラムデータをやり取りする際に必要なコードの量を最小限にすることができます。
TComponent / TPersistent
TPersistent クラスは、Classes ユニット内で定義され、{$M+}というコンパイラスイッチで使用されます。このスイッチは定義されたクラスに対して、 Run Time Type Information(RTTI)(訳注:実行時型情報)を付加するコンパイラ指令です。 つまり、TPersistentクラスと、その派生クラスは、実行時型情報をもつクラス群として機能することを意味します。'Published' のプロパティは'public'のプロパティと同様に使用することができますが、実行時型情報が付加されていることで、これらのクラスに対しては、実行時にプロパティへの動的なアクセスが可能になります。
すべてのPublished のプロパティが、読み書きできるだけでなく、実行時に、どのような名前で、どのような型になっているかをリストアップすることができます。 IDE では、ユーザーが生成したコンポーネントを、(今つくられたコンポーネントがどのようなものか、過去につくられたIDEが知る由もありませんが、しかし..)IDEがそのクラスをプロパティを通して自由に扱えるようにするための優れた機能です。
TComponentはTPersistentから派生し、子コンポーネントを持てるようになっています。これはストリーミングで大変重要な事項です。1つのコンポーネントがルートコンポーネントになると、子コンポーネントのリストからlookup rootによって参照されます。
(訳注:1つのクラスがストリーミング or シリアライズされる時は、メモリ上での参照問題をなんらかの形で解決する必要がある。ルートはストリーミングの基点となるクラスであり、参照解決の重要な1つである。)
TReader / TWriter
These are the worker classes, which reads/writes a TComponent to/from a stream (See CreateLRSReader and CreateLRSWriter). They use a Driver to read/write a special format. At the moment there are a reader (TLRSObjectReader) and a writer (TLRSObjectWriter) for binary object format defined in the LResources unit and a writer (TXMLObjectWriter) for TDOMDocument defined in Laz_XMLStreaming. The LResources unit also contains functions to convert binary format to text and back (LRSObjectBinaryToText, LRSObjectTextToBinary). The LCL prefers UTF8 for strings, while Delphi prefers Widestrings. So there are some conversion functions as well.
独自コンポーネントの書き方 - Part 1
カスタムコンポーネントは次のようにシンプルです:
type TMyComponent = class(TComponent) private FID: integer; published property ID: integer read FID write FID; end;
Writing a component to a stream
LResources ユニットには次のような関数があります:
procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);
It writes a component in binary format to the stream. For example:
procedure TForm1.Button1Click(Sender: TObject); var AStream: TMemoryStream; begin AStream:=TMemoryStream.Create; try WriteComponentAsBinaryToStream(AStream,AGroupBox); ... save stream somewhere ... finally AStream.Free; end; end;
Reading a component from a stream
LResources には次のような関数があります:
procedure ReadComponentFromBinaryStream(AStream: TStream; var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
- AStream is the stream containing a component in binary format.
- RootComponent is either an existing component, which data will be overwritten, or it is nil and a new component will be created.
- OnFindComponentClass is a function, that is used by TReader to get the class from the classnames in the stream. For example:
procedure TCompStreamDemoForm.OnFindClass(Reader: TReader; const AClassName: string; var ComponentClass: TComponentClass); begin if CompareText(AClassName,'TGroupBox')=0 then ComponentClass:=TGroupBox else if CompareText(AClassName,'TCheckBox')=0 then ComponentClass:=TCheckBox; end;
- コンポーネントを新規に生成した場合、OWner にはそのコンポーネントのオーナーとなります。
Streamable properties
There are some limitations, what types TReader/TWriter can stream:
- Base types can be streamed: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. .
- TPersistent and descendants can be streamed
- records, objects and classes not descending from TPersistent can not be streamed. To stream them you need to tell TReader/TWriter how. See below #Streaming custom Data - DefineProperties.
Streaming custom Data - DefineProperties
You can stream additinal arbitrary data by overriding DefineProperties. This allows to stream all data, that have no base types. For example to stream a variable FMyRect: TRect of your component, add the following three methods to your component:
procedure DefineProperties(Filer: TFiler); override; procedure ReadMyRect(Reader: TReader); procedure WriteMyRect(Writer: TWriter);
続けて次のコードを記述します:
procedure TMyComponent.DefineProperties(Filer: TFiler); var MyRectMustBeSaved: Boolean; begin inherited DefineProperties(Filer); MyRectMustBeSaved:=(MyRect.Left<>0) or (MyRect.Top<>0) or (MyRect.Right<>0) or (MyRect.Bottom<>0); Filer.DefineProperty('MyRect',@ReadMyRect,@WriteMyRect,MyRectMustBeSaved); end; procedure TMyComponent.ReadMyRect(Reader: TReader); begin with Reader do begin ReadListBegin; FMyRect.Left:=ReadInteger; FMyRect.Top:=ReadInteger; FMyRect.Right:=ReadInteger; FMyRect.Bottom:=ReadInteger; ReadListEnd; end; end; procedure TMyComponent.WriteMyRect(Writer: TWriter); begin with Writer do begin WriteListBegin; WriteInteger(FMyRect.Left); WriteInteger(FMyRect.Top); WriteInteger(FMyRect.Right); WriteInteger(FMyRect.Bottom); WriteListEnd; end; end;
This will save MyRect as a property 'MyRect'.
If you stream a lot of TRect, then you probably do not want to write everytime ths code. The unit LResources contains an example how to write a procedure to define a rect property:
procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);
上述の方法はこのように非常に短く記述することもできます:
procedure TMyComponent.DefineProperties(Filer: TFiler); begin inherited DefineProperties(Filer); DefineRectProperty(Filer,'MyRect',@FMyRect,nil); end;
独自コンポーネント - Part 2
Now the example can be extended and we can use arbitrary properties with only a few lines of code:
type TMyComponent = class(TComponent) private FID: integer; FRect1: TRect; FRect2: TRect; protected procedure DefineProperties(Filer: TFiler); override; public property Rect1: TRect read FRect1 write FRect1; property Rect2: TRect read FRect2 write FRect2; published property ID: integer read FID write FID; end; procedure TMyComponent.DefineProperties(Filer: TFiler); begin inherited DefineProperties(Filer); DefineRectProperty(Filer,'Rect1',@FRect1,nil); DefineRectProperty(Filer,'Rect2',@FRect2,nil); end;
このコンポーネントは RTTI コントロール を使用して、保存、読み込み利用ができます。これ以上、詳細なコードを書く必要はありません。
Writing and Reading components from/to XML
Streaming components is simple: See the example in lazarus/examples/xmlstreaming/.
結論
RTTI は強力なメカニズムです。これは全てのクラスでストリームを簡単に取り扱うことができ、また膨大で退屈な load/save コードの記述を回避するのに役立ちます。
こちらも参照してください。