2009-07-27 11 views
3

C#では、宣言された変数を宣言して初期化するのではなく、以前に宣言した変数を再初期化する利点または欠点がありますか?変数を再初期化するか、新規に宣言しますか?

string str; 
for (int i = 0; i < 100; i++) 
{ 
    str = "This is string #" + i.ToString(); // Re-initialize the "str" variable. 
    Console.WriteLine(str); 
} 

DataColumn col1 = new DataColumn(); 
col1.ColumnName = "Subsite"; 
gridDataTable.Columns.Add(col1); 

DataColumn col2 = new DataColumn(); // Declare a new variable instead. 
col2.ColumnName = "Library"; 
gridDataTable.Columns.Add(col2); 

ループを伴う同様の例VS

DataColumn col = new DataColumn(); 
col.ColumnName = "Subsite"; 
dataTable.Columns.Add(col); 

col = new DataColumn(); // Re-use the "col" declaration. 
col.ColumnName = "Library"; 
dataTable.Columns.Add(col); 

たとえば

(簡潔およびヒト可読性に思考を無視して)、これらの二つのサンプルを比較します対

for (int i = 0; i < 100; i++) 
{ 
    string str = "This is string #" + i.ToString(); // Declare a new "str" each iteration. 
    Console.WriteLine(str); 
} 

編集:これまでの回答はありがとうございます。それらを読んだ後、私は少し私の質問に拡大すると思った:

私が間違っている場合は私を修正してください。

System.Stringのような参照型を宣言して初期化すると、スタックに存在するそのオブジェクトへのポインタと、ヒープ上に存在するオブジェクトの内容(ポインタでのみアクセス可能)があります。

最初のループの例では、1つのポインタstrを作成して、ヒープ上に存在するStringクラスの100個のインスタンスを作成するようです。私の考えでは、ループを繰り返しながら、 "str"ポインタを変更してStringクラスの新しいインスタンスを指すようにするだけです。それらへのポインタをもはや持っていないそれらの "古い"文字列はガベージコレクションされます - 私はそれがいつ発生するかわかりませんが。

2番目のループの例では、Stringクラスの100個のインスタンスを作成するだけでなく、100個のポインタを作成するようです。

私は、もはや必要ではなくなったスタック上のアイテムに何が起こるのか分かりません。私はガベージコレクターがこれらのアイテムも取り除いたとは思わなかった。おそらく、スコープを終了するとすぐにスタックから削除されますか?たとえそれが本当であっても、ポインタが1つしか作成されず、それが指し示すものを更新する方が、100の異なるポインタを作成するより効率的で、それぞれが一意のインスタンスを指していると思います。

"早すぎる最適化は悪い"という議論があると私は理解していますが、私はプログラムの最適化ではなく、より深い理解を得ようとしています。

+0

"人間の読みやすさに対する考えを無視する" - 第2の最も重要な要素(最初の "正しさ")を無視することは、おそらく決定を下す悪い方法です。 –

+0

@エリック:「このようにする、読みやすくする」のような回答を断るためにのみ、その文章を追加しました。もちろん、私はコード読み取り可能なパラマウントを作ることを検討します。私は、パフォーマンスの違いがあるかのように、より技術的な答えを探していました。 –

答えて

6

あなたの2番目の例ははるかに明確な答えがあり、2番目の例はより良い例です。その理由は、変数strはforブロック内でのみ使用されるからです。 forブロックの外側に変数を宣言すると、別のコードがこの変数に誤ってバインドされ、アプリケーションにバグが発生する可能性があります。偶発的な使用を防ぐために、可能な最も具体的なスコープ内のすべての変数を宣言する必要があります。

最初のサンプルでは、​​それはより重要な問題だと思います。私にとっては、新しい変数を作成することを選択しました。なぜなら、各変数は単一の目的を持つべきだと思うからです。私が変数を再利用しているのであれば、通常は私のメソッドをリファクタリングする必要があるということです。

0

中括弧が終わった後にオブジェクト自体を破壊する「使用する」という言葉はどうですか? 私は確信していませんが、それは私の考えです。あなたの意見も知りたいです。

2番目の例では、常に2番目の例を使用していますが、わかりませんが、たとえばACM-ICPCコンテストのいくつかの問題では、再初期化を忘れるためにバグを紛失していました。私はこの方法で使った。

+1

これは、stringまたはDataColumnではなく、IDiposableを実装するクラスにのみ適用されます。 –

1

これは早すぎる最適化のための情報を提供するように設計された質問のように疑わしいと思います。どちらのシナリオもソフトウェアの99.9%で重要な点で異なるとは思っていません。メモリが作成され、いずれかの方法で使用されています。唯一の違いは、変数参照です。

利点や欠点があるかどうかを確認するには、アセンブリのサイズや性能に本当に気を付ける必要があります。サイズ要件を満たせない場合は、2つの選択肢の間のアセンブリサイズの違いを測定します(ただし、他の領域で利益を上げる可能性は高いです)。パフォーマンス要件を満たすことができない場合は、プロファイラを使用してコードのどの部分が遅すぎるかを確認します。

1

2つの別個のオブジェクトを作成しているので、同じ宣言された名前を使用するかどうかは問わないが、主に読みやすい問題です。あなたは本当に単一の焦点を念頭に置いてオブジェクトや変数を作成する必要があります。それはあなたの人生を楽にします。

2番目の例では、初期化の唯一の大きな違いは、文字列を "for"ループの範囲外に置くことによって、より外界の影響にさらされていることです。ループの内部または外部を宣言するためのメモリまたはスピードの利点はありません。文字列変数を変更するたびに、基本的に新しい文字列が作成されます。ですから、例えば:

string test1 = "new string"; 
string test2 = "and now I am reusing the string"; 

はこれを回避するには、あなたはせずに文字列を変更することができますStringBuilderクラスを、使用します。

string test = "new string"; 
test = "and now I am reusing the string"; 

は次のように、2つの別個の文字列の作成と同じです新しい文字列を作成します。特に、ループ内で文字列が大きく変更される状況で使用する必要があります。

0

ほとんどの場合、いいえ、違いはありません。主な違いは、ローカル変数(クラスの場合は "ポインタ")がスタックに格納されていることです。関数が何らかの理由で再帰的で、1つではなく2つのローカル変数を持つと、深い再帰関数でスタック空間をより速く使い果たすことができます。どちらの場合でも限界に近づくことは、おそらく非再帰的な方法を使用すべきであるという兆候です。

また、ちょうどそれを言及するために、あなたは完全に変数をスキップして書くことができます:

dataTable.Columns.Add(new DataColumn() { ColumnName = "Subsite" }); 
dataTable.Columns.Add(new DataColumn() { ColumnName = "Library" }); 

どの私は性能面は2つのローカル変数を持つようになると信じて、私はそこに間違っている可能性があります。私はILコードで何が生成されたかを正確に覚えていません。

+0

ローカル変数は必然的にスタックに格納されません。ジッタは、地方自治体が安全であると確信している場合、地方自治体を登録することが許可されています。ジッタとC#コンパイラの両方で、異なる変数に対してスタックスロットを再利用することも可能です(信頼性が高い場合)。 –

関連する問題