Golang推迟澄清 [英] Golang defer clarification

查看:123
本文介绍了Golang推迟澄清的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



例如:

pre> rows:= Query(`SELECT FROM whatever`)
推迟rows.Close()
for rows.Next(){
// do
}
rows = Query(`SELECT FROM another`)
推迟rows.Close()
作为rows.Next(){
//执行其他操作

其中 when最后一个 rows.Close()被调用?

解决方案

它取决于接收方关于变量类型。



简短回答:如果您使用< a href =https://golang.org/pkg/database/sql/ =nofollow noreferrer> database / sql 包,你的延期 Rows.Close() 方法将正确关闭你的实例因为 Rows.Close()指针 receiver ,因为 DB.Query() 返回一个指针(所以 rows 是一个指针)。为了避免混淆,我建议使用不同的变量,它会清楚你想要的是什么,以及什么是什么。

  rows:= Query(`SELECT FROM whatever`)
推迟行。 Close()
// ...
rows2:= Query(`SELECT FROM whatever`)
推迟rows2.Close()






我想指出一个来自延迟函数及其参数的重要事实,在 Effective Go 博文以及语言规范:递延声明


<每次执行延迟语句时,调用的函数值和参数都照常进行评估,重新保存,但实际的f不会调用联合。相反,延迟函数在周围函数返回前立即被调用,按照推迟顺序的相反顺序。

如果变量不是指针:当调用延迟方法时,您将观察到不同的结果,具体取决于方法是否有指针接收器。

如果变量是指针,则总会看到期望结果。

看下面这个例子:

  type X struct {
$ s


func(x X)Close(){
fmt.Println(Value-Closing,xS)
}

func(x * X)CloseP(){
fmt.Println(Pointer-Closing,xS)
}

func main(){
x:= X {Value-X First}
推迟x.Close()
x = X {Value-X Second}
推迟x.Close()

x2:= X {Value-X2 First}
推迟x2.CloseP()
x2 = X {Value-X2 Second}
推迟x2。 CloseP()

xp:=& X {Pointer-X First}
推迟xp.Close()
xp =& X {Pointer-X Second}
推迟xp.Close()

xp2:=& X {指针-X2 First}
推迟xp2.CloseP()
xp2 =& X {Pointer-X2 Second}
推迟xp2.CloseP()
}

输出:

 指针闭合指针-X2第二
指针闭合指针-X2第一
值闭指针-X第二
值闭指针-X第一
指针闭合值-X2第二个
指针 - 收盘价-X2第二个
价值收盘价-X第二个
价值收盘价-X第一个

Go Playground



使用指针变量的结果总是好的(如预期的那样)。



指针变量和使用指针接收器我们看到相同的打印结果(最新),但如果我们有价值的接收器,它会打印2个不同的结果。



非指针变量的解释:

如上所述,评估包括接收器在内的延迟函数当 defer 执行时。如果是指针接收器,它将是本地变量的地址。所以当你给它赋一个新值并调用另一个 defer 时,指针接收器将再次是局部变量的相同地址指向值不同)。因此,稍后当函数被执行时,两者将使用相同的地址两次,但是指向的值将是相同的,稍后分配给它。

在值接收方的情况下,接收方是执行 defer 时产生的副本,所以如果您为变量分配新值并调用另一个 defer ,另一个副本将与前一个不同。


What happened when defer called twice when the struct of that method has been changed?

For example:

rows := Query(`SELECT FROM whatever`)
defer rows.Close()
for rows.Next() { 
  // do something
}
rows = Query(`SELECT FROM another`) 
defer rows.Close()
for rows.Next() {
  // do something else
}

which rows when the last rows.Close() called?

解决方案

It depends on the method receiver and on the type of the variable.

Short answer: if you're using the database/sql package, your deferred Rows.Close() methods will properly close both of your Rows instances because Rows.Close() has pointer receiver and because DB.Query() returns a pointer (and so rows is a pointer). See reasoning and explanation below.

To avoid confusion, I recommend using different variables and it will be clear what you want and what will be closed:

rows := Query(`SELECT FROM whatever`)
defer rows.Close()
// ...
rows2 := Query(`SELECT FROM whatever`)
defer rows2.Close()


I'd like to point out an important fact that comes from the deferred function and its parameters being evaluated immedately which is stated in the Effective Go blog post and in the Language Spec: Deferred statements too:

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. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.

If variable is not a pointer: You will observe different results when calling a method deferred, depending if the method has a pointer receiver.
If variable is a pointer, you will see always the "desired" result.

See this example:

type X struct {
    S string
}

func (x X) Close() {
    fmt.Println("Value-Closing", x.S)
}

func (x *X) CloseP() {
    fmt.Println("Pointer-Closing", x.S)
}

func main() {
    x := X{"Value-X First"}
    defer x.Close()
    x = X{"Value-X Second"}
    defer x.Close()

    x2 := X{"Value-X2 First"}
    defer x2.CloseP()
    x2 = X{"Value-X2 Second"}
    defer x2.CloseP()

    xp := &X{"Pointer-X First"}
    defer xp.Close()
    xp = &X{"Pointer-X Second"}
    defer xp.Close()

    xp2 := &X{"Pointer-X2 First"}
    defer xp2.CloseP()
    xp2 = &X{"Pointer-X2 Second"}
    defer xp2.CloseP()
}

Output:

Pointer-Closing Pointer-X2 Second
Pointer-Closing Pointer-X2 First
Value-Closing Pointer-X Second
Value-Closing Pointer-X First
Pointer-Closing Value-X2 Second
Pointer-Closing Value-X2 Second
Value-Closing Value-X Second
Value-Closing Value-X First

Try it on the Go Playground.

Using a pointer variable the result is always good (as expected).

Using a non-pointer variable and using pointer receiver we see the same printed results (the latest) but if we have value receiver, it prints 2 different results.

Explanation for non-pointer variable:

As stated, deferred function including the receiver is evaluated when the defer executes. In case of a pointer receiver it will be the address of the local variable. So when you assign a new value to it and call another defer, the pointer receiver will be again the same address of the local variable (just the pointed value is different). So later when the function is executed, both will use the same address twice but the pointed value will be the same, the one assigned later.

In case of value receiver, the receiver is a copy which is made when the defer executed, so if you assign a new value to the variable and call another defer, another copy will be made which is different from the previous one.

这篇关于Golang推迟澄清的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