Golang开发--defer关键字

defer是Go语言中的一个关键字,用于延迟执行函数或方法的调用,defer语句会将其后面的函数或方法调用推迟到当前函数返回之前执行,无论函数是正常返回还是发生异常。
使用defer语句的方法如下:

  1. 在函数中使用defer语句时,需要在调用函数或方法的语句前面加上defer关键字。

  2. 可以使用多个defer语句,它们的执行顺序与声明顺序相反,即最后一个defer语句最先执行。

  3. defer语句中的函数或方法调用可以包含参数,这些参数会在defer语句执行时被计算并保存,但实际调用时会使用当前的参数值。

  4. defer 语句中的函数参数会在 defer 语句执行时求值,而不是在函数返回时求值。这意味着,如果 defer 语句中的函数调用有参数,参数的值在 defer 语句执行时就会被确定。

func main() {
    defer fmt.Println("defer 1")
    defer fmt.Println("defer 2")
    fmt.Println("Hello, World!")
}

输出结果为:

Hello, World!
defer 2
defer 1

可以看到,"Hello, World!"是先输出的,而defer语句中的函数调用是在main函数返回之前执行的,且按照声明顺序的相反顺序执行。

defer的作用主要有以下几个方面:

  1. 资源释放:defer语句常用于释放资源,比如关闭文件、关闭数据库连接等。通过在打开资源后使用defer语句,可以确保在函数返回之前一定会执行资源释放操作,避免资源泄漏。
func readFile() {
    file, err := os.Open("filename.txt")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer file.Close()
    // 读取文件并进行其他操作
}

在上面的示例中,使用 defer 语句将 file.Close() 推迟到函数返回之前执行,无论函数返回时是正常执行还是发生错误,都能确保文件被关闭,避免资源泄漏。

2.解锁操作:在使用互斥锁(mutex)或读写锁(RWMutex)的情况下,使用 defer 语句延迟解锁,以避免忘记解锁而导致死锁。例如:

var mu sync.Mutex

func someFunction() {
    mu.Lock()
    defer mu.Unlock()
    // 临界区代码
}

在上面的示例中,使用 defer 语句将 mu.Unlock() 推迟到函数返回之前执行,确保在函数退出时解锁互斥锁。

  1. 错误处理:defer语句也可以用于处理错误。在函数中可能会发生多个错误,但只有最后一个错误需要处理,可以使用defer语句将错误处理代码推迟到函数返回之前执行,确保错误处理代码只会执行一次。
func process() error {
    err := setup()
    if err != nil {
        return err
    }
    defer cleanup()
    // 其他操作
    return nil
}

在上面的示例中,cleanup() 函数会在 process() 函数退出前被推迟执行,无论是正常返回还是发生错误。

  1. 日志记录:defer语句还可以用于记录日志。在函数中可能会有多个地方需要记录日志,但只需要在函数返回之前记录一次即可,可以使用defer语句将日志记录代码推迟到函数返回之前执行。

5.defer 的执行顺序
defer执行顺序和调用顺序相反,类似于栈后进先出(LIFO),defer在return之后执行,但在函数退出之前,defer可以修改返回值。

func test() int {
	i := 0
	defer func() {
		fmt.Println("defer1")
	}()
	defer func() {
		i += 1
		fmt.Println("defer2")
	}()
	return i
}

func main() {
	fmt.Println("return", test())
}
// defer2
// defer1
// return 0

上面这个例子中,test返回值并没有修改,这是由于Go的返回机制决定的,执行Return语句后,Go会创建一个临时变量保存返回值。如果是有名返回(也就是指明返回值func test() (i int))

func test() (i int) {
	i = 0
	defer func() {
		i += 1
		fmt.Println("defer2")
	}()
	return i
}

func main() {
	fmt.Println("return", test())
}
// defer2
// return 1

这个例子中,返回值被修改了。对于有名返回值的函数,执行 return 语句时,并不会再创建临时变量保存,因此,defer 语句修改了 i,即对返回值产生了影响。

有名返回(Named Return)和无名返回(Unnamed Return)是用于函数返回值的两种不同机制。

有名返回无名返回
定义有名返回是在函数定义时为返回值指定了变量名。可以在函数体内部直接使用这些变量名,并在函数结束时,直接通过 return 语句返回这些变量的值。无名返回是在函数定义时没有为返回值指定变量名,只指定了返回值的类型。函数体内部直接通过 return 语句返回计算结果,不需要使用具体的变量名来存储返回值。
使用使用有名返回时,函数体内部可以在任何地方使用返回值的变量名,并对其进行修改或操作。在函数结束时,只需使用 return 语句返回这些变量名即可。使用无名返回时,返回值的计算通常在 return 语句中完成,并且该值不能在函数体内的其他地方使用或修改。
优点可以在函数体内部多次修改返回值,灵活性更高。可以给返回值起一个有意义的变量名,增加代码的可读性和可维护性。简洁明了,减少了代码的复杂性。对于简单的函数,无名返回可以更加直观和高效。

有名返回:

func calculateSum(a, b int) (sum int) {
    sum = a + b
    return sum
}

无名返回:

func calculateSum(a, b int) int {
    return a + b
}

需要注意的是,在有多个返回值的函数中,有名返回和无名返回可以混合使用。例如,函数可以定义有名返回值和无名返回值的组合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值