在C#中声明变量使用下述语法:
datatype identifier;
例如:
int i;
该语句声明int变量i。编译器不会让我们使用这个变量,除非我们用一个值初始化了该变量。但这个声明会在堆栈中给它分配4个字节,以保存其值。
一旦它被声明之后,就可以使用赋值运算符(=)给它分配一个值:
i = 10;
还可以在一行代码中声明变量,并初始化它的值:
int i = 10;
其语法与C++和Java语法相同,但与VB中声明变量的语法完全不同。如果你是一位VB6用户,应记住C#不区分对象和简单的类型,所以不需要类似Set的关键字,即使是要把变量指向一个对象,也不需要Set关键字。无论变量的数据类型是什么,声明变量的C#语法都是相同的。
如果在一个语句中声明和初始化了多个变量,那么所有的变量都具有相同的数据类型:
int x = 10, y =20; // x and y are both ints
要声明类型不同的变量,需要使用单独的语句。在多个变量的声明中,不能指定不同的数据类型:
int x = 10;
bool y = true; // Creates a variable that stores true or false
int x = 10, bool y = true; // This won't compile!
变量的初始化
变量的初始化是C#强调安全性的另一个例子。简单地说,C#编译器需要用某个初始值对变量进行初始化,之后才能在操作中引用该变量。大多数现代编译器把没有初始化标记为警告,但C#编译器把它当作错误来看待。这就可以防止我们无意中从其他程序遗留下来的内存中获取垃圾值。
C#有两个方法可确保变量在使用前进行了初始化:
● 变量是类或结构中的字段,如果没有显式进行初始化,在默认状态下当创建这些变量时,其值就是0。
● 方法的局部变量必须在代码中显式初始化,之后才能在语句中使用它们的值。此时,初始化不是在声明该变量时进行的,但编译器会通过方法检查所有可能的路径,如果检测到局部变量在初始化之前就使用了它的值,就会产生错误。
C#的方法与C++的方法相反,在C++中,编译器让程序员确保变量在使用之前进行了初始化,在VB中,所有的变量都会自动把其值设置为0。
例如,在C#中不能使用下面的语句:
public static int Main ()
{
int d;
Console.WriteLine(d); // Can't do this! Need to initialize d before use
return 0;
}
注意在这段代码中,演示了如何定义Main(),使之返回一个int类型的数据,而不是void的数据。
在编译这些代码时,会得到下面的错误消息:
Use of unassigned local variable 'd'
同样的规则也适用于引用类型。考虑下面的语句:
Something objSomething;
在C++中,上面的代码会在堆栈中创建Something类的一个实例。在C#中,这行代码仅会为Something对象创建一个引用,但这个引用还没有指向任何对象。对该变量调用方法或属性会导致错误。
在C#中实例化一个引用对象需要使用new关键字。如上所述,创建一个引用,使用new关键字把该引用指向存储在堆上的一个对象:
objSomething = new Something(); // This creates a Something on the heap
变量的作用域
变量的作用域是可以访问该变量的代码区域。一般情况下,确定作用域有以下规则:
● 只要字段所属的类在某个作用域内,其字段(也称为成员变量)也在该作用域内(在C++、Java和 VB中也是这样)。
● 局部变量存在于表示声明该变量的块语句或方法结束的封闭花括号之前的作用域内。
● 在for、while或类似语句中声明的局部变量存在于该循环体内(C++程序员注意,这与C++的ANSI标准相同。Microsoft C++编译器的早期版本不遵守该标准,但在循环停止后这种变量仍存在)。
1. 局部变量的作用域冲突
大型程序在不同部分为不同的变量使用相同的变量名是很常见的。只要变量的作用域是程序的不同部分,就不会有问题,也不会产生模糊性。但要注意,同名的局部变量不能在同一作用域内声明两次,所以不能使用下面的代码:
int x = 20;
// some more code
int x = 30;
考虑下面的代码示例:
using System;
namespace Wrox.ProCSharp.Basics
{
public class ScopeTest
{
public static int Main ()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
} // i goes out of scope here
// We can declare a variable named i again, because
// there's no other variable with that name in scope
for (int i = 9; i >= 0; i--)
{
Console.WriteLine(i);
} // i goes out of scope here
return 0;
}
}
}
这段代码使用一个for循环打印出从0~9的数字,再打印从9~0的数字。重要的是在同一个方法中,代码中的变量i声明了两次。可以这么做的原因是在两次声明中,i都是在循环内部声明的,所以变量i对于循环来说是局部变量。
下面看看另一个例子:
public static int Main ()
{
int j = 20;
for (int i = 0; i < 10; i++)
{
int j = 30; // Can't do this - j is still in scope
Console.WriteLine(j + i);
}
return 0;
}
如果试图编译它,就会产生如下错误:
ScopeTest.cs(12,14): error CS0136: A local variable named 'j' cannot be declared in this scope because it would give a different meaning to 'j', which is already used in a 'parent or current' scope to denote something else
(局部变量j不能在这个作用域内声明两次,因为这会给变量j赋予不同的含义,已用在'parent or current'作用域内的变量j表示其他内容)
其原因是:变量j是在for循环开始前定义的,在执行for循环时应处于其作用域内,在Main方法结束执行后,变量j才超出作用域,第二个j(不合法)则在循环的作用域内,该作用域嵌套在Main方法的作用域内。编译器无法区别这两个变量,所以不允许声明第二个变量。这也是与C++不同的地方,在C++中,允许隐藏变量。
2. 字段和局部变量的作用域冲突
在某些环境下,可以区分名称相同(尽管不是经过完全限定的名称)、作用域相同的两个标识符。此时编译器允许声明第二个变量。原因是C#使得变量之间有一个基本的区分,它把声明为类型级的变量看作是字段,而把在方法中声明的变量看作局部变量。
考虑下面的代码:
using System;
namespace Wrox.ProCSharp.Basics
{
class ScopeTest2
{
static int j = 20;
public static void Main ()
{
int j = 30;
Console.WriteLine(j);
return;
}
}
}
即使在Main方法的作用域内声明了两个变量j,这段代码也会编译—— j被定义在类级(class level)上,在该类删除前是不会超出作用域的(在本例中,当Main方法中断时,程序结束)。此时,在Main方法中声明的新变量j隐藏了同名的类级变量,所以在运行这段代码时,会显示数字30。
但是,如果要引用类级变量,该怎么办?可以使用语法object.fieldname,在对象的外部引用类的字段或结构。在上面的例子中,我们访问静态方法中的一个静态字段(静态字段详见下一节),所以不能使用类的实例,只能使用类本身的名称:
…
public static void Main ()
{
int j = 30;
Console.WriteLine(ScopeTest2.j);
}
...
如果要访问一个实例字段(该字段属于类的一个特定实例),就需要使用this关键字。this的作用与C++和Java中的this相同,与VB中的Me相同。