2012-05-13 4 views
1

私は、その値を失うフィールドを持つStructを持っています。私はフィールドを静的に宣言でき、それが問題を解決します。私はまた、構造体をクラスに変更するだけで(何も変えずに)問題を解決することもできます。なぜ私はこのことが不思議だったのですか?同じクラスのフィールドにない場合、構造体のフィールドがその値を失うのはなぜですか?

+2

コードを表示できますか? – gideon

+1

私はあなたが何をしようとしているのか知っていると思いますが、コードを投稿してください。 –

+1

私はこれがなぜこのような疑念を持っていますが、確認するためにはコードを提示する必要があります。 –

答えて

4

構造体は値渡しされます。つまり、構造体を渡すと、その値のコピーが渡されます。したがって、値のコピーを取り、それを変更すると、オリジナルは変更されません。元のコピーではなくコピーを変更しました。

あなたのコードを見ることなく、私は確信することはできませんが、これは起こっていると思います。

これは、参照によって渡されたクラスでは発生しません。

これは、構造体が不変でなければならないことに言及する価値があります。つまり、いったん作成されると、値が変更されないということです。変更されたバージョンを提供する操作は新しい構造体を返します。


編集:以下のコメントでは、@ supercatは、変更可能なプロパティがより便利になることを示唆しています。しかし、構造体のプロパティセッターは奇妙な失敗を引き起こす可能性があります。構造体の仕組みを深く理解していない限り、あなたを驚かせる例があります。私にとっては、変更可能な構造体を完全に避けるだけの理由があります。

には、次のタイプを考慮してください。

struct Rectangle { 
    public double Left { get; set; } 
} 

class Shape { 
    public Rectangle Bounds { get; private set; } 
} 

[OK]を、今、このコードを想像:

myShape.Bounds.Left = 100; 

おそらく、驚くほど、これは何の効果は全くを持っていません!どうして?さんが長く、まだ同等の形でコードを再書き込みしてみましょう:

var bounds = myShape.Bounds; 
bounds.Left = 100; 

それはBoundsは、ローカル変数にコピーされ、その値がどのように変化するかここを参照する方が簡単です。しかし、どんな時点でも、元の値はShapeに更新されています。

これは、すべてのパブリック構造体を不変にするためのかなり説得力のある証拠です。あなたが何をしているのか知っていれば、変更可能な構造体は便利ですが、個人的には、私はそれらをprivateネストされたクラスとしてその形式で実際に使用します。

@supercatが指摘するように、代替が少し見苦しいです:

myShape.Bounds = new Rectangle(100, myShape.Bounds.Top, 
           myShape.Bounds.Width, myShape.Bounds.Height); 

時にはそれがヘルパーメソッドを追加する方が便利です:

myShape.Bounds = myShape.Bounds.WithLeft(100); 
+0

'this'を変更するプロパティーセッター以外の構造体メソッドは、あなたが宣言した理由で問題があります。しかし、パブリックフィールドが公開されている古い古いデータ構造体は、コンストラクタを介した更新のみを可能にする構造体よりも優れていることがよくあります。 'MyRect.Left + = MyRect.Width;'は 'MyRect = new Rectangle(MyRect.Left + MyRect.Width、MyRect.Top、MyRect.Width、MyRect.Height);よりもはるかに明確であり、さらに高速に実行されます。変更可能な構造体はスレッドセーフな方法で使用できますが、一括置換でしか変更できない構造体は使用できないことにも注意してください。 – supercat

+0

@supercat、私はコメントに収まらない応答で私の答えを編集しました。 –

+0

どの現在のコンパイラが 'myShapeを受け入れるか。Bounds.X = 100'? Mineは "変数ではないため、 'G.Shape.Bounds'の戻り値を変更できません。ヘルパーメソッドに関しては、大量のコードを構造体定義に追加して、ほとんどの場合、 'tempRect = myShape.Bounds;によってより迅速かつ簡単に処理できるものを行います。 tempRect.X = 100; myShape.Bounds = tempRect; ' – supercat

0

構造体は値渡しされる場合には、システム呼び出し側の構造体のコピーが作成され、その内容が表示され、おそらく独自のコピーが変更されますが、呼び出し側のコピーのフィールドには影響しません。 refによって構造体を渡すこともできます。この場合、呼び出し先は構造体の呼び出し側のコピーで作業し、必要に応じて修正し、refによって同様に行うことができる関数に渡すこともできます。呼び出された関数が他の関数が利用可能な構造体の呼び出し側のコピーを作ることができる唯一の方法は、しかし、refことによってそれを渡すことで、すべての機能はそれがrefによって構造体を通過したためにまで呼び出された関数が戻らないことに注意してくださいまた戻った。したがって、呼び出し元は、関数呼び出しの結果として構造体に発生する可能性のある変更は、関数呼び出しが戻るまでに発生していることが保証されます。

この動作は、クラスオブジェクトとは異なります。関数が変更可能なクラスオブジェクトを別の関数に渡す場合、関数の実行が終了した後であっても、他の関数がそのオブジェクトを即座にまたは将来の任意の時点で変更させるかどうかを知る方法がありません。変更可能なオブジェクトが外部コードによって変更されないことを確かめる唯一の方法は、オブジェクトの作成時点から放棄までのオブジェクトの唯一の所有者であることです。

セマンティクスを評価するのに慣れていない人は、まず構造体を値渡しするだけで、呼び出された関数にコピーを渡し、別の構造体格納場所を割り当てることで、構造体、値型が提供する保証は非常に便利です。 Pointが構造体であるので、一つはMyPoints[5].X += 1;ようなステートメント(MyPointsを想定したが配列である)ことを知ることができるMyPoints[5].Xに影響を与えますが、他のPointには影響しません。一つは、さらにいずれかのMyPointsは、別の配列に置き換えられます、または何かがMyPoints[5]に書き込むとMyPoints[5].Xが変わる唯一の方法であることを保証することができます。これとは対照的に、PointはクラスだったとMyPoint[5]が今まで外の世界にさらされていた、上記の文はタイプPointの他の保管場所のフィールド/プロパティXに影響を与えるかどうかを知る唯一の方法は、のすべての単一の保存場所を調べることであろうそれはMyPoints[5]と同じインスタンスを指摘している場合、コード内のどこに存在していたタイプPointまたはObjectを確認します。特定のタイプのすべての格納場所をコードが検査する方法はないので、Point[5]がこれまでに外部に公開されていた場合、そのような保証は不可能です。

構造体には厄介なしわが1つあります。一般的に、対象のコードに構造体への書き込みが許可されている場合は、refで構造体を渡すことができます。ただし、Structメソッド呼び出しおよびプロパティゲッターは、thisrefパラメーターとして受け取りますが、上記の制限はありません。代わりに、構造体のコピーを作成し、refによってメソッドまたはプロパティゲッターに渡してから、構造体のコピーを作成して破棄します。メソッドまたはプロパティのゲッターがthisの突然変異を試みるかどうかを知る方法がないため、このような場合には不平を言うことはありません。単に愚かなコードを生成するだけです。しかし、プロパティーセッター以外のものにthisの突然変異を避けると(読み取り専用構造体ではプロパティーセッターの使用が許可されません)、問題を回避できます。

関連する問題