5.包和文件

包和文件

Go 文件中的内部信息, 根据是否大写字母开头判断是否导出

1
2
var Str string  // 其他类导入包后可以调用
var str string  // 包内可调用

按照惯例,一个包的名字和包的导入路径的最后一个字段相同,例如gopl.io/ch2/tempconv包的名字一般是tempconv。

如果导入了一个包,但是又没有使用该包将被当作一个编译错误处理。这种强制规则可以有效减少不必要的依赖

使用 golang.org/x/tools/cmd/goimports 自动添加删除导入包

包的初始化

包的初始化首先是解决包级变量的依赖顺序,然后按照包级变量声明出现的顺序依次初始化:

1
2
3
4
5
var a = b + c // a 第三个初始化, 为 3
var b = f()   // b 第二个初始化, 为 2, 通过调用 f (依赖c)
var c = 1     // c 第一个初始化, 为 1

func f() int { return c + 1 }

如果包中含有多个.go源文件,它们将按照发给编译器的顺序进行初始化,Go语言的构建工具首先会将.go文件根据文件名排序,然后依次调用编译器编译。

init 函数特点

  • init 优先于 main 自动执行 , 不能被其它函数调用
  • 每个包可以有多个 init 函数
  • 导入包前缀加上 _ 仅执行 该包的 全局变量初始化和 init
  • 无参数声明和结果声明
  • 在每个文件中的init初始化函数,在程序开始执行时按照它们声明的顺序被自动调用。

image-20200407010517771

注 : 程序不应该依赖各个包的 init 的执行顺序

init 常用用途

实现包级变量复杂初始化,注: 尽量不要使用 init 函数来初始化包级变量,如无必要,不要使用 init 函数,减少代码复杂性。

1
2
3
4
5
6
7
8
var ProjectDebug = false

func init(){
  e := os.Getenv("PROJECT_DEBUG")
  if strings.Contains(e, "http2debug=1") { 
    ProjectDebug = true 
  }
}

注册模式

利用 _ 导入,会使导入的依赖初始化,如 pg 包中实现的驱动注册。

通过在 init 函数中注册自己的实现的模式,有效降低了 Go 包对外的直接暴露。

1
import _ "github.com/lib/pq"

Go 程序初始化流程

  1. 初始化导入的包 , 没有依赖的包优先初始化
  2. 初始化有依赖的包
  3. 初始化没有依赖的常量 , 变量
  4. 初始化有依赖的常量 , 变量
  5. 执行 init 函数
  6. 执行 main 函数

这里写图片描述

image-20220408190138953

作用域

当编译器遇到一个名字引用时,它会对其定义进行查找,查找过程从最内层的词法域向全局的作用域进行。如果查找失败,则报告“未声明的名字”这样的错误。如果该名字在内部和外部的块分别声明过,则内部块的声明首先被找到。在这种情况下,内部声明屏蔽了外部同名的声明,让外部的声明的名字无法被访问

for 循环有显示词法域和隐式词法域 , 显示词法域是花括号里代码块 , 隐式词法域是 i:=0 初始化

和for循环类似,if和switch语句也会在条件部分创建隐式词法域,还有它们对应的执行体词法域。

switch语句的每个分支也有类似的词法域规则:条件部分为一个隐式词法域,然后是每个分支的词法域。

1
2
3
4
5
6
7
8
if x := f(); x == 0 {
    fmt.Println(x)
} else if y := g(x); x == y {
    fmt.Println(x, y)
} else {
    fmt.Println(x, y)
}
fmt.Println(x, y) // compile error: x and y are not visible here

一个错误的演示

1
2
3
4
5
if f, err := os.Open(fname); err != nil { // compile error: unused: f
    return err
}
f.ReadByte() // compile error: undefined f
f.Close()    // compile e

你可以这样干, 但是不推荐

1
2
3
4
5
6
7
if f, err := os.Open(fname); err != nil {
    return err
} else {
    // f and err are visible here too
    f.ReadByte()
    f.Close()
}

对于变量的初始化 , 如果要处理错误 , 不建议直接声明赋值 , 而是这样做

1
2
3
4
5
6
7
8
9
var cwd string

// 这里的会创建局部变量, 并没有对包变量 cwd 赋值
func init() {
    cwd, err := os.Getwd() // compile error: unused: cwd
    if err != nil {
        log.Fatalf("os.Getwd failed: %v", err)
    }
}

不建议的

var cwd,_ = os.GetWd()

正确的做法

1
2
3
4
5
6
7
8
9
var cwd string

func init() {
    var err error
    cwd, err = os.Getwd()
    if err != nil {
        log.Fatalf("os.Getwd failed: %v", err)
    }
}
Licensed under CC BY-NC-SA 4.0
本文阅读量 次, 总访问量 ,总访客数
Built with Hugo .   Theme Stack designed by Jimmy