web-dev-qa-db-ja.com

C#構造体が不変なのはなぜですか?

構造体、文字列などが不変である理由を知りたくてたまらなかったのですか?それらを不変にし、残りのオブジェクトを変更可能にする理由は何ですか。オブジェクトを不変にすると見なされるものは何ですか?

可変オブジェクトと不変オブジェクトのメモリの割り当て方法と割り当て解除方法に違いはありますか?

57
Sandeep

この主題に興味がある場合は、不変プログラミングに関する記事を http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/ に多数掲載しています。

構造体、文字列などが不変である理由を知りたくてたまらなかったのですか?

構造体とクラスはデフォルトでは不変ではありませんが、構造体を不変にすることをお勧めします。不変クラスも好きです。

文字列は不変です。

それらを不変にし、残りのオブジェクトを変更可能にする理由は何ですか。

すべてのタイプを不変にする理由:

  • 変更されないオブジェクトについて推論する方が簡単です。キューに3つのアイテムが含まれている場合、現在は空ではなく、5分前には空でなかったことがわかっているので、将来的に空になることはありません。それは不変です!それについての事実を知ったら、その事実を永遠に使うことができます。不変オブジェクトに関する事実は古くなりません。

  • 最初のポイントの特別なケース:不変オブジェクトはスレッドセーフにするのがはるかに簡単です。ほとんどのスレッドセーフティの問題は、あるスレッドでの書き込みと別のスレッドでの読み取りが原因です。不変オブジェクトには書き込みがありません。

  • 不変オブジェクトは分解して再利用できます。たとえば、不変のバイナリツリーがある場合、その左側と右側のサブツリーをdifferentツリーのサブツリーとして使用できます。可変構造では、1つの論理オブジェクトに変更を加えて別の論理オブジェクトに影響を与えたくないため、通常、データのコピーを作成して再利用します。これにより、時間とメモリのロットを節約できます。

構造体を不変にする理由

構造体を不変にする理由はたくさんあります。 1つだけです。

構造体は参照ではなく値でコピーされます。構造体を参照によってコピーされているものとして誤って処理することは簡単です。例えば:

void M()
{
    S s = whatever;
    ... lots of code ...
    s.Mutate();
    ... lots more code ...
    Console.WriteLine(s.Foo);
    ...
}

次に、そのコードの一部をヘルパーメソッドにリファクタリングする必要があります。

void Helper(S s)
{
    ... lots of code ...
    s.Mutate();
    ... lots more code ...
}

違う!それは(ref S s)でなければなりません-そうしないと、sのcopyで変更が発生します。そもそも突然変異を許さないのであれば、このような問題はすべてなくなります。

文字列を不変にする理由

不変構造が事実のままであるという事実に関する私の最初のポイントを覚えていますか?

文字列が変更可能だったとします:

public static File OpenFile(string filename)
{
    if (!HasPermission(filename)) throw new SecurityException();
    return InternalOpenFile(filename);
}

敵対的な呼び出し元がファイル名にセキュリティチェックを変更し、beforeを変更した場合ファイルが開かれていますか?コードは、権限のないファイルを開いただけです!

繰り返しになりますが、変更可能なデータについては推論するのが困難です。 「この呼び出し元は、この文字列で記述されたファイルを表示することを許可されている」という事実をではなく(== --- ==)foreverにする必要があります突然変異が起こるまで。可変文字列を使用する場合、安全なコードを作成するには、変更されないことがわかっているデータのコピーを常に作成する必要があります。

オブジェクトを不変にすると見なされるものは何ですか?

型は論理的に「永遠」な値を表しますか?数値12は数値12です。変化しません。整数は不変でなければなりません。点(10、30)は点(10、30)です。変化しません。ポイントは不変である必要があります。文字列「abc」は文字列「abc」です。変化しません。文字列は不変である必要があります。リスト(10、20、30)は変更されません。等々。

タイプは、変化することを表す場合があります。メアリースミスの姓はスミスですが、明日はメアリージョーンズになるかもしれません。または、今日のミススミスは明日ドクタースミスになるかもしれません。エイリアンは現在50のヘルスポイントを持っていますが、レーザービームに当たった後は10を持っています。突然変異として最もよく表されるものがあります。

可変オブジェクトと不変オブジェクトのメモリの割り当てと割り当て解除の方法に違いはありますか?

そうではありません。ただし、前述したように、不変値の良い点の1つは、コピーを作成せずに値の一部を再利用できることです。その意味で、メモリ割り当ては非常に異なる場合があります。

115
Eric Lippert

構造体ではない...だからこそ、可変構造体は悪だ。

