パブリックな可変フィールドまたはプロパティを持つ構造体は悪ではありません。
「プロパティ」を変更する構造体メソッド(プロパティセッターとは異なります)は、.netがそれらを変更しないメソッドと区別する手段を提供していないために、多少悪です。「this」を変更しない構造体メソッドは、読み取り専用の構造体であっても、防御的なコピーを必要とせずに呼び出すことができます。「this」を変更するメソッドは、読み取り専用の構造体ではまったく呼び出されません。.netは、「this」を変更しない構造体メソッドが読み取り専用の構造体で呼び出されるのを禁止したくないが、読み取り専用の構造体を変更できるようにしたくないので、構造的に読み取り構造をコピーします。コンテキストのみで、間違いなく両方の世界で最悪の事態に陥っています。
ただし、読み取り専用コンテキストでの自己変異メソッドの処理に関する問題にもかかわらず、可変構造体は、可変クラス型よりはるかに優れたセマンティクスを提供することがよくあります。次の3つのメソッドシグネチャを検討してください。
struct PointyStruct {public int x、y、z;};
クラスPointyClass {public int x、y、z;};
void Method1(PointyStruct foo);
void Method2(ref PointyStruct foo);
void Method3(PointyClass foo);
それぞれの方法について、次の質問に答えてください。
- メソッドが「安全でない」コードを使用しない場合、fooを変更する可能性がありますか?
- メソッドが呼び出される前に 'foo'への外部参照が存在しない場合、外部参照が後に存在する可能性はありますか?
答え:
質問1:
Method1()
:なし(意図クリア)
Method2()
:はい(意図クリア)
Method3()
:はい(不確かな意図)
質問2:
Method1()
:いいえ
Method2()
:いいえ(危険な場合を除き)
Method3()
:はい
Method1はfooを変更できず、参照を取得しません。Method2は、一時的なfooへの参照を取得します。これは、fooのフィールドを変更するまで何度でも、任意の順序で変更できますが、その参照を永続化することはできません。Method2が戻る前は、安全でないコードを使用していない限り、「foo」参照から作成された可能性のあるすべてのコピーが消えています。Method3は、Method2とは異なり、無差別に共有可能なfooへの参照を取得します。これを使用して何ができるかはわかりません。それはfooをまったく変更しないかもしれません、それはfooを変更してから戻るかもしれません。
構造の配列は素晴らしいセマンティクスを提供します。Rectangle型のRectArray [500]が与えられた場合、たとえば、要素123を要素456にコピーし、しばらくしてから要素123の幅を要素456に影響を与えずに555に設定する方法は明らかです。 "RectArray [432] = RectArray [321 ]; ...; RectArray [123] .Width = 555; "。RectangleがWidthと呼ばれる整数フィールドを持つ構造体であることを知っていると、上記のステートメントについて知っておく必要があるすべてのものがわかります。
ここで、RectClassがRectangleと同じフィールドを持つクラスであり、タイプRectClassのRectClassArray [500]で同じ操作を実行したいとします。おそらく、配列は、可変のRectClassオブジェクトへの初期化済みの不変の参照を500個保持することになっています。その場合、適切なコードは「RectClassArray [321] .SetBounds(RectClassArray [456]); ...; RectClassArray [321] .X = 555;」のようになります。おそらく、配列は変更されないインスタンスを保持すると想定されているため、適切なコードは「RectClassArray [321] = RectClassArray [456]; ...; RectClassArray [321] = New RectClass(RectClassArray [321 ]); RectClassArray [321] .X = 555; " 何をすべきかを知るためには、RectClassの両方についてより多くを知る必要があります(たとえば、コピーコンストラクター、コピー元メソッドなどをサポートしていますか)。)とアレイの使用目的。構造体を使用するほどきれいな場所はありません。
確かに、配列以外のコンテナークラスが構造体配列の明確なセマンティクスを提供するための良い方法はありません。コレクションに文字列などでインデックスを付ける場合、最善の方法は、インデックスの文字列、ジェネリックパラメーター、および渡されるデリゲートを受け入れるジェネリック "ActOnItem"メソッドを提供することです。ジェネリックパラメーターとコレクションアイテムの両方を参照する。それは構造体配列とほぼ同じセマンティクスを可能にしますが、vb.netとC#の人々が素晴らしい構文を提供するように追求されない限り、コードは、それが合理的なパフォーマンスであっても不格好に見えるでしょう(一般的なパラメーターを渡すと静的デリゲートの使用を許可し、一時クラスインスタンスを作成する必要がなくなります)。
個人的に、私は憎しみのあるエリック・リッペルトらに腹を立てています。変更可能な値の型に関する説明。それらは、至る所で使用されている無差別参照タイプよりもはるかに明確なセマンティクスを提供します。.netの値タイプのサポートにはいくつかの制限がありますが、変更可能な値タイプが他の種類のエンティティよりも適している場合が多くあります。
int
s、bool
sであると主張することに似ており、他のすべての値タイプは悪です。可変性と不変性の場合があります。これらのケースは、メモリの割り当て/共有のタイプではなく、データが果たす役割に依存します。