3 回答
TA贡献1835条经验 获得超7个赞
这取决于方法接收者和变量的类型。
简短回答:如果您正在使用该database/sql包,您的延迟Rows.Close()方法将正确关闭您的两个Rows实例,因为Rows.Close()有指针接收器并且因为DB.Query()返回一个指针(rows也是一个指针)。请参阅下面的推理和解释。
为避免混淆,我建议使用不同的变量,并且很清楚您想要什么以及将关闭什么:
rows := Query(`SELECT FROM whatever`)
defer rows.Close()
// ...
rows2 := Query(`SELECT FROM whatever`)
defer rows2.Close()
我想指出一个重要的事实,它来自延迟函数及其参数被立即评估,这在Effective Go博客文章和语言规范:延迟语句中也有说明:
每次执行“defer”语句时,函数值和调用的参数都会像往常一样评估并重新保存,但不会调用实际的函数。相反,在周围函数返回之前立即调用延迟函数,以与延迟相反的顺序调用。
如果变量不是指针:当调用延迟的方法时,您将观察到不同的结果,这取决于该方法是否具有指针接收器。
如果变量是一个指针,您将始终看到“期望”的结果。
看这个例子:
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()
}
输出:
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
在Go Playground上试一试。
使用指针变量的结果总是好的(如预期的那样)。
使用非指针变量和指针接收器,我们会看到相同的打印结果(最新的),但如果我们有值接收器,它会打印 2 个不同的结果。
非指针变量说明:
如前所述,包括接收器在内的延迟函数在defer执行时被评估。如果是指针接收器,它将是局部变量的地址。因此,当您为其分配一个新值并调用 another 时defer,指针接收器将再次使用本地变量的相同地址(只是指向的值不同)。所以稍后在执行函数时,两者都会使用相同的地址两次,但指向的值将相同,即稍后分配的值。
在值接收器的情况下,接收器是执行时创建的副本defer,因此如果您为变量分配一个新值并调用 another defer,则会创建另一个与前一个不同的副本。
TA贡献1858条经验 获得超8个赞
有效的围棋提到:
延迟函数的参数(如果函数是方法,则包括接收者)在延迟执行时计算,而不是在调用执行时计算。
除了避免担心函数执行时变量会改变值,这意味着单个延迟调用站点可以延迟多个函数执行
在您的情况下,延迟将引用第二行实例。
这两个延迟函数以 LIFO 顺序执行(也如“延迟、恐慌和恢复”中所述)。
正如icza在他的回答和评论中提到的:
2 个延迟
Close()
方法将引用2 个不同的 Rows 值,并且两者都将被正确关闭,因为rows
是指针,而不是值类型。
- 3 回答
- 0 关注
- 264 浏览
添加回答
举报