假设我有以下功能
func printNumbers(){
var x int
defer fmt.Println(x)
for i := 0; i < 5; i++{
x++
}
}
正如specification中所说:
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.
显然,函数执行结束时会打印出零。
但是如果我想打印出变量x
的最终值怎么办呢?
我想出了以下解决方案:
func printNumbers(){
var x int
printVal := func(){
fmt.Println(x)
}
defer printVal()
for i := 0; i < 5; i++{
x++
}
}
所以我想知道是否有更好的方法来解决这个问题。
最佳答案
一般来说,重要的是 x
不能作为您要延迟的函数的参数,因为它们是在执行 defer
时计算的。
1) 具有匿名功能
这是一个使用匿名函数的解决方案:
defer func() { fmt.Println(x) }()
这里的x
不是延迟匿名函数的参数,所以不会被求值。仅当执行匿名函数并调用 fmt.Println()
时。
2) 带指针
使用指向 x
的指针(如 &x
)会起作用,因为只评估地址,最后指向的值将是 5
当然。问题是 fmt.Println()
不会打印指向的值,而是打印指针本身。
但是为了演示它的工作原理,请看这个辅助函数:
func Print(i *int) {
fmt.Println(*i)
}
并使用它:
defer Print(&x) // Will print 5 at the end
3) 自定义类型
这类似于指针解决方案,但不需要辅助函数。但它确实需要您编写您的 String()
方法:
type MyInt int
func (m *MyInt) String() string {
return strconv.Itoa(int(*m))
}
并使用它:
var x MyInt
defer fmt.Println(&x)
for i := 0; i < 5; i++ {
x++
}
当执行defer
语句时,只会计算指针(x
的地址,*Myint
的类型)。由于 *MyInt
类型实现了 fmt.Stringer
, fmt.Println()
会调用它的 String()
方法。
4) 包装
这也类似于指针解决方案,它甚至不会像您期望的那样只打印 5
,但是:
#2 的问题是 fmt.Println()
将打印指针而不是指向的值(我们用自己的 Print()
函数解决了这个问题)。然而,还有其他类似于指针的类型,fmt.Println()
将打印它们的内容。
所以让我们将变量包装到一个 slice 中,看看会发生什么:
x := []int{0}
defer fmt.Println(x)
for i := 0; i < 5; i++ {
x[0]++
}
打印:
[5]
看到 5
的原因是 slice 是一个描述符。当 defer
被评估时,一个副本由 slice 组成(当它被执行时将被传递给 fmt.Println()
)但它引用相同的底层数组.
另请注意,如果指针是指向结构、数组、 slice 、映射的指针,则 fmt.Println()
会打印指向的内容,因此以下代码也有效:
x := struct{ i int }{}
defer fmt.Println(&x)
for i := 0; i < 5; i++ {
x.i++
}
并打印:
&{5}
关于go - 推迟使用说明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31404471/