Go 言語 の defer は、少なくとも今日の主流のプログラミング言語と比較すると、この言語の新しい機能とみなされます。
defer ステートメントは関数を呼び出します。この関数の実行は、周辺関数が戻るか、周辺関数が最後まで実行されるか、または対応する goroutine パニックが発生するまで延期されます。
defer が指定されるたびに、後続の関数値 (Go では、関数は参照型であり、第一級市民であり、変数に代入できます) と関数パラメーターが評価されますが、関数は (↑) まですぐには呼び出されません。 ) 上記の 3 つの状況が発生します。これが defer の内容全体であり、残りは defer のベスト プラクティスです。
関数はすぐには呼び出されません
最も単純なものから始めます:
func readFile(fileName string){ f,err := os.Open(fileName) if err!=nil { return } defer f.Close() var content [1024]byte f.Read(content[:]) fmt.Printf("%s",content) } func main() { readFile("test.data") }
プログラムは test.data の最初の 1024 バイトを出力します。このようなオープン/クローズのペアリング操作は defer の慣用的な使用法であることに注意してください。この例は、上記の文の後半を説明しています。
「ただし、関数は呼び出されません。」
because if f.Close() after defer が遅延していない場合、ファイル記述子がすべて閉じられている場合、何も読み取られません。
関数の値と関数のパラメーターは評価されますが、関数はすぐには呼び出されません。
次の例では前半を説明します。これは <> から来ています。 、少し変更:
func trace(funcName string) func(){ start := time.Now() fmt.Printf("function %s enter\n",funcName) return func(){ log.Printf("function %s exit (elapsed %s)",funcName,time.Since(start)) } } func foo(){ defer trace("foo()")() time.Sleep(5*time.Second) } func main(){ foo() foo() } /* OUTPUT: function foo() enter function foo() exit (elapsed 5.0095471s) function foo() enter function foo() exit (elapsed 5.0005382s) */
なぜ foo 出力は enter になり、exit を出力するまで約 5 秒待機しますか? 前述したように、
defer の後の関数の値とパラメータは評価されますが、実際の関数 呼び出しは最後まで待つ必要があります。
ここでの関数値は、trace() によって返される匿名関数です。関数パラメータは、もちろん文字列リテラル値 "foo()" です。trace の評価("foo()") は出力関数 foo() に入り、実際の関数呼び出し trace("foo()")()、つまり出力関数 foo() exit(elapsed x.x) は実行が返されるまで延期されます。 (return が戻り値変数を更新する場合、更新後に defer 関数を実行する前に実行されます)。
その他
もう少し詳しく言うと、複数の defer ステートメントがある場合、最後の defer 関数の実行順序は defer の出現順序と逆になります。例:
func main() { func1 := func(){ fmt.Println("func1() execution deferred") } func2 := func(){ fmt.Println("func2() execution deferred") } defer func1() defer func2() fmt.Println("strat\nworking...") } /* OUTPUT: strat working... func2() execution deferred func1() execution deferred */
以上がgo defer(遅延機能)の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。