これを行う必要があるのは(ローカルに割り当てる)唯一の時間は、foreachループまたはLinqを使用している場合です。そうしないと、変更されたクロージャの問題が発生します。
MSDNブログのスニペットです(以下の内容はすべてリンクからのものです)。
http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
しかし、私は先に自分の取得しています。この断片の出力は?
var values = new List<int>() { 100, 110, 120 };
var funcs = new List<Func<int>>();
foreach(var v in values)
funcs.Add(()=>v);
foreach(var f in funcs)
Console.WriteLine(f());
ほとんどの人は100/110/120と予想しています。実際には120/120/120です。なぜですか?
()=> vは、「代理人の作成時に戻った値を返す」ではなく、「変数vの現在の値を返す」ことを意味するためです。クロージャーは、値を超えてではなく、変数を閉じます。メソッドが実行されるとき、vに割り当てられた最後の値は120だったので、それでもその値を持ちます。
これは非常に混乱しています。コードを書き込む正しい方法は次のとおりです。
foreach(var v in values)
{
var v2 = v;
funcs.Add(()=>v2);
}
ここで何が起こりますか?ループ本体を再起動するたびに、新しい変数v2を論理的に作成します。各クロージャは別のv2で閉じられます。これは1回のみ割り当てられます。したがって、常に正しい値が保持されます。
我々は拡張が
try
{
while(e.MoveNext())
{
int m; // INSIDE
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
た予想通り、コードが振る舞うだろうと指定した場合は、foreachループが
{
IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
try
{
int m; // OUTSIDE THE ACTUAL LOOP
while(e.MoveNext())
{
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
}
finally
{
if (e != null) ((IDisposable)e).Dispose();
}
}
ためのシンタックスシュガーであることを指定するために基本的には、問題が発生します。
変数が他の場所で使用されないと仮定すると、私が見ることができる最初のケースの唯一の理由はデバッグを支援することです。 – ChrisF