4.1.5 变量的作用域

我们从中国移动的一句经典广告语开始:我的地盘我做主。变量的作用域即广告语中所说的“地盘”,出了变量的作用域便相当于离开了它的地盘,也就意味着该变量将变得不再“可见”,当然也就无法对它执行读取、赋值等操作了,实际上,从栈的角度来看,变量离开了作用域后,该变量将被弹出栈。

我们来研究下这个“地盘”是如何确定的,确定“地盘”的规则如下:

❑类的字段所处的作用域等同于该字段所属类所在的作用域;

❑局部变量的作用域仅限于声明它的方法或循环体内部,以大括号{}为界。

变量的作用域如图4-2所示。

4.1.5 变量的作用域 - 图1

图 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,其值为:“你好,世界!”。

关于变量,我们就介绍到这里,其他章节还会对它做相应的补充,接下来我们一起看看变量的“对立面”:常量。