推迟使用说明
Defer usage clarification
假设我有以下功能
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
语句时,只会计算指针([=19=的地址,*Myint
的类型)。由于类型 *MyInt
实现了 fmt.Stringer
,fmt.Println()
将调用它的 String()
方法。
4) 环绕
这也类似于指针解决方案,它甚至不会像您期望的那样只打印 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}
如果 defer 有参数,它们将在 defer 语句的行进行计算;以下代码段对此进行了说明,其中 defer 将打印 0:
func printNumber() {
i := 0
defer fmt.Println(i) // will print 0
i++
return
}
如果要将语句或函数的执行推迟到封闭(调用)函数结束,则可以将匿名函数用作延迟语句。这是一个更新的示例:
func printNumbers() {
x := 0
defer func() { fmt.Println(x) }()
for i:=0; i < 5; i++ {
x++;
}
return
}
假设我有以下功能
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
语句时,只会计算指针([=19=的地址,*Myint
的类型)。由于类型 *MyInt
实现了 fmt.Stringer
,fmt.Println()
将调用它的 String()
方法。
4) 环绕
这也类似于指针解决方案,它甚至不会像您期望的那样只打印 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}
如果 defer 有参数,它们将在 defer 语句的行进行计算;以下代码段对此进行了说明,其中 defer 将打印 0:
func printNumber() {
i := 0
defer fmt.Println(i) // will print 0
i++
return
}
如果要将语句或函数的执行推迟到封闭(调用)函数结束,则可以将匿名函数用作延迟语句。这是一个更新的示例:
func printNumbers() {
x := 0
defer func() { fmt.Println(x) }()
for i:=0; i < 5; i++ {
x++;
}
return
}