2012-04-06 5 views
13

主な質問は、有用性と記憶に関してこのキーワードを変更できるようにすることの意味ですか?なぜC#言語仕様でこれが許可されていますか?C#でこのキーワードを割り当てます

その他の質問/サブパートに回答することも、そうしないようにすることもできます。私は彼らの答えが主な質問に対する答えを明確にするのに役立つだろうと思った。

私はC#言語仕様もこれを許可する理由のまわりで私の頭をラップしようとしてきたWhat's the strangest corner case you've seen in C# or .NET?

public struct Teaser 
{ 
    public void Foo() 
    { 
     this = new Teaser(); 
    } 
} 

への答えとしてこれに出くわしました。 サブ部1このは変更可能ですか?それはすべて役に立ちますか?

その答えへのコメントの一つは、C#を経由してCLRから

た:あなたは は別の コンストラクタで構造体のパラメータなしのコンストラクタを呼び出すことができますので、彼らはこれを作った理由があります。構造体の1つの値だけを初期化したい場合は、 は他の値をゼロ/ null(デフォルト)にしたい場合は、publicと書くことができます。 Foo(int bar){this = new Foo(); specialVar = bar;}これは ではなく、実際には正当化されていません(specialVarは2回割り当てられています)が、FYIのちょうど です。私はいないよ

サブ部2:(この()なぜ我々 だけでやるべきではない公共のFoo(int型バー)それは本の中で与えられた理由です、私は知りません)確かに私はその推論に従う。誰かが彼の意味を明確にすることはできますか?多分それがどのように使用されるのかの具体例がありますか?

EDIT(メモリの解放またはガベージコレクションに関しては、スタックまたはヒープのメインポイントは無視されます。int []ではなく、262144のpublic intフィールドで置き換えることができます) また、私の理解から、構造体はこの構造体はそう

public int[] Mb = new int[262144]; 

サブ部3として初期化1 Mbのバイト配列フ​​ィールドを持っていた場合は、ヒープではなく、このfooが呼び出されたときに、これまでスタックから削除されますでしょうか?私にとっては、構造体がスコープ外に出ることはないので、スタックからは削除されません。今夜はテストケースを作成する時間がありませんが、多分私は明日このためにします。

以下のコードで

Teaser t1 = new Teaser(); 
Teaser tPlaceHolder = t1; 
t1.Foo(); 

サブ部4。 t1とtPlaceHolderは同じアドレス空間または異なるアドレス空間を占有していますか?

3歳のポストを持ち上げて申し訳ありませんが、これは本当に私の頭が傷ついています。

FYIの最初の質問stackoverflowだから私は質問に間違って何かを得た場合親切にコメントを投稿し、私は編集します。

2日後、この質問に50の賞金を払うつもりです。私の考えで勝者を選んだとしても、回答には質問を説明するのに妥当な量の作業が必要になると思います。

+3

4つの質問がスタックオーバーフローに適していません。 * –

+4

@AnthonyPegram - このケースでは、これらの質問は構造体が実際にどのように動作するのかという主要な質問に対する副題であるため、受け入れられると思います。 – Polity

+1

私は、すべてを1つにまとめる方法がわかりません。私が主張する主な質問は、このキーワードが変更されるのを許すという側面に何が影響しているかによってまとめられます。質問3-4は私が考えられる副作用です。誰かが私のほかに質問を編集する力を持っていて、彼らがそれをもっとよく聞くことができると思うなら、それを自由にしてください。または、より良い質問を示唆するコメントを残して、最初から始めます。 –

答えて

6

まず、正しい質問をしているかどうかを調べることから始めてください。おそらく、私たちは "なぜC#でなくがstruct内のthisに代入できるのでしょうか?"

リファレンスタイプでthisキーワードを割り当てることは、潜在的に危険です。あなたが実行しているメソッドのオブジェクトへの参照を上書きしています;その参照を初期化しているコンストラクタでもそうすることができます。

値の型でthisキーワードに割り当てられますが、それは一般的に有用ではないので、仕様(またはコンパイラ)によって許可されていません。値の型の割り当てはコピー操作です。各フィールドの値は、割り当ての右から左に再帰的にコピーされます。これはコンストラクタ内であっても、構造に対して完全に安全な操作です。構造の元のコピーがまだ残っていれば、データを変更するだけです。これは、構造体の各フィールドを手動で設定することとまったく同じです。スペックやコンパイラは、明確で安全な操作を禁止するのはなぜですか?

これは、ところで、あなたのサブ質問の1つに答えます。値のタイプの割り当ては、ディープコピー操作であり、参照コピーではありません。あなたのTeaser構造の2つのコピーを割り当てられ、第二のフィールドに最初のフィールドの値をコピーした

Teaser t1 = new Teaser(); 
Teaser tPlaceHolder = t1; 
t1.Foo(); 

:このコードを考えます。これは値型の性質です。同一のフィールドを持つ2つの型は同一であり、「メモリ内」のどこに関係なく、2つの変数が同じである2つのintの変数と同じです。

また、これは重要であり、繰り返す価値があります。「スタック」と「ヒープ」について何が起こっているかを注意深く前提にしてください。値の型は、使用されるコンテキストに応じて、ヒープ上で常に終了します。クローズされていないか、そうでなければスコープから持ち出されない短命(ローカルスコープ)構造体は、スタックに割り当てられる可能性が非常に高い。しかし、それはあなたが気にしたり頼りにしてはいけない重要でないimplementation detailです。キーは、それらが値型であり、そのように動作することです。

