4

構造体は不変であることが意図されており、構造体の変更は悪であり、構造体の値を変更する適切な方法は、新しいインスタンスを作成することです。ただし、構造体を変更可能にするのではなく、新しいインスタンスのメモリとパフォーマンスの側面/問題については明確ではありません。

私が構造体を持っているとしましょう、

struct Vehicle
{
    public readonly int Number;
    public readonly double Speed, Direction;

    public Vehicle(int number, double speed, double direction)
    {
        this.Number = number;
        this.Speed = speed;
        this.Direction = direction;
    }
}

そしてそれを次のように作成します:

Vehicle car;

と同様

Vehicle plane = new Vehicle(1234, 145.70, 73.20)

後でNumber、Speed、またはDirectionに割り当てる必要がある場合は、読み取り専用を削除して構造体を可変にすることができます。これを行うのは間違いありません。-これにより、すでに作成された構造体の値を「変更」します。

または、新しい構造体インスタンスを作成することもできます。つまり、car.Speed=120.7と言う代わりに; car = new Vehicle(car.Number、178.55、car.Direction);と言えます。これにより、Speedが変更されただけで、古い値とほぼ同じ新しい構造体値が作成されます。ただし、既存の構造体の値は変更されません。

これが問題です。極端な例として、速度や方向を1秒間に何千回も更新する必要があるとします。これだけ多くのインスタンスを作成すると、メモリとパフォーマンスに深刻な影響を与えると思います。この場合は、構造体を変更可能にする方がよいと思います。

誰かが、このタイプの極端なケースで構造体を実装する適切な方法と比較して、可変構造体のメモリとパフォーマンスの問題を明確にできますか?

4

3 に答える 3

3

あなたが自分で答える必要がある質問は、「私が書いているタイプの意図されたセマンティクスは何ですか?」です。

値型を書き込もうとしているときstructは、aの代わりにAを使用する必要があります。値型の良い例はです。他に何が起こっているかに関係なく、2013年1月12日午後3時33分GMT-7は、常に2013年1月12日午後3時33分GMT-7になります。色は別の良い例です。RGB 255、0、0で構成される2つの色は、決して異なることはありません。classDateTime

作成しようとしているタイプには、車の特定のインスタンスに対して更新されることを意図した状態を含める必要があるため、値のセマンティクスVehicleがありません。したがって、の代わりにミュータブルを作成する必要があります。classstruct

于 2013-01-12T22:37:40.217 に答える
3

構造体には2つの異なる使用例があります。場合によっては、単一の値をカプセル化し、ほとんどがクラスのように動作するが、パフォーマンスが向上し、デフォルト値がnull以外の型が必要になります。そのような場合、私が不透明な構造と呼ぶものを使用する必要があります。構造に関するMSDNのガイドラインは、これが唯一の使用例であるという前提で書かれています。

ただし、他の場合では、構造体の目的は、ダクトテープでいくつかの変数をバインドすることです。そのような場合、それらの変数をパブリックフィールドとして公開するだけの透過的な構造体を使用する必要があります。そのようなタイプについては何も悪いことはありません。悪なのは、すべてがクラスオブジェクトのように動作する必要がある、またはすべてが「カプセル化」されている必要があるという概念です。構造体のセマンティクスが次のようなものである場合:

  1. 状態全体を公開する読み取り可能なメンバー(フィールドまたはプロパティ)の固定セットがいくつかあります
  2. これらのメンバーに必要な値のセットがあれば、それらの値を使用してインスタンスを作成できます(値の組み合わせは禁止されていません)。
  3. 構造体のデフォルト値は、これらすべてのメンバーをそれぞれのタイプのデフォルト値に設定する必要があります。

そして、それらに変更を加えると、それを使用するコードが壊れます。その場合、構造体の将来のバージョンで実行できる可能性のある、透過的な構造体では実行できないことはなく、透過的な構造体で許可される将来のバージョンもありません。構造体のバージョンは防ぐことができるでしょう。その結果、カプセル化は付加価値なしでコストを課します。

実用的な場合は常に、すべての構造体を透明または不透明にするように努めることをお勧めします。さらに、.netが構造体メソッドを処理する方法に欠陥があるため、thisおそらくプロパティセッターを除いて、不透明な構造体のパブリックメンバーを変更しないようにすることをお勧めします。皮肉なことに、MSDNのガイドラインでは、「単一の値」を表さないものには構造体を使用すべきではありませんが、あるコードから別のコードに変数のグループを渡したいだけの一般的なシナリオでは、透明な構造体は、不透明な構造体やクラスタイプよりもはるかに優れており、フィールドの数に応じて優位性のマージンが大きくなります。

ところで、元の質問に関しては、プログラムが(1)車と、(2)特定の車に関連する情報の2種類のことを処理したい場合があることを表すのが役立つことをお勧めします。構造体があり、型のフィールドを保持するCarStateインスタンスがあると便利な場合があることをお勧めします。これにより、のインスタンスが外部コードに状態を公開できるようになり、制御された状況下で外部コードが状態を変更できるようになります(次のようなメソッドを使用)CarCarStateCar

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つ以上のメンバーをカプセル化するものと見なされるように指定する方法があればいいのにと思います[たとえば、RectanglecalledRStringcalledを保持する構造体Nameがフィールド、、、を持ちXY対応WidthするHeight構造体フィールドをエイリアスします。それが可能であれば、タイプが以前に予想されていたよりも多くの状態を保持する必要がある状況を大幅に促進します。現在のCILは、安全なタイプでそのようなエイリアシングを許可しているとは思いませんが、それができなかった概念的な理由はありません。

于 2013-01-12T23:41:01.777 に答える
1

この質問のパフォーマンスの側面にのみ答えます。時期尚早の最適化の警告とセマンティクスに関する議論は割愛します。

はい、構造体全体を割り当てるとパフォーマンスが低下します。ミューティングバリアントには存在しなかった割り当てを紹介します。.NET JITは非常に単純であるため(高速コード生成用に最適化されているため)、JITがそれらを最適化することを信頼していません。

おそらく、これらの割り当てはメモリ内で隣接しており、メモリキャッシュラインがすでにロードされているため、非常に安価です。それらのためにメモリトランザクションを発生させる必要はありません。

パフォーマンスの違いはわずかです(適度なサイズの構造体の場合)。ただし、それを測定することはできます。

また、コードにクラッターを導入して、ミューテーションごとに構造体の新しいインスタンスを作成します。実際に、コード品質の観点から可変構造体を使用するのが最善であることがわかった場合に遭遇しました。それらはほんの数例ですが、存在します。

于 2013-01-12T22:47:35.873 に答える