第 8 节 · 第一部分 基础语法

变量作用域

时长 4:41授课计划第 3 周

讲义

跟着视频的顺序写。每行代码都有注释,代码和运行结果都是实际运行得到的。

1作用域管什么

作用域管变量的两件事:可见性(在哪儿能用这个名字)和生命周期(变量能存在多久)。作用域通常由一对大括号 {} 划出来。

视频把程序比成一张沙盘:每对大括号是一圈围栏,代码找名字只能从自己这圈往外找,看不进别的围栏。这只是比喻,内存里没有围栏,作用域是编译器查名字的规则。

2局部变量

在方法里声明的变量叫局部变量。教程只给了片段,下面补上调用和打印真跑一下。这是顶级语句写法(不写类和 Main,代码从第一行开始执行),void MyMethod() 是直接写在文件里的方法:

C# · 第 1-7 行
1MyMethod();// 调用方法 MyMethod
2
3void MyMethod()// 定义方法 MyMethod,不接收参数、不返回值
4{// MyMethod 开始
5 int localVar = 10; // 局部变量// 在方法里声明 localVar,赋值 10
6 Console.WriteLine(localVar);// 在方法里面找得到它,输出 10
7}// MyMethod 结束,localVar 跟着失效
运行结果
10

出了方法再用 localVar,编译不过:

C# · 第 8-9 行
8// localVar 在这里不可见
9Console.WriteLine(localVar);// 在方法外面打印 localVar
编译报错
Program.cs(9,19): error CS0103: 当前上下文中不存在名称“localVar”

每次调用都重来

局部变量不光看不见,还活不长:方法一结束它就不存在了,下次调用重新创建。

C#
1Count();// 第 1 次调用:n 新建为 0,加 1,输出 1
2Count();// 第 2 次调用:n 又新建为 0,加 1,还是输出 1
3
4void Count()// 定义方法 Count
5{// Count 开始
6 int n = 0;// 每次进来都新建一个 n,赋值 0
7 n = n + 1;// n 变成 1
8 Console.WriteLine(n);// 输出 n
9}// Count 结束,n 随之消失
运行结果
1
1

3块级作用域

不光是方法,随便一对大括号都能圈出一个作用域。

C#
1{// 代码块开始
2 int blockVar = 20; // 块级作用域// 在块里声明 blockVar,赋值 20
3 Console.WriteLine(blockVar);// 在块里打印,输出 20
4}// 代码块结束,blockVar 失效
5// blockVar 在这里不可见

出了右大括号再用,又是同一个错误:

C# · 第 6 行
6Console.WriteLine(blockVar);// 在块外面打印 blockVar
编译报错
Program.cs(6,19): error CS0103: 当前上下文中不存在名称“blockVar”

块级作用域从 C# 第一个版本就有,教程说“C# 7 才引入”的说法不准。

4方法参数

参数写在方法名后面的小括号里,在整个方法里都能用,方法里再套一层大括号也能用。

C# · 第 1-10 行
1MyMethod(5);// 调用 MyMethod,把 5 传给 parameter
2
3void MyMethod(int parameter)// 定义 MyMethod,小括号里的 int parameter 是参数
4{// MyMethod 开始
5 // parameter 在整个方法中可见
6 Console.WriteLine(parameter);// 输出 5
7 {// 里层代码块开始
8 Console.WriteLine(parameter * 2);// 从里层往外找到 parameter:5 × 2,输出 10
9 }// 里层代码块结束
10}// MyMethod 结束
运行结果
5
10

到了方法外面,parameter 一样找不到:

C# · 第 11 行
11Console.WriteLine(parameter);// 在方法外面打印 parameter
编译报错
Program.cs(11,19): error CS0103: 当前上下文中不存在名称“parameter”

5成员变量

写在类里面、方法外面的变量叫成员变量(教程叫“全局变量”)。类里的方法都找得到它。