thisへの割り当てが本当にどのように役に立つかについては、それほどではありません。特定のユースケースについては既に説明しています。これを使用して、デフォルト値で構造体をほとんど初期化しますが、少数を指定することができます。その向こう

public void SwapValues(MyStruct other) 
{ 
    var temp = other; 
    other = this; 
    this = temp; 
} 

:また、迅速なスワップ操作を実行するために使用することができる

public struct Foo 
{ 
    // Fields etc here. 

    public Foo(int a) 
    { 
    this = new Foo(); 
    this.a = a; 
    } 
} 

:あなたは、コンストラクタが戻る前に、すべてのフィールドを設定する必要があるので、これは冗長なコードの多くを保存することができますその言語のちょうど面白い副作用と、構造や値の型が実装されていることを、あなたが知る必要はほとんどないでしょう。

+0

+1が終了する前に試行します。私は明日のいつかこの質問に言い直しをしようとします。 –

+0

私はこれを投票することにしました。これは答えであり、新しいものを再開しません。値によってコピーされたものが最終的に沈んだのです。私はいつも "this"を参照として考えていて、構造体を扱う際には値型を参照する "this"という言葉を考えていませんでした。私は、私が作った構造体の中でこのキーワードを使ったとは思わない。 –

1

この割り当てを持つと、構造体の「高度な」コーナーケースが可能になります。それは不変である構造体のデフォルトの「希望」自然に違反するので、私は強く、この用途に対して主張するだろう

struct Foo 
{ 
    void Swap(ref Foo other) 
    { 
     Foo temp = this; 
     this = other; 
     other = temp; 
    } 
} 

:私が見つけた一つの例では、スワップ方法でした。このオプションを使用する理由は、間違いなく不明です。

今、structs自体については、

  • これらは管理されたヒープではなくスタック上に存在することができます。
  • アンマネージコードにマーシャリングすることができます。
  • NULL値に割り当てることはできません。

完全な概要については、以下を参照してください。あなたの質問への相対

http://www.jaggersoft.com/pubs/StructsVsClasses.htmは、あなたの構造体は、スタックやヒープ上に住んでいるかどうかです。これは構造体の割り当て場所によって決まります。構造体がクラスのメンバであれば、それはヒープ上に割り当てられます。それ以外の場合は、構造体が直接割り当てられている場合は、ヒープに割り当てられます(実際はこれはピクチャの一部です)。この全体は、C#2.0で導入されたクロージャについて話し始めてからかなり複雑になりますが、あなたの質問に答えてください)。

.NETの配列は、デフォルトでヒープに割り当てられます(この動作は、安全でないコードとstackallocキーワードを使用すると一貫しません)。上記の説明に戻ると、structインスタンスもヒープに割り当てられることを示します。実際に、これを証明する簡単な方法は、1 MBのサイズの配列を割り当て、NO stackoverflow例外がスローされることを観察することです。

スタック上のインスタンスの有効期間は、その有効範囲によって決まります。これは、ガベージコレクタによってライフタイムが決定されるマネージャヒープのインスタンスとは異なります(また、そのインスタンスに対する参照がまだ存在するかどうか)。範囲内にある限り、スタック上のすべてのものが確実に存続するようにすることができます。スタックにインスタンスを割り当ててメソッドを呼び出すと、そのインスタンスがスコープから外れるまで(デフォルトでは、そのインスタンスが宣言されたメソッドが終了したとき)、そのインスタンスの割り当てを解除することはありません。

struct cantは、管理された参照を管理しています(ポインタはアンマネージコードでも使用できます)。 C#でスタック上の構造体を操作する場合、基本的に参照ではなくインスタンスに向かってラベルを付けます。構造体を別の構造体に代入すると、単純に基礎となるデータがコピーされます。参照は構造体として参照できます。単純に言えば、参照はメモリ内の特定の部分へのポインタを含む構造体にすぎません。一方の参照を他方の参照に割り当てると、ポインタデータがコピーされます。

// declare 2 references to instances on the managed heap 
var c1 = new MyClass(); 
var c2 = new MyClass(); 

// declare 2 labels to instances on the stack 
var s1 = new MyStruct(); 
var s2 = new MyStruct(); 

c1 = c2; // copies the reference data which is the pointer internally, c1 and c2 both point to the same instance 
s1 = s2; // copies the data which is the struct internally, c1 and c2 both point to their own instance with the same data 
+0

私はそれがスタックにコピーされているかどうかにかかわらず推測します。ヒープはFooを呼び出すと、GCは最終的に元の構造体に関連付けられたメモリをクリーンアップしますか?私は厳密に管理されたコードについて話しています。 Ohh and Foo私はTeaser構造体のFoo構造体をあなたのFoo構造体ではないという意味でした。 –

+0

GCは、管理対象ヒープに割り当てられたインスタンスのみをクリーンアップします。スタックベースのインスタンスは実際には実際にはクリーンアップされません(したがって、構造体にはデストラクタが含まれていません)。それらのラベルは、範囲外になると無効になります。この記事のeric lippertの答えを参照してください:http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope – Polity

+0

structは値型です。明示的にガベージコレクションされることはありません。値型がスコープを離れると、値型のメモリがOSに返されます。それがローカル変数またはパラメータの場合、これが起こり、関数が戻ります。フィールドの場合、コンテナクラスがGCになったときに発生します。等 –

関連する問題