在 Go 语言中,代码块内声明的标识符,只能在代码块内使用。例如函数体是个代码块,里面定义的变量,只要没发生逃逸,在外部无法访问。 但有一种特殊情况,就是在支持初始化子语句的流程语句中,初始化子语句中定义的变量,作用域会延伸到条件对应的代码块之外。请看代码:
package main
import "fmt"
func main() {
if a := 1; false {
fmt.Println(a)
//fmt.Println(b) // 不能引用下面语句定义的块级变量
} else if b := 2; a > b {
fmt.Println(a - b) // 可以引用上面语句定义的块级变量
} else {
fmt.Println(a + b) // 在最后 a 和 b 都可以引用
}
}
左看右看也想不明白,为什么 else if 中定义的变量 b 也能在 else 块中使用?如果有更多条件判断,那么前一个 else if 中定义的变量,在之后所有 else if 块中都能使用。
在 C++ 中,if 中定义的变量 a,可以跨整个流程语句,else if 中定义的变量明确不能在其他 else 代码块中引用:
#include <iostream>
int main() {
if (int a = 1; false) { // a 在整个 if-else 结构中可见
std::cout << a << std::endl;
} else if (int b = 2; a > b) { // a 可访问
std::cout << a - b << std::endl;
} else {
std::cout << a << std::endl; // a 可访问
// std::cout << b << std::endl; // 错误:b 不可访问
}
return 0;
}
在 C# 中,规定更是严格,if 和 else if 中定义的变量,只能在随后的代码块中使用,不能跨到代码块:
public class Program {
public static void Main() {
if (int a = 1; false) {
Console.WriteLine(a);
} else if (int b = 2; a > b) { // 错误:a 不可访问
Console.WriteLine(a - b);
} else {
Console.WriteLine(a + b); // 错误:a 和 b 都不可访问
}
}
}
有本 Go 语言书管这叫「惰性求值」行为,但我也仅仅是知道有这么个现象,不明白为什么会设计成这样?