我们假设我有以下功能
func printNumbers(){
var x int
defer fmt.Println(x)
for i := 0; i < 5; i++{
x++
}
}
正如specification中所述:
每次执行“延迟”语句时,将像往常一样评估调用的函数值和参数,并重新保存,但不会调用实际函数。
显然,当函数执行结束时,将打印出零。
但是,如果我想打印变量x
的最终值,我该怎么办?
我提出了以下解决方案:
func printNumbers(){
var x int
printVal := func(){
fmt.Println(x)
}
defer printVal()
for i := 0; i < 5; i++{
x++
}
}
所以我想知道是否有更好的方法来解决这个问题。
答案 0 :(得分:6)
一般来说重要的是x
不能是你要推迟的函数的参数,因为它们是在执行defer
时进行评估的。
这是使用匿名函数的解决方案:
defer func() { fmt.Println(x) }()
此处x
不是延迟匿名函数的参数,因此不会对其进行求值。仅当执行匿名函数并且它调用fmt.Println()
。
使用指向&x
的指针(如x
)会起作用,因为只评估地址,当然最后的指向值为5
。这个问题是fmt.Println()
不会打印指向的值而是指针本身。
但为了证明它的工作原理,请参阅此辅助函数:
func Print(i *int) {
fmt.Println(*i)
}
使用它:
defer Print(&x) // Will print 5 at the end
这类似于指针解决方案,但不需要辅助函数。但它确实需要您编写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()
方法。
这也类似于指针解决方案,这甚至不会像您期望的那样只打印5
,但是:
#2的问题是fmt.Println()
将打印指针而不是指向的值(我们用自己的Print()
函数解决)。但是,还有其他类型与指针类似,fmt.Println()
将打印其内容。
因此,让我们将变量包装成一个切片,看看会发生什么:
x := []int{0}
defer fmt.Println(x)
for i := 0; i < 5; i++ {
x[0]++
}
打印:
[5]
查看5
的原因是切片是描述符。在评估defer
时,会对切片进行复制(在执行时将传递给fmt.Println()
),但它引用相同的基础数组。
另请注意,如果指针是指向结构,数组,切片,贴图的指针,fmt.Println()
将打印指向的内容,因此以下代码也可以工作:
x := struct{ i int }{}
defer fmt.Println(&x)
for i := 0; i < 5; i++ {
x.i++
}
并打印:
&{5}
答案 1 :(得分:3)
如果延迟有参数,则在延迟语句的行中对它们进行评估;这在下面的代码片段中说明,其中defer将打印0:
self.viewController.check()
如果要将语句或函数的执行推迟到封闭(调用)函数的末尾,则可以使用匿名函数作为延迟语句。这是一个更新的例子:
func printNumber() {
i := 0
defer fmt.Println(i) // will print 0
i++
return
}