Delphi RTL( Genericsコレクション vs 従来のコレクション)
目次
概要
Delphiには以前からList<T>のようなGenericsコレクションがありましたが、古いスタイルのListも残っています。どちらを使用すべきでしょうか?
2009のバージョン以降、Delphiがジェネリックスをサポートするようになってから、ランタイムライブラリ(RTL)にGenericsコレクションユニットが搭載されました。ただし、RTLはまだ従来のコレクションが利用できます。最近、どちらを使用することをお勧めするかについていくつかのリクエストがありました。
議論はもう少し深くなる可能性がありますが、このサポート情報は、ベストプラクティスとして推奨するものではありませんので、その点はどうか予めご注意ください。
クラシックコンテナクラス(従来のコレクション)
RTLの最も基本的なコレクションクラスはTListクラス(System.Classesユニット内)であり、ポインタ(またはオブジェクト参照)の一般的なリストを表します。 コンテナクラス(System.Contnrsユニット内)には、以下のクラスが含まれます。
- TObjectList: TObject参照のためのリストで、所有権のサポートが含まれています。
- TComponentList: 所有権のサポートに加えて、すべてのコンポーネントが持つ通知のサポートを追加
- TClassList
- TOrderedList
- TStackから継承したTObjectStack および TQueueから継承したTObjectQueue
- 様々なバケットリスト
など
これらのコンテナを使用する際には、管理しているオブジェクトの型から、リストがサポートする固定型へのキャストが必要になることがよくあります。このため、リストから抽出されたオブジェクトの型を("as "のような)動的なキャストで継続的にチェックすると、エラーが発生する可能性があり、実行時に遅延が発生します。
myList: TList;
myList.Get(3) as TMyObject
Genericsコレクションクラス
Delphiでは、言語のジェネリックスに加えて、基本的なGenericsコレクションのセットが追加されました。これらは、System.Generics.Collectionsユニットで定義され、必要な特定のデータ型に関連付けられたコンテナの一般的なプループリントを提供します。Genericsコレクションには、以下のクラスが含まれます。
- TList<T>: ソートと列挙のサポートを含む基本的な汎用リスト
- TThreadList<T>: ロック機能を備えたスレッドセーフなリスト
- TQueue<T>および TStack<T>
- TDictionary<TKey,TValue>: カスタマイズ可能なキーと値のタイプを備えたかなり強力なディクショナリクラス
- TObjectList<T:class>: 所有権をサポートし、以下の他のコンテナと同様に、オブジェクト型にのみ使用可能な
- TObjectQueue<T: class> および TObjectStack<T: class>
- TObjectDictionary<TKey,TValue>
- TThreadedQueue<T>
Genericsコレクションの利点
Genericsコレクションは、使用する必要のある特定のデータ型に対して特定のコンテナを定義する機能を提供し、実行時に同じことを行うのではなく、コンパイラにデータ型の互換性をチェックさせます。これにより、コードがよりクリーンで読みやすく、アプリケーションがより堅牢になり、実行時のチェックの必要性が減ることで実行速度が向上します。
myList: TList <TMyObject>;
myList.Get(3); // returns TMyObject
Delphiの新しいライブラリやサブシステムはすべて新しいコレクションを使用しており、例えば、FireMonkeyは従来のコーディングスタイルではなく、新しいコレクションを多用しています。
Genericsコレクションを使用しない理由はありますか?
新しいコレクションを使用しない理由は、基本的に2つあります。1つは、すでに動いているコードがある場合、それを変更したくないケースがあります。さらに、そのコードがライブラリ(VCLなど)で利用している場合、それを変更すると、そのライブラリを使用しているコードとの間に互換性がなくなる可能性があるケースです。
もう1つの理由は、コレクションで使用されるすべてのデータ型について、クラスメソッドがほぼ同一であっても複製されるため、Delphiのジェネリックスは生成されるバイナリコードの大幅な膨張化を引き起こす可能性がある点です。 非常に大規模なアプリケーションでは、この仕様は、コンパイルとリンクに要する時間、および実行可能ファイル側のバイナリサイズに悪影響を及ぼす恐れがあります。
まとめ
- Genericsコレクションは、Delphi 2009以降で言語機能として採用
- FireMonkeyのフレームワーク内部のコレクションは、従来のコレクションクラスではなく、Genericsコレクションクラスに刷新されている
- Genericsコレクションを利用すると、その仕様上、生成されるバイナリコードの肥大化や、それによる動作遅延が発生する場合がある
- 既存のライブラリ(例えば、VCLなど)で従来のコレクションクラスを多く利用している場合、Genericsコレクションに書き換えると互換性がなくなる可能性がある
Windows以外のプラットホームで利用するケースを除いて、上記の観点から従来のコレクションクラスを引き続き利用したほうが良い場合があります。
但し、このサポート情報は繰り返すになりますが、あくまで要約で、これをベストプラクティスとして推奨するものではありません。Delphiの長期のバージョンにわたって同じ仕様を維持するか保証できない点と、将来のバージョンでは、Genericsコレクションの改善が行われれば、現行とは評価が変わる可能性があるためです。
結局は、お客様のプロジェクトに応じて、どのコレクションを利用すべきかご判断いただくことになります。
そのため、この情報がGenericsコレクションを十分に理解していないDelphiの古いージョンを利用している開発者の皆様の参考になれば幸いです。