.net の構造は、通常、「透明」と「不透明」と呼ぶ 2 つのスタイルのいずれかに準拠する必要があります。MSDN のガイドラインは、すべてがクラス オブジェクトのように振る舞うと考えている人によって書かれたようであり、クラス オブジェクトのように振る舞わないことは欠陥であることに注意してください。その結果、透明な構造はそれ自体で有用なセマンティクスを持つことができ、多くの場合、不透明な構造を非効率にする要因がクラスをさらに非効率にする可能性があるという事実を無視しています。
透過的な構造体は、すべてのフィールドが公開されている構造体であり、構造体の状態は、フィールド内の値の組み合わせにすぎません。透明な構造体は、そのフィールドの値に基づいて計算される読み取り専用の「便利なプロパティ」を持つことができます (たとえば、Vector3d
フィールド DX、DY、および DZ を持つ透明な構造体は、Length
それらのフィールドに基づいて計算されるプロパティを持つ可能性があります)。そのようなプロパティは構造体の状態の一部を形成するのではなく、フィールドで計算を実行するための便利な省略表現であることを明確にしてください。たとえば、vec.Length
は の代わりになりSomeLibrary.Distance3d(vec.DX, vec.DY, vec.DZ)
ます。
透過構造型の変数は、フィールドごとに 1 つの変数を割り当てます。必要に応じて、各フィールドに個別の変数としてアクセスできます。透明な構造型を値で渡すと、すべてのフィールドを個別に渡すのとほぼ同じコストがかかります (場合によっては、より効率的である場合もあれば、より少ない場合もあります)。透過的な構造体型ref
を渡すには、フィールドの数に関係なく、オブジェクト参照を渡すのと同じ固定コストがかかります。
多くの場合、現在のバージョンで次の基準がすべて満たされ、考えられるすべての将来のバージョンでも満たされる場合、構造体は透過的である必要があります [つまり、基準を満たさなかった構造体は、互換性のある代替品とは見なされません]。
- 状態全体を公開する読み取り可能なメンバー (フィールドまたはプロパティ) の固定セットがあります。
- これらのメンバーに必要な値のセットがあれば、それらの値を使用してインスタンスを作成できます (値の組み合わせは禁止されていません)。
- 構造体のデフォルト値は、これらすべてのメンバーをそれぞれの型のデフォルト値に設定する必要があります。
構造体が上記の基準を満たしている場合、そのフィールドをパブリックに公開しても、他の方法では実行できない操作を外部コードで実行することはできません。それ以外の場合は、代わりにアトミック プリミティブを使用して安全にロックを実行する必要があります。
もちろん、すべての構造が上記の基準を満たしているわけではありませんが、基準を満たしている構造を透明にすると、多くの場合 (場合によっては大幅に) パフォーマンスを向上させることができます。実際、構造体がクラスよりわずかに高速であると見なされることが多い理由の多くは、構造体を不必要に不透明にすることでパフォーマンスが低下するためです。
不透明な構造とは、その状態がそのフィールドの値以外のものを意味的に表すものです。たとえばDecimal
、大きさが 1234 で指数が 2 の は、数値量 12.34 を表す場合があります。全体の新しい値を計算しない限り、不透明な構造の 1 つの側面だけを変更することはできません。不透明な構造体は、そのフィールドに不変条件を強制しようとする場合がありますが、構造体の代入が機能する方法により、多くの場合、スレッドを悪用する (ただし、特別な実行権限を持たない) コードが悪用される可能性があることに注意することが重要です。構造体の不変条件に違反する構造体インスタンスを生成するスレッド。
Microsoft のガイドラインは、不透明な構造に適用すると非常に優れています。このような構造では、通常、プロパティ セッターの公開を控える必要があります。型は、その規則からの逸脱を表していません Point
。代わりに、それらは、そもそも不透明な構造であってはならない型を表します。つまり、透明な構造であるべきです。プロパティセッターを公開する不透明な構造が、他の手段では達成できないセマンティクスを提供できる場合がありますが、そのような構造には、一般に、同じ目的を達成する他の手段よりも望ましくないいくつかの厄介な落とし穴があります。Rectangle
提案された構造に関しては、3つのフィールドを持つ透明な構造にし、MedianPrice
呼び出されるたびにこれらの3つのフィールドの中央値を計算するプロパティにするか、4つのフィールドを持つ不透明な構造にすることをお勧めします。コンストラクターの MedianPrice。私の決定は、読書の相対的な頻度に基づいていますMedianPrice
、他のコードを変更せずに価格の 1 つを変更したいコードの相対頻度と比較します。前者の方がはるかに多い場合は、不透明な構造体です。後者の場合、透明な構造体。透明な構造体の使用を開始し、それを利用するようにコードが記述されている場合、不透明な構造体を処理するようにコードを適応させるのは難しい場合があることに注意してください。対照的に、不透明な構造体で動作するように記述されたコードは、透過的な構造体でも動作しますが、それ以外の場合ほど効率的ではありません。
透明な構造体と他のタイプとの効率の違いは、構造体のサイズとともに大きくなることに注意してください。たとえば、多くの 3D 三角形の座標を保存する必要があるとします。Triangle3d
タイプの 3 つのフィールド (それぞれが 3 つの数値 X、Y、Z を保持する) を含む透明な構造体を定義し、Point3d
すべての三角形をTriangle3d[]
. このような定義があれば、任意の三角形の 1 点の 1 つの座標を、他の点に触れたり読み取ったりすることなく、簡単に変更できます。対照的に、不透明な構造または不変のクラス型を使用した場合、単一のプロパティを変更するだけでも、古いインスタンスから新しいインスタンスにすべての情報をコピーする必要があります。型に含まれるフィールドが多いほど、不透明または不変型を使用するコストが高くなります。変更可能なクラス型を使用した場合、変更は安価になりますが、前述の配列を保持するメソッドで、基になるストレージ オブジェクトを公開せずに三角形の座標を呼び出し元が利用できるようにする良い方法はありません。すべてのフィールド値を新しい不変オブジェクトにコピーします。 したがって、Microsoft のガイドラインに反して、多くの使用パターンでは、型に含まれるフィールドが多いほど、クラスではなく透明な構造体である利点が大きくなります。
ちなみに、一部の人々は、公開フィールド構造体がカプセル化に違反していると主張するでしょう。私は反対のことを主張します。のような型は、 type のフィールドと、 typeの別のKeyValuePair<TKey,TValue>
フィールドを保持することになっています。2 つのフィールドは自由に読み取ることができるため、それぞれの型に有効な値の任意の組み合わせを割り当てることができ、その型の将来のバージョンが実行する可能性があり、透過的な構造体が実行しないと考えられる有用なものは実際には何もありません。いわゆる「カプセル化」は、ゼロ値を追加しながらコードの効率を低下させるだけです 。TKey
TValue