4.1.5 变量的作用域
我们从中国移动的一句经典广告语开始:我的地盘我做主。变量的作用域即广告语中所说的“地盘”,出了变量的作用域便相当于离开了它的地盘,也就意味着该变量将变得不再“可见”,当然也就无法对它执行读取、赋值等操作了,实际上,从栈的角度来看,变量离开了作用域后,该变量将被弹出栈。
我们来研究下这个“地盘”是如何确定的,确定“地盘”的规则如下:
❑类的字段所处的作用域等同于该字段所属类所在的作用域;
❑局部变量的作用域仅限于声明它的方法或循环体内部,以大括号{}为界。
变量的作用域如图4-2所示。
图 4-2 变量的作用域
下面,我们来看一个例子,如代码清单4-5所示。
代码清单4-5 变量的作用域
using System;
namespace ProgrammingCSharp4CSharp4
{
class AboutVariableScope
{
public string fieldVar;//作用域和它所属的类的作用域相同
public void DoSomething()
{
//someVar变量作用域仅限在DoSomething()方法内部,以{}为界
var someVar="some value";
//someVars变量作用域同someVar变量
int[]someVars={1,2,3,4,5,6};
//item的作用域仅限foreach循环体内部,以{}为界
foreach(var item in someVars)
{
Console.WriteLine(item);
}
//这里将无法访问foreach循环体内部定义的item变量,
//编译器会报错
Console.WriteLine(item);
//sampleObject对象的sampleValue字段的作用域
//与sampleObject对象的作用域相同
SampleObject sampleObject=new SampleObject();
sampleObject.sampleValue="sampleValue's scope";
}
}
class SampleObject
{
public string sampleValue;
}
}
代码中以"//"开始的语句称为注释,主要是起说明和备注作用,注释语句会被编译器忽略,并不会被编译。关于注释的更多信息,请参阅第9.17节。
代码清单4-5 中各变量的作用域情况已经在注释中说明了,建议大家亲自动手实践一下。
接下来,我们探讨作用域的重叠问题。从层次的角度来看C#代码,就是一层层的{}嵌套,那么必然会存在变量作用域重叠的问题,这时候我们就说两者的作用域发生了冲突。冲突主要分为两种情况:
❑同一个作用域内,存在两个同名的变量,这里不关心变量的类型是否相同。如果存在这种情况,编译器将会报错,编译将无法继续;
❑局部变量和字段级变量同名,那么局部变量会将同名的字段级变量隐藏,就是说在局部变量的作用域内,局部变量的值覆盖了字段级变量的值。
针对以上两种情况,我们来看一下示例代码,如代码清单4-6所示。
代码清单4-6 作用域冲突
1 using System;
2
3 namespace ProgrammingCSharp4CSharp4
4{
5 class AboutVariableScope
6{
7 public string fieldVar=“你好,世界!”;
8 public void DoSomething()
9{
10 var someVar="some value";
11 var fieldVar=“我是DoSomething()方法内的局部变量”;
12
13 int[]someVars={1,2,3,4,5,6};
14
15 foreach(var item in someVars)
16{
17 string item=“作用域冲突了!”;
18 Console.WriteLine(item);
19}
20
21 Console.WriteLine(fieldVar);
22 Console.WriteLine(this.fieldVar);
23}
24}
25}
我们来分析上面这段代码,第17行的item变量位于foreach循环体内,位于第15行的foreach循环中已定义了变量item,并且它和第17行的item作用域相同,这符合前面说的第一种冲突条件,属于语法错误,编译器会及时产生错误异常,如下所示:
不能在此范围内声明名为"item"的局部变量,因为这样会使"item"具有不同的含义,而它已在“父级或当前”范围中表示其他内容了。
这类冲突编译器可以协助我们检查出来,但接下来的一种情况,则属于逻辑错误,并且很难发现。我们来看第7行,这里定义了一个字段级别的变量fieldVar,它的值为:“你好,世界!”,但在另一个地方——DoSomething()方法内部(即第11行),我们又定义了一个相同名称的局部变量fieldVar,其值为:“我是DoSomething()方法内的局部变量”,那么我们来看第21行——输出fieldVar的值,它会输出字段变量fieldVar还是局部变量fieldVar的值呢?由于局部变量隐藏了字段变量,因此这里输出的是:“我是DoSomething()方法内的局部变量”。如果要使用字段变量fieldVar的值,则要像第22行那样使用this关键字。this关键字代表当前类的实例,可以看作当前类实例的别名,比如这里的this.fieldVar代表的是字段变量fieldVar,其值为:“你好,世界!”。
关于变量,我们就介绍到这里,其他章节还会对它做相应的补充,接下来我们一起看看变量的“对立面”:常量。