MSDN によると、16 バイト以下のクラスは struct [citation]として処理する方が適切です。
何故ですか?
構造体が 16 バイトを超える場合、クラスよりも効率が悪いということですか、それとも同じですか?
クラスが 16 バイト未満かどうかをどのように判断しますか?
構造体がクラスのように振る舞うのを制限するものは何ですか? (パラメーターなしのコンストラクターを許可しないことに加えて)
7 に答える
この質問にはいくつかの異なる答えがあり、少し主観的ですが、私が考えることができるいくつかの理由は次のとおりです。
struct
s は値型、class
es は参照型です。合計ストレージに 16 バイトを使用している場合、それぞれにメモリ参照 (4 ~ 8 バイト) を作成する価値はおそらくありません。- オブジェクトが非常に小さい場合は、オブジェクトへの参照ではなく、IL スタックにプッシュできることがよくあります。これにより、呼び出し先側でのメモリ逆参照が排除されるため、一部のコードを実際に高速化できます。
- IL のクラスに関連付けられた余分な「綿毛」が少しあります。データ構造が非常に小さい場合、この綿毛はとにかく使用されないため、必要のない余分ながらくたです。
ただし、 astruct
と aの最も重要な違いは、 s が値型でes が参照型であることです。class
struct
class
「効率的」とは、おそらくクラスまたは構造体を表すのに必要なメモリの量について話しているのでしょう。
32 ビット プラットフォームでは、オブジェクトの割り当てに最低 16 バイトが必要です。64 ビット プラットフォームでは、オブジェクトの最小サイズは 24 バイトです。したがって、純粋に使用されるメモリの量から見ている場合、16 バイト未満のデータを含む構造体は、対応するクラスよりも「優れています」。
しかし、使用されるメモリの量だけがすべてではありません。値型 (構造体) は、参照型 (クラス) とは根本的に異なります。構造体は操作が不便な場合があり、注意しないと実際にパフォーマンスの問題を引き起こす可能性があります。
もちろん、本当の答えは、状況に最も適したものを使用することです。ほとんどの場合、クラスを使用する方がはるかに優れています。
このリンクを確認してください。今日のSOの回答の1つである.NETTypeInternalsで見つけました。構造体とクラスの違いについては、SOとグーグルで「参照型と値型」を検索することもできます。
構造体がクラスのように動作することを制限するものは何ですか?
多くの違いがあります。たとえば、構造体から継承することはできません。
仮想メソッドを持つことはできないため、構造体を使用してインターフェースを実装することはできません。構造体のインスタンスメソッドは構造体のプライベートフィールドにアクセスできますが、それを除けば、補助的な「ヘルパー」関数のように動作します(不変の構造体の場合、プライベートデータにアクセスする必要がない場合もあります)。したがって、クラスメソッドほど「価値のある」ものではないことがわかりました。
struct
class
s は、ヒープではなくスタックに格納されるため、es とは異なります。つまり、struct
as パラメーターを使用してメソッドを呼び出すたびに、コピーが作成されてメソッドに渡されます。そのため、大きなstruct
s は非常に非効率的です。
それにもかかわらず、 s の使用は積極的に思いとどまらせますstruct
。これは、いくつかの微妙なバグを引き起こす可能性があるためです。たとえば、 a のフィールドを変更するstruct
と、呼び出し元に反映されません (コピーのみを変更したため)。クラス。
したがって、16 バイトは の妥当な最大サイズだと思いますが、struct
それでもほとんどの場合、class
. それでも を作成したい場合はstruct
、少なくとも不変にするようにしてください。
メモリ内では、構造体はデータを直接保持しますが、クラスはポインタのように動作します。構造体をパラメーターとしてメソッドに渡すとその値が渡され(スタックにコピーされ)、クラスが値への参照を渡すため、これだけでも重要な違いが生じます。構造体が大きい場合は、メソッド呼び出しごとに多くの値をコピーすることになります。本当に小さい場合は、値をコピーして直接使用する方が、ポインターをコピーして別の場所から取得するよりもおそらく高速です。
制限について:nullに割り当てることはできません(ただし、Nullable <>を使用することはできます)。また、すぐに初期化する必要があります。
これは、CLR が構造体とクラスを処理する方法が異なるためです。構造体は値型であり、マネージド ヒープではなくスタック上にあることを意味します。構造体をメソッド引数として渡し始めると、メソッドに渡されるときに構造体全体がコピーされるため、オーバーヘッドが発生するため、経験則として構造体を小さく保つことをお勧めします。
クラスはメソッドへの参照のコピーを渡すため、メソッドの引数として使用した場合に発生するオーバーヘッドははるかに少なくなります。
クラスのサイズを決定する最善の方法は、クラスのすべてのメンバーに必要なバイト数と、CLR オーバーヘッド (同期ブロック インデックスとオブジェクトの型への参照) 用の余分な 8 バイトを合計することです。
構造体のインスタンスをコピーすると、クラスの新しいインスタンスを作成して古いインスタンスからデータをコピーするよりも時間がかかりませんが、クラス インスタンスは共有できますが、構造体インスタンスは共有できません。したがって、「structvar1 = structvar2」では新しい構造体インスタンスをコピーする必要がありますが、「classvar1 = classvar2」では classvar1 と classvar2 が同じ構造体インスタンスを参照できます (新しいものを作成する必要はありません)。
新しい構造体インスタンスの作成を処理するコードは、最大 16 バイトのサイズに最適化されています。構造体が大きくなると、処理効率が低下します。構造体は、構造体を保持するすべての変数が独立したインスタンスを保持する場合に有利です (つまり、特定の 2 つの変数が同一のインスタンスを保持すると期待する理由はありません)。多くの変数が同じインスタンスを保持する可能性がある場合、それらは(たとえ勝っていたとしても)大した勝利ではありません。