遅延関数呼び出しとクロージャ キャプチャ
Go では、defer ステートメントを使用すると、周囲の関数が戻る直前に関数を実行できます。 defer の重要な側面の 1 つは、クロージャの処理方法です。
このコード スニペットでは:
package main import "fmt" func main() { var whatever [5]struct{} for i := range whatever { fmt.Println(i) } // part 1 for i := range whatever { defer func() { fmt.Println(i) }() // part 2 } for i := range whatever { defer func(n int) { fmt.Println(n) }(i) // part 3 } }
パート 1 では、単純に i の値を 0 から 4 まで出力します。ただし、パート 2 では、次のことを示します。興味深い行動。 0 ~ 4 の期待値を出力する代わりに、「44444」を出力します。
これは、パート 2 のクロージャが i 変数をキャプチャしているためです。後でクロージャが実行されると、変数 i には、range ステートメントの最後の反復で持っていた値、つまり 4 が入ります。その結果、すべての遅延関数呼び出しでは 4 が出力されます。
対照的に、パート3 は外部変数をキャプチャしません。 Go の仕様によれば、「『defer』ステートメントが実行されるたびに、呼び出しに対する関数の値とパラメーターが通常どおり評価され、新たに保存されますが、実際の関数は呼び出されません。」これは、遅延関数呼び出しのそれぞれに異なる値の 'n' パラメーターがあることを意味します。これは、'defer' ステートメントが実行された時点の 'i' の値です。
したがって、パート 3 は正しくなります。遅延関数呼び出しは周囲の関数の前に LIFO 順序 (後入れ先出し) で実行されるため、「43210」が出力されます。
'defer f(e)' の式は即座に評価されるのに対し、'defer f()' の関数式は defer ステートメントの実行時には実行されないことを覚えておくことが重要です。
以上がGo で `defer func() { fmt.Println(i) }()` が '44444' を出力するのに、 `defer func(n int) { fmt.Println(n) }(i)` は正しく '43210' を出力するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。