


Utilisez huit démos pour comprendre les cinq fonctionnalités majeures du report du langage Go
Utilisez le mot-clé defer
en langage Go pour retarder l'exécution du code jusqu'à la fin de la fonction. En développement, nous utilisons souvent le mot-clé defer
pour effectuer le travail ultérieur, comme la fermeture des descripteurs de fichiers ouverts, la fermeture des connexions et la libération des ressources. defer
关键字可以将代码延迟到函数结束之前执行。在开发中,我们经常使用defer
关键字完成善后工作,如关闭打开的文件描述符、关闭连接以及释放资源等。
func demo0() { fileName := "./test.txt" f, _ := os.OpenFile(fileName, os.O_RDONLY, 0) defer f.Close() contents, _ := ioutil.ReadAll(f) fmt.Println(string(contents))}
defer
关键字一般紧跟在打开资源代码的后面,防止后续忘记释放资源,defer 声明的代码实际上要等到函数结束之前才会被执行。defer 虽然简单易用,但如果忽略了它的特性,就会在开发中面临困惑。于是,我总结了 defer 的五大特性,通过 8 个demo逐步介绍 defer 的特性。
特性1:多个 defer 时的调用顺序:先进后出
使用多个 defer 关键字时,先被声明的 defer 语句后被调用。类似于“栈”先进后出的特性,defer 的这一特性也很好理解,先被打开的资源,可能会被后续代码依赖,所以要后释放才安全。
func demo1() { for i := 0; i < 5; i++ { defer fmt.Println("defer:", i) }}// defer: 4// defer: 3// defer: 2// defer: 1// defer: 0
特性2:作用域为当前函数,不同函数下拥有不同的 defer 栈
运行 demo2 ,从结果中可以看出,第一个匿名函数和第二个匿名函数的 defer 执行顺序没有关系。
defer 作用域仅为当前函数,在当前函数最后执行,所以不同函数下拥有不同的 defer 栈。
func demo2() { func() { defer fmt.Println(1) defer fmt.Println(2) }() fmt.Println("=== 新生代农民工啊 ===") func() { defer fmt.Println("a") defer fmt.Println("b") }()}// 2// 1// === 新生代农民工啊 ===// b// a
特性3:defer 后的函数形参在声明时确认(预计算参数)
运行 demo3_1 ,根据结果,我们可以得出:defer 在声明时,就已经确认了形参n的值,而不是在执行时确认的;所以,后续变量 num 无论如何改变都不影响 defer 的输出结果。
func demo3_1() { num := 0 defer func(n int) { fmt.Println("defer:", n) }(num) // 等同 defer fmt.Println("defer:", num) for i := 0; i < 10; i++ { num++ } fmt.Println(num)}//10//defer: 0
运行 demo3_2,为什么这里 defer 的最终输出的结果会和变量 num 相同?因为这里使用的是指针。
defer 声明时,已经确认了形参p指针的指向地址,指向变量 num;后续变量 num 发生改变。所以在 defer 执行时,输出的是p指针指向的变量num的当前值。
func demo3_2() { num := 0 p := &num defer func(p *int) { fmt.Println("defer:", *p) }(p) for i := 0; i < 10; i++ { num++ } fmt.Println(*p)}//10//defer: 10
再看一下 demo3_3,defer 打印的变量并没有通过函数参数传入,在defer执行时,才获取的”全局变量”num,所以 defer 输出结果与变量num一致。
func demo3_3() { num := 0 defer func() { fmt.Println("defer:", num) }() for i := 0; i < 10; i++ { num++ } fmt.Println(num)}//10//defer: 10
特性4:return 与 defer 执行顺序:return 先 defer 后
运行 demo4_1,可以发现 defer、return 都是在函数最后执行,但 return 先于 defer 执行;
func demo4_1() (int, error) { defer fmt.Println("defer") return fmt.Println("return")}// return// defer
这一点从输出结果上显而易见,但当 return、defer 的执行顺序和**函数返回值**
“相遇”时,又将会产生许多复杂的场景。
在 demo4_2 中,函数使用命名返回值
,最终输出结果为7。其中经历了这几个过程:
(首先)变量 num 作为返回值,初始值为0;
(其次)随后变量 num 被赋值为 10;
(然后)return 时,变量 num 作为返回值被重新赋值为 2;
(接着)defer 在 return 后执行,拿到变量 num 进行修改,值为7;
-
(最后)变量 num 作为返回值,最终函数返回结果为7;
func demo4_2() (num int) { num = 10 defer func() { num += 5 }() return 2}// 7
Copier après la connexion
再来看一个例子。
在 demo4_3 中,函数使用匿名返回值
func demo4_3() int { num := 10 defer func() { num += 5 }() return 2}// 2
defer
suit généralement le code pour ouvrir la ressource pour éviter d'oublier ultérieurement de libérer la ressource. Le code déclaré par defer ne sera effectivement exécuté qu'à la fin de la fonction. Bien que defer soit simple et facile à utiliser, si vous ignorez ses fonctionnalités, vous serez confronté à une confusion lors du développement. Par conséquent, j'ai résumé les cinq fonctionnalités majeures de defer et j'ai progressivement introduit les fonctionnalités de defer à travers 8 démos. - Fonction 1 : Ordre d'appel lorsque plusieurs defers sont utilisés : premier entré, dernier sortiLorsque plusieurs mots-clés defer sont utilisés, l'instruction defer déclarée en premier est appelée plus tard. Semblable à la fonctionnalité « pile » premier entré, dernier sorti, cette fonctionnalité de report est également facile à comprendre. Les ressources ouvertes en premier peuvent être utilisées par le code suivant. est nécessaire pour pouvoir libérer en toute sécurité plus tard.
- Fonction 2 : La portée est la fonction actuelle, et il existe différentes piles de report sous différentes fonctionsExécutez la démo2. Il ressort des résultats que l'ordre de report de l'exécution de la première fonction anonyme et de la deuxième fonction anonyme. n'a aucun rapport.
La portée du report est uniquement la fonction actuelle et est exécutée à la fin de la fonction actuelle, donc différentes fonctions ont différentes piles de report. - Fonction 3 : Les paramètres de fonction après report sont confirmés au moment de la déclaration (paramètres précalculés) Exécutez demo3_1, selon les résultats, nous can get Out: defer La valeur du paramètre formel n a été confirmée lors de la déclaration, pas lors de l'exécution, par conséquent, les modifications ultérieures de la variable num ne l'affecteront pas. sortie de report. rrreee
- Exécutez demo3_2, pourquoi le résultat final de defer est-il le même que la variable num ? Parce que des pointeurs sont utilisés ici.
defer Lorsqu'elle est déclarée, l'adresse pointée par le paramètre formel p pointeur a été confirmée, pointant vers la variable num ; par la suite, la variable num change. Ainsi, lorsque defer est exécuté, la sortie est la valeur actuelle de la variable num pointée par le pointeur p.rrreee
Regardez à nouveau demo3_3. Les variables imprimées par defer ne sont pas transmises via les paramètres de la fonction. Le num "variable globale" n'est obtenu que lorsque deferest exécuté, donc le résultat de sortie de defer est cohérent. avec la variable num. rrreee - Fonction 4 : ordre d'exécution return et différé : return d'abord defer puis Exécutez demo4_1, vous pouvez constater que defer et return sont exécutés à la fin de la fonction, mais return est exécuté avant defer
rrreee
C'est ; évident d'après les résultats de sortie Mais lorsque l'ordre d'exécution de return et defer et**fonction renvoie la valeur**
"se rencontrent", de nombreux scénarios complexes se produiront.
Dans demo4_2, la fonction utilise unevaleur de retour nommée
et le résultat final est 7. Il a suivi ces processus : (premier) la variable num est utilisée comme valeur de retour, avec une valeur initiale de 0(deuxième) puis la variable num se voit attribuer une valeur de 10 ;
(then ) Lors du retour, la variable num est réaffectée à 2 comme valeur de retour ;
func demo5_1() { defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) panic("没点赞异常") // 触发defer出栈执行 defer fmt.Println(4) // 得不到执行}
func demo5_2() { defer func() { if err := recover(); err != nil { fmt.Println(err, "问题不大") } }() panic("没点赞异常") // 触发defer出栈执行 // ...}
Dans demo4_3, la fonction utilise une
valeur de retour anonyme
et le résultat final est 2. Le processus est le suivant : lorsque 🎜🎜🎜🎜 entre dans la fonction, la variable de valeur de retour n'est pas créée à ce moment ; 🎜🎜🎜🎜 crée la variable num et lui attribue une valeur de 10 lorsque 🎜🎜🎜🎜return, le la variable de valeur de retour de la fonction est créée et la valeur attribuée est 2 ; vous pouvez considérer cette variable de valeur de retour comme une variable anonyme, ou une variable a, b, c, d..., mais ce n'est pas la variable num quand 🎜🎜 ; 🎜🎜defer, peu importe la façon dont vous modifiez la variable num, Cela n'a rien à voir avec la valeur de retour de la fonction 🎜🎜🎜🎜Donc, le résultat final renvoyé par la fonction est 2 🎜rrreee🎜Fonction 5 : En cas de panique, le résultat déclaré est : defer sortira de la pile et exécutera 🎜🎜Run demo5_1, vous pouvez voir que lorsque la panique se produit, le defer déclaré sortira de la pile puis la panique, et le defer déclaré après la panique ne sera pas exécuté. 🎜rrreee🎜En utilisant cette fonctionnalité, vous pouvez capturer la panique via la récupération en différé pour empêcher le programme de planter. 🎜rrreee🎜Ci-joint🎜🎜code complet : 🎜github.com/newbugcoder/learngo/tre...🎜🎜🎜🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds



La bibliothèque utilisée pour le fonctionnement du numéro de point flottante dans le langage go présente comment s'assurer que la précision est ...

Quelles bibliothèques de GO sont développées par de grandes entreprises ou des projets open source bien connus? Lors de la programmation en Go, les développeurs rencontrent souvent des besoins communs, ...

Problème de threading de file d'attente dans Go Crawler Colly explore le problème de l'utilisation de la bibliothèque Crawler Crawler dans le langage Go, les développeurs rencontrent souvent des problèmes avec les threads et les files d'attente de demande. � ...

La différence entre l'impression de chaîne dans le langage go: la différence dans l'effet de l'utilisation de fonctions println et string () est en Go ...

GO POINTER SYNTAXE ET ATTENDRE DES PROBLÈMES DANS LA BIBLIOTHÈQUE VIPER Lors de la programmation en langage Go, il est crucial de comprendre la syntaxe et l'utilisation des pointeurs, en particulier dans ...

Deux façons de définir les structures dans le langage GO: la différence entre les mots clés VAR et le type. Lorsque vous définissez des structures, GO Language voit souvent deux façons d'écrire différentes: d'abord ...

Pourquoi l'itération de la carte dans GO fait-elle que toutes les valeurs deviennent le dernier élément? En langue go, face à des questions d'entrevue, vous rencontrez souvent des cartes ...

GO Language Slice Index: Pourquoi une tranche à élément unique intercepte-t-elle de l'index 1 sans erreur? En langue GO, les tranches sont une structure de données flexible qui peut se référer au bas ...
