構造体には 2 つの使用例があります。不透明な構造は、不変のクラスを使用して実装できるものに役立ちますが、十分に小さいため、最高の状況であっても、クラスを使用するメリットはほとんどありません。作成されて破棄される頻度は、単純にコピーされる頻度のかなりの割合です。たとえば、Decimal
は 16 バイトの構造体であるため、100 万個のDecimal
値を保持するには 16 メガバイトが必要になります。クラスの場合、Decimal
インスタンスへの各参照には 4 または 8 バイトが必要ですが、個別のインスタンスごとにさらに 20 ~ 32 バイトが必要になるでしょう。要素が少数の異なる配列からコピーされた多くの大きな配列がある場合Decimal
Decimal
インスタンス、クラスが勝つ可能性がありますが、ほとんどのシナリオでは、構造体が勝つことを意味するの 100 万の異なるインスタンスへの 100 万の参照を持つ配列を持つ可能性が高くなります。
このように構造体を使用することは、通常、MSDN から引用されたガイドラインが適用される場合にのみ有効です (ただし、不変性のガイドラインは主に、構造体メソッドが基になる構造体を変更することを示す方法がまだないという事実の結果です)。最後の 3 つのガイドラインのいずれにも当てはまらない場合は、構造体よりも不変クラスを使用する方がよいでしょう。ただし、最初のガイドラインが適用されない場合、不透明な構造体を使用すべきではなく、代わりにクラスを使用すべきではないことを意味します。
場合によっては、データ型の目的は、変数のグループをダクトテープで固定して、それらの値を 1 つの単位として渡すことができるようにすることですが、意味的には別個の変数として残ります。たとえば、多くのメソッドでは、3 次元座標を表す 3 つの浮動小数点数のグループを渡す必要がある場合があります。Point3d
三角形を描画したい場合、9 つの浮動小数点数よりも3 つのパラメーターを渡す方がはるかに便利です。多くの場合、そのような型の目的は、ドメイン固有の動作を与えることではなく、単純に物事を便利に渡す手段を提供することです。このような場合、構造体を適切に使用すれば、構造体はクラスよりも大きなパフォーマンス上の利点を提供できます。型の 3 つの変数を表す構造体double
ダクト テープで固定された には、 type の 3 つのパブリック フィールドが必要ですdouble
。このような構造体により、2 つの一般的な操作を効率的に実行できます。
- 与えられたインスタンスで、その状態のスナップショットを作成して、スナップショットを乱すことなくインスタンスを変更できるようにします
- 不要になったインスタンスを考えると、どうにかしてわずかに異なるインスタンスを考え出す
不変のクラス型を使用すると、クラスが保持するデータ量に関係なく、最初のクラスを固定コストで実行できますが、2 番目のクラスは非効率的です。変数が表すと想定されるデータの量が多いほど、最初の操作を実行するときの構造体に対する不変クラス型の利点が大きくなり、2 番目の操作を実行するときの露出フィールド構造体の利点が大きくなります。
可変クラス型は、2 番目の操作が支配的で、最初の操作がほとんど必要ないシナリオでは効率的ですが、オブジェクト自体を外部の変更にさらすことなく、オブジェクトが可変クラス オブジェクトの現在の値を公開することは困難な場合があります。
使用パターンによっては、大きな公開フィールド構造が、不透明な構造やクラス型よりもはるかに効率的であることに注意してください。17 バイトを超える構造は、小さい構造よりも効率が悪いことがよくありますが、それでもクラスよりははるかに効率的です。さらに、構造体をref
パラメーターとして渡すコストは、そのサイズに依存しません。大きな構造体は、フィールドではなくプロパティを介してアクセスしたり、不必要に値で渡したりする場合は非効率的ですが、冗長な「コピー」操作を避けるように注意すると、クラスと構造体の損益分岐点がない使用パターンがあります。構造体 - 構造体は単純にパフォーマンスが向上します。
一部の人々は、型がフィールドを公開しているという考えに恐怖を覚えるかもしれませんが、私が説明するような構造体は、それ自体がエンティティとしてではなく、読み取るものの拡張と考えるべきであることをお勧めします。またはそれを書きます。例えば:
public struct SlopeAndIntercept
{
public double Slope,Intercept;
}
public SlopeAndIntercept FindLeastSquaresFit() ...
多数のポイントの最小二乗フィットを実行するコードは、結果のラインの勾配または Y 切片を見つけるためにかなりの量の作業を行う必要があります。両方を見つけても、それ以上の費用はかかりません。メソッドを呼び出すコードはFindLeastSquaresFit
、1 つの変数に勾配を持ち、別の変数に切片を持ちたいと思うでしょう。そのようなコードの場合:
var resultLine = FindLeastSquaresFit();
その結果、2 つの変数が効果的に作成さresultLine.Slope
れresultLine.Intercept
、メソッドが適切と思われるように操作できます。のフィールドは実際にはにも にもresultLine
属していません。を宣言するコードに属します。メソッドが次のように使用された場合と状況は少し異なります。SlopeIntercept
FindLeastSquaresFit
resultLine
double Slope, Intercept;
FindLeastSquaresFit(out Slope, out Intercept);
そのコンテキストでは、関数呼び出しの直後に、2 つの変数にメソッドによって割り当てられた意味があることは明らかですが、それ以外の時点での意味は、メソッドがそれらに対して他に何を行うかによって異なります。前述の構造体のフィールドについても同様です。
透過的な構造ではなく、不変クラスを使用してデータを返す方がよい場合があります。とりわけ、クラスを使用すると、 を返す関数の将来のバージョンで、Foo
追加情報を含む何かを返すことが容易になります。一方、コードが個別のものの特定のセットを処理することを期待する状況が多くあり、そのセットを変更すると、クライアントがそれを処理する必要が根本的に変わります。たとえば、(x,y) ポイントを処理するコードがたくさんある場合、「z」座標を追加するにはそのコードを書き直す必要があり、「ポイント」タイプがそれを緩和するためにできることは何もありません。