変更可能な構造体を作成すると、アプリケーションであらゆる種類の奇妙な動作が発生する可能性があるため、非常に悪い考えと見なされます(参照型のように見えますが、実際には値型であり、渡すたびにコピーされます。それらの周り)。

一方、文字列はそうです。これにより、本質的にスレッドセーフになるだけでなく、文字列のインターンによる最適化も可能になります。複雑な文字列をその場で作成する必要がある場合は、StringBuilderを使用できます。

9
Justin Niessner

可変性と不変性の概念は、構造体とクラスに適用すると、異なる意味を持ちます。可変クラスの重要な側面(多くの場合、重要な弱点)は、FooList<Integer>タイプのフィールドBarがあり、これが(1,2 、3)、同じリストへの参照を持つ他のコードは、それを変更する可能性があります。たとえば、Barは(4,5,6)、を含むリストへの参照を保持します。その他のコードがBarにまったくアクセスできない場合でも。対照的に、FooBizタイプSystem.Drawing.Pointのフィールドがある場合、Bizのあらゆる側面を変更できる唯一の方法はそのフィールドへの書き込みアクセス権を持ちます

構造体のフィールド(パブリックおよびプライベート)は、構造体が格納されている格納場所を変更できるコードによって変更でき、格納されている格納場所を変更できないコードによって変更することはできません。構造体にカプセル化されたすべての情報がそのフィールドに保持されている場合、そのような構造体は、不変型の制御を可変型の利便性と効果的に組み合わせることができます。残念ながら、一部のMicrosoftプログラマーはこれを推奨しています。

構造体の「問題」は、メソッド(プロパティの実装を含む)が読み取り専用コンテキスト(または不変の場所)の構造体で呼び出されると、システムが構造体をコピーし、一時コピーでメソッドを実行し、サイレントに実行することです。結果を破棄します。この動作により、プログラマは、メソッドの変更に関する問題を回避する方法は、プロパティを単純に置き換えるだけで問題を回避できた場合に、区分的な更新を許可しないことであるという不幸な考えを提起しました。公開フィールド

ちなみに、クラスプロパティが便利に変更可能な構造体を返す場合、構造体への変更は元のクラスには影響を及ぼさないと不平を言う人もいます。私はそれが良いことだと思います-返されたアイテムが構造体であるという事実は動作を明確にします(特にそれが公開フィールド構造体である場合)。 Drawing.Matrixの架空の構造体とプロパティを使用するスニペットを、Microsoftによって実装されているそのクラスの実際のプロパティを使用するスニペットと比較します。

 //架空の構造体
公開構造体{
 public float xx、xy、yx、yy、dx、dy; 
} Transform2d; 
 
 // "System.Drawing.Drawing2d.Matrix"の仮説プロパティ
 public Transform2d Transform {get;} 
 
 // "System.Drawing。 Drawing2d.Matrix "
 public float [] Elements {get; } 
 
 //架空の構造体を使用したコード
 Transform2d myTransform = myMatrix.Transform; 
 myTransform.dx + = 20; 
 ...その他myTransform 
 
を使用したコード//実際のMicrosoftプロパティを使用したコード
 float [] myArray = myMatrix.Elements; 
 myArray [4] + = 20; 
 ... myArray 
を使用する他のコード

実際のMicrosoftプロパティを見て、myArray[4]への書き込みがmyMatrixに影響するかどうかを確認する方法はありますか?ページを見ても http://msdn.Microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx 確認する方法はありますか?プロパティが構造体ベースの同等のものを使用して記述されていた場合、混乱はありません。構造体を返すプロパティは、6つの数値の現在の値以上のものを返しません。 myTransform.dxの変更は、他の何にも関連付けられていない浮動小数点変数への書き込みにすぎません。 myTransform.dxの変更がmyMatrixに影響を与えないという事実を気に入らない人は、myArray[4]の記述がmyMatrixにも影響を与えないことを同様に苛立たせる必要があります。 myMatrixmyTransformの独立性は明らかですが、myMatrixmyArrayの独立性は明らかではありません。

4
supercat

構造体型は不変ではありません。はい、文字列です。独自の型を不変にするのは簡単です。デフォルトのコンストラクタを提供せず、すべてのフィールドをプライベートにし、フィールドの値を変更するメソッドやプロパティを定義しないでください。オブジェクトを変更するメソッドが代わりに新しいオブジェクトを返すようにします。メモリ管理の角度があり、多くのコピーやゴミを作成する傾向があります。

2
Hans Passant

構造体は変更可能である可能性がありますが、コピーセマンティクスを持っているため、これは悪い考えです。構造体に変更を加えた場合、実際にはコピーを変更している可能性があります。変更された内容を正確に追跡することは非常に困難です。

可変構造体は間違いを生みます。

1
Craig Gidney