クラスまたは構造体の選択

すべてのフレームワーク デザイナーが直面する基本的な設計上の判断の 1 つは、型をクラスとして設計するか (参照型)、構造体として設計するか (値型) ということです。 この選択を行うには、参照型と値型の動作の違いをよく理解しりことが重要です。

参照型と値型の 1 つ目の違いは、参照型がヒープに割り当てられ、ガベージ コレクションの対象となるのに対し、値型はスタック上に、またはそれを含む型にインラインで割り当てられ、スタックがアンワインドされたとき、またはそれを含む型が割り当て解除されたときに割り当てが解除されるという点です。 したがって、値型の割り当てと割り当て解除は、参照型の割り当てと割り当て解除よりも一般的に低コストです。

次に、参照型の配列はアウトオブラインで割り当てられます。つまり、配列要素は、ヒープに存在する参照型のインスタンスへの参照にすぎません。 値型の配列はインラインで割り当てられます。つまり、配列要素は値型の実際のインスタンスです。 したがって、値型の配列の割り当てと割り当て解除は、参照型の配列の割り当てと割り当て解除よりもはるかに低コストです。 また、ほとんどの場合、値型の配列は参照の局所性が大幅に向上します。

次の相違点は、メモリ使用量に関連します。 参照型に、またはそれによって実装されるインターフェイスのいずれかにキャストするとき、値型はボックス化されます。 値型にキャストし直すと、ボックス化が解除されます。 ボックスはヒープに割り当てられ、ガベージ コレクションの対象になるオブジェクトであるため、ボックス化とボックス化解除が過剰になると、ヒープ、ガベージ コレクター、そして最終的にアプリケーションのパフォーマンスに悪影響を及ぼすおそれがあります。 これに対し、参照型がキャストされるとき、このようなボックス化は行われません (詳細については、「ボックス化とボックス化解除」を参照してください)。

次に、参照型の割り当てによって参照がコピーされます。一方、値型の割り当てでは、値全体がコピーされます。 したがって、大きな参照型の割り当ては、大きな値型の割り当てよりも低コストです。

最後に、参照型は参照によって渡されるのに対し、値型は値によって渡されます。 参照型のインスタンスに加えた変更は、そのインスタンスを指すすべての参照に影響します。 値型のインスタンスは、値によって渡されるときにコピーされます。 値型のインスタンスが変更された場合、当然、そのインスタンスのコピーには影響しません。 コピーはユーザーによって明示的に作成されるのではなく、引数が渡されるとき、または戻り値が返されるときに暗黙的に作成されるため、変更可能な値型は多くのユーザーに混乱を招くおそれがあります。 したがって、値型は不変である必要があります。

経験則として、フレームワークのほとんどの型はクラスである必要があります。 ただし、場合によっては、値型の特性によって、構造体を使用する方が適切であることがあります。

✔️ 型のインスタンスが小さく、有効期間が短いことが多い場合、または他のオブジェクトに埋め込まれることが多い場合は、クラスではなく構造体を定義することを検討してください。

❌ 型が次のすべての特性を持つ場合を除き、構造体を定義することは避けてください。

  • プリミティブ型 (intdouble など) と同様に、論理的に単一の値を表す。

  • インスタンスのサイズが 16 バイト未満である。

  • 不変である。

  • 頻繁にボックス化する必要がない。

その他すべての場合は、型をクラスとして定義する必要があります。

Portions © 2005, 2009 Microsoft Corporation.All rights reserved.

2008 年 10 月 22 日に Microsoft Windows Development シリーズの一部として、Addison-Wesley Professional によって発行された、Krzysztof Cwalina および Brad Abrams による「Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition」 (フレームワーク デザイン ガイドライン: 再利用可能な .NET ライブラリの規則、用法、パターン、第 2 版) から Pearson Education, Inc. の許可を得て再印刷されています。

関連項目