構造体には2つの異なる使用例があります。場合によっては、単一の値をカプセル化し、ほとんどがクラスのように動作するが、パフォーマンスが向上し、デフォルト値がnull以外の型が必要になります。そのような場合、私が不透明な構造と呼ぶものを使用する必要があります。構造に関するMSDNのガイドラインは、これが唯一の使用例であるという前提で書かれています。
ただし、他の場合では、構造体の目的は、ダクトテープでいくつかの変数をバインドすることです。そのような場合、それらの変数をパブリックフィールドとして公開するだけの透過的な構造体を使用する必要があります。そのようなタイプについては何も悪いことはありません。悪なのは、すべてがクラスオブジェクトのように動作する必要がある、またはすべてが「カプセル化」されている必要があるという概念です。構造体のセマンティクスが次のようなものである場合:
- 状態全体を公開する読み取り可能なメンバー(フィールドまたはプロパティ)の固定セットがいくつかあります
- これらのメンバーに必要な値のセットがあれば、それらの値を使用してインスタンスを作成できます(値の組み合わせは禁止されていません)。
- 構造体のデフォルト値は、これらすべてのメンバーをそれぞれのタイプのデフォルト値に設定する必要があります。
そして、それらに変更を加えると、それを使用するコードが壊れます。その場合、構造体の将来のバージョンで実行できる可能性のある、透過的な構造体では実行できないことはなく、透過的な構造体で許可される将来のバージョンもありません。構造体のバージョンは防ぐことができるでしょう。その結果、カプセル化は付加価値なしでコストを課します。
実用的な場合は常に、すべての構造体を透明または不透明にするように努めることをお勧めします。さらに、.netが構造体メソッドを処理する方法に欠陥があるため、this
おそらくプロパティセッターを除いて、不透明な構造体のパブリックメンバーを変更しないようにすることをお勧めします。皮肉なことに、MSDNのガイドラインでは、「単一の値」を表さないものには構造体を使用すべきではありませんが、あるコードから別のコードに変数のグループを渡したいだけの一般的なシナリオでは、透明な構造体は、不透明な構造体やクラスタイプよりもはるかに優れており、フィールドの数に応じて優位性のマージンが大きくなります。
ところで、元の質問に関しては、プログラムが(1)車と、(2)特定の車に関連する情報の2種類のことを処理したい場合があることを表すのが役立つことをお勧めします。構造体があり、型のフィールドを保持するCarState
インスタンスがあると便利な場合があることをお勧めします。これにより、のインスタンスが外部コードに状態を公開できるようになり、制御された状況下で外部コードが状態を変更できるようになります(次のようなメソッドを使用)Car
CarState
Car
delegate void ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);
delegate void ActionByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, T3 p3);
void UpdateState<TP1>(ActionByRef<CarState, TP1> proc, ref TP1 p1)
{ proc(ref myState, ref p1); }
void UpdateState<TP1,TP2>(ActionByRef<CarState, TP1,TP2> proc, ref TP1 p1, ref TP2 p2)
{ proc(ref myState, ref p1, ref p2); }
このようなメソッドは、車の状態を変更可能なクラスにすることのパフォーマンス上の利点のほとんどを提供しますが、無差別なオブジェクト参照に関連する危険性がないことに注意してください。外部コードが他のときにその状態を変更するCar
ことを許可せずに、外部コードが上記のメソッドを介して車の状態を更新できるようにすることができます。
ところで、私は本当に.netに、「安全な」構造体またはクラスがその構成要素の1つ以上のメンバーをカプセル化するものと見なされるように指定する方法があればいいのにと思います[たとえば、Rectangle
calledR
とString
calledを保持する構造体Name
がフィールド、、、を持ちX
、Y
対応Width
するHeight
構造体フィールドをエイリアスします。それが可能であれば、タイプが以前に予想されていたよりも多くの状態を保持する必要がある状況を大幅に促進します。現在のCILは、安全なタイプでそのようなエイリアシングを許可しているとは思いませんが、それができなかった概念的な理由はありません。