C# に関する興味深い問題に遭遇しました。以下のようなコードがあります。
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(() => variable * 2);
++ variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
0、2、4、6、8 が出力されると予想します。しかし、実際には 5 つの 10 が出力されます。
これは、すべてのアクションが 1 つのキャプチャされた変数を参照していることが原因であると思われます。その結果、アクションが呼び出されると、すべて同じ出力になります。
この制限を回避して、各アクション インスタンスに独自のキャプチャ変数を持たせる方法はありますか?
ベストアンサー1
はい - ループ内の変数のコピーを取得します。
while (variable < 5)
{
int copy = variable;
actions.Add(() => copy * 2);
++ variable;
}
C# コンパイラは、変数宣言に到達するたびに「新しい」ローカル変数を作成すると考えることができます。実際には、適切な新しいクロージャ オブジェクトが作成され、複数のスコープ内の変数を参照する場合は複雑になります (実装の点で) が、動作します :)
この問題がより一般的に発生するのは、for
またはを使用する場合であることに注意してくださいforeach
。
for (int i=0; i < 10; i++) // Just one variable
foreach (string x in foo) // And again, despite how it reads out loud
詳細については、C# 3.0仕様のセクション7.14.4.2を参照してください。閉鎖に関する記事他にも例があります。
C# 5 コンパイラ以降では (C# の以前のバージョンを指定する場合でも) の動作がforeach
変更されたため、ローカル コピーを作成する必要がなくなりました。この答え詳細については。