C# · 第 8-17 行
8int memberVar = 30; // 成员变量,在整个类中可见// 写在 MyClass 里、方法外面,初值 30
9
10public void Add()// Add 方法:给 memberVar 加 1
11{// Add 开始
12 memberVar = memberVar + 1;// 找得到类里的 memberVar,加 1
13}// Add 结束
14
15public void Show()// Show 方法:打印 memberVar
16{// Show 开始
17 Console.WriteLine(memberVar);// 同样找得到 memberVar
C# · 第 1-4 行
1MyClass obj = new MyClass();// 用 new 创建一个 MyClass 对象 obj
2obj.Show();// 输出 30
3obj.Add();// memberVar:30 + 1 = 31
4obj.Show();// 输出 31:两次调用之间,值一直留着
运行结果
30
31

C# 没有全局变量,命名空间里不能直接写变量:

C# · 第 3-6 行
3namespace MyApp// 定义命名空间 MyApp
4{// MyApp 开始
5 int globalVar = 50;// 想在命名空间里直接放一个变量
6}// MyApp 结束
编译报错
Program.cs(5,9): error CS0116: 命名空间不能直接包含字段、方法或语句之类的成员

6静态变量

在类里用 static 声明的变量叫静态变量,在这个类自己的方法里直接写名字就能用。

C# · 第 1-10 行
1MyClass.Show();// 用“类名.方法名”调用静态方法 Show,输出 40
2
3class MyClass// 定义 MyClass 类
4{// MyClass 开始
5 static int staticVar = 40; // 静态变量,在整个类中可见// static 声明,初值 40
6
7 public static void Show()// 静态方法 Show
8 {// Show 开始
9 Console.WriteLine(staticVar);// 在类自己的方法里,直接写名字就能用
10 }// Show 结束
运行结果
40

在类外面直接写 staticVar 还是找不到,它的作用域也只在这个类里:

C# · 第 1 行
1Console.WriteLine(staticVar);// 在 MyClass 外面直接写名字
编译报错
Program.cs(1,19): error CS0103: 当前上下文中不存在名称“staticVar”

想在外面用,要加上 public,再写成“类名.变量名”,同样输出 40(public 的细节讲封装时再说):

C# · 第 1-5 行
1Console.WriteLine(MyClass.staticVar);// 类名.变量名:从外面访问静态变量
2
3class MyClass// 定义 MyClass 类
4{// MyClass 开始
5 public static int staticVar = 40; // 静态变量,在整个类中可见// 加了 public,外面才能用

7循环变量

for 后面括号里声明的 i,只在这个循环里能用。

C# · 第 1-5 行
1for (int i = 0; i < 5; i++)// i 从 0 开始,小于 5 就继续,每轮加 1
2{// 循环体开始
3 // i 在循环体内可见
4 Console.WriteLine(i);// 依次输出 0、1、2、3、4
5}// 循环体结束,i 失效
运行结果
0
1
2
3
4

出了循环再打印 i,同样报错:

C# · 第 7 行
7Console.WriteLine(i);// 在循环外面打印 i
编译报错
Program.cs(7,19): error CS0103: 当前上下文中不存在名称“i”

正因为这样,两个并排的循环可以各用各的 i,互不打架:

C#
1for (int i = 0; i < 3; i++)// 第一个循环,有自己的 i
2{// 第一个循环体开始
3 Console.Write(i);// Write 不换行:输出 012
4}// 第一个循环体结束,这个 i 失效
5Console.WriteLine();// 换行
6for (int i = 0; i < 3; i++)// 第二个循环,重新声明一个 i,不冲突
7{// 第二个循环体开始
8 Console.Write(i);// 再输出 012
9}// 第二个循环体结束
运行结果
012
012

反过来不行:外面已经有一个 i,循环里不能再声明一个 i。

C# · 第 1-2 行
1int i = 100;// 外面先声明了 i
2for (int i = 0; i < 5; i++)// 循环里又声明 i,和外面的撞名
编译报错
Program.cs(2,10): error CS0136: 无法在此范围中声明名为“i”的局部变量或参数,因为该名称在封闭局部范围中用于定义局部变量或参数

8小结

  • 作用域管两件事:在哪儿可见,能活多久。
  • 局部变量、块里的变量、参数和循环变量,出了大括号就找不到(CS0103);局部变量每次调用都重来。
  • 成员变量和静态变量在整个类里都能用,成员变量的值在两次调用之间保留;C# 没有全局变量(CS0116)。
  • 找名字从里往外一圈圈找;内层不能再声明外层已有的同名局部变量(CS0136)。

下一节:有些值从头到尾都不该被改,比如圆周率,怎么让它定下来就改不了?

检查题

5 道,选完马上告诉你对不对。

  1. 1void MyMethod() { int localVar = 10; } 之后在方法外面写 Console.WriteLine(localVar); 会?

  2. 2方法 Count() 里 int n = 0; n = n + 1; Console.WriteLine(n);,调用两次输出?

  3. 3类里 int memberVar = 30;,方法 Add 让它加 1,依次 Show、Add、Show 输出?

  4. 4哪个说法对?

  5. 5int i = 100; for (int i = 0; i < 5; i++) { … } 会?