コールバックなど、なんらかのクロージャを使用する処理があったとして:
using System; using System.Collections.Generic;
... private List<Action> callbacks = new List<Action>(); void AddCallback(Action callback) { callbacks.Add(callback); } void RunCallbacks() { callbacks.ForEach((callback) => callback()); }
|
C#でforeach
ループで用いる要素の変数を束縛すると、値はすべてループが終わったときの値になってしまう:
int[] table = {1, 2, 3};
foreach (var x in table) { AddCallback(() => { Debug.Log(x); }); } RunCallbacks();
|
まあよく考えると当然で、ループの変数はループ本体で共有する1つの変数であって、それをキャプチャしているのでそうなるのも頷ける:
int x; for (int i = 0; i < table.Length; ++i) { x = table[i]; AddCallback(() => { Debug.Log(x); }); }
|
そうではなくて個々の値をキャプチャする場合にはループ内部で別の変数に代入してやって、クロージャ内からはそれを参照するようにすれば別々の値がキャプチャされることになる:
foreach (var x in table) { var y = x; AddCallback(() => { Debug.Log(y); }); } RunCallbacks();
|
Suck!
- 2016/10/12: Unity5.5.0b6で試したところ、解消されて個々の値が束縛されるようになった!
- しかし有効なのは
foreach
だけで、 for
のループ変数は相変わらずなのであった…
for (var i = 0; i < table.Length; ++i) { var j = i; AddCallback(() => { Debug.Log("i=" + i + ", j=" + j); }); }
|