C# が foreach で変数を再利用する理由はありますか? 質問する

C# が foreach で変数を再利用する理由はありますか? 質問する

C# でラムダ式や匿名メソッドを使用する場合は、変更されたクロージャへのアクセスの落とし穴に注意する必要があります。例:

foreach (var s in strings)
{
   query = query.Where(i => i.Prop == s); // access to modified closure
   ...
}

修正されたクロージャにより、上記のコードでは、Whereクエリのすべての句が の最終値に基づくようになりますs

説明したようにこここれは、上記のループsで宣言された変数foreachがコンパイラで次のように変換されるために発生します。

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}

代わりに次のようになります:

while (enumerator.MoveNext())
{
   string s;
   s = enumerator.Current;
   ...
}

指摘したようにここただし、ループ外で変数を宣言してもパフォーマンス上の利点はなく、通常の状況では、これを行う唯一の理由は、ループのスコープ外で変数を使用する予定がある場合です。

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}
var finalString = s;

ただし、ループ内で定義された変数はforeachループ外で使用することはできません。

foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.

そのため、コンパイラは、目に見える利点を生み出さずに、見つけてデバッグするのが難しいエラーが発生しやすい方法で変数を宣言します。

この方法では、ループを内部スコープ変数でコンパイルした場合にはできない操作を実行できるのでしょうかforeach。それとも、これは匿名メソッドとラムダ式が利用可能または一般的になる前に行われた恣意的な選択であり、それ以降変更されていないのでしょうか。

ベストアンサー1

コンパイラは、エラーが発生しやすくなり、見つけてデバッグするのが難しくなることが多い方法で変数を宣言しますが、目に見える利点はまったくありません。

あなたの批判は完全に正当です。

この問題についてはここで詳しく説明します。

ループ変数を閉じることは有害であると考えられる

この方法で foreach ループを使用すると、内部スコープ変数を使用してコンパイルした場合にはできないことが可能になりますか? それとも、これは匿名メソッドとラムダ式が利用可能または一般的になる前に行われた恣意的な選択であり、それ以降変更されていないのでしょうか?

後者です。C# 1.0 仕様では、ループ変数がループ本体の内側にあるか外側にあるかは実際には明記されていませんでした。目に見える違いがないためです。C# 2.0 でクロージャ セマンティクスが導入されたとき、"for" ループと一致するように、ループ変数をループの外側に配置することが選択されました。

誰もがその決定を後悔していると言っても過言ではないと思います。これは C# における最悪の「落とし穴」の 1 つであり、私たちはこれを修正するために破壊的変更を行う予定です。C # 5 では、foreach ループ変数は論理的にループ本体内に配置され、したがってクロージャは毎回新しいコピーを取得します。

ループforは変更されず、変更は以前のバージョンの C# に「バックポート」されません。したがって、このイディオムを使用する場合は引き続き注意する必要があります。

おすすめ記事