これは、C#5.0では正常に機能します(期待どおりの意味です)。
var actions = new List<Action>();
foreach (var i in Enumerable.Range(0, 10))
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var act in actions) act();
0から9を出力します。ただし、これは10を10回表示します。
var actions = new List<Action>();
for (var i = 0; i < 10; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var act in actions) act();
質問:これは、5.0より前のC#バージョンで発生した問題でした。そのため、クロージャーにループローカルプレースホルダーを使用する必要がありましたが、C#5.0では「foreach」ループで修正されました。しかし、「for」ループではありません!
この背後にある理由は何ですか(for
ループの問題も修正されていません)?
この背後にある理由は何ですか?
「なぜfor
ループでも変更されなかったのですか?」という意味だと思います。
答えは、for
ループの場合、既存の動作は完全に理にかなっているということです。 for
ループを次のように分割した場合:
...ループは大まかに:
{
initializer;
while (condition)
{
body;
iterator;
}
}
(もちろん、iterator
がcontinue;
ステートメントの最後でも実行されることを除いて。)
初期化部分論理的には1回だけ発生するため、「変数のインスタンス化」が1つしかないことは完全に論理的です。さらに、ループの各反復で変数の自然な「初期」値はありません-for
ループhasが変数を宣言する形式であることは言うまでもありませんイニシャライザー、条件でテストし、イテレーターで変更します。このようなループで何ができると思いますか?
for (int i = 0, j = 10; i < j; i++)
{
if (someCondition)
{
j++;
}
actions.Add(() => Console.WriteLine(i, j));
}
これを、反復ごとに個別の変数を宣言しているようにlooksであるforeach
ループと比較してください。ちなみに、変数は読み取り専用であり、偶数になりますmore反復ごとに変化する1つの変数であると考えると奇妙です。 foreach
ループを、イテレーターから取得した値を使用して、各反復で新しい読み取り専用変数を宣言するものと考えるのは完全に理にかなっています。