在C#中声明变量使用下述语法:
datatype identifier;
例如:int i;
该语句声明int变量i。编译器不允许在表达式中使用这个变量,除非用一个值初始化了该变量。声明i之后,就可以使用赋值运算符(=)给它赋值:i = 10。
还可以在一行代码中声明变量,并初始化它的值:int i = 10;
如果在一条语句中声明和初始化了多个变量,那么所有的变量都具有相同的数据类型: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#有两个方法可确保变量在使用前进行了初始化:
(1)变量是类或结构中的字段,如果没有显示初始化,创建这些变量时,其默认值就是0。
(2)方法的局部变量必须在代码中显示初始化,之后才能在语句中使用它们的值。此时,初始化不是在生命该变量时进行的,但编译器会通过方法检测所有可能的路径,如果检测到局部变量在初始化之前就使用了它的值,就会产生错误。
考虑下面的语句:
Something objSomething;
在C#中,这行代码仅会为Something对象创建一个引用,但这个引用还没有指向任何对象。对该变量调用方法或属性会导致错误。
在C#中实例化一个引用对象需要使用new关键字。如上所术,创建一个引用,使用new关键字把该引用指向存储在堆上的一个对象:
objSomething = new Something(); // This creates a Something on the heap
类型推断
类型推断(type inference)使用var关键字。编译器可以根据变量的初始化值"推断"变量的类型。
例如:int someNumber = 0;
就变成:var someNumber = 0;
即使someNumber从来没有声明为int,编译器也可以确定,只要someNumber在其作用域内,就是一个int。编译后,上面两个语句是等价的。
using System; namespace ConsoleApplication { class Program { static void Main(string[] args) { var name = "Bugs Bunny"; var age = 25; var isRabbit = true; Type nameType = name.GetType(); Type ageType = age.GetType(); Type isRabbitType = isRabbit.GetType(); Console.WriteLine("name is type " + nameType.ToString()); Console.WriteLine("age is type " + ageType.ToString()); Console.WriteLine("isRabbit is type " + isRabbitType.ToString()); } } }
这个程序的输出如下:
name is type System.String
age is type System.Int32
isRabbit is type System.Bool
需要遵循一些规则:
(1)变量必须初始化。否则,编译器就没有推断变量类型的依据。
(2)初始化器不能为空。
(3)初始化器必须放在表达式中。
(4)不能把初始化器设置为一个对象,除非在初始化器重创建了一个新对象。
声明了变量,推断出了类型后,就不能改变变量类型了。变量的类型确定后,就遵循其他变量类型遵循的强类型化规则。
变量的作用域
变量的作用域是可以访问该变量的代码区域。一般情况下,确定作用域遵循以下规则:
(1)只要类在某个作用域内,其字段(也称为成员变量)也在改作用域内。
(2)局部变量存在于表示声明该变量的块语句或方法结束的右花括号之前的作用域内。
(3)在for、while或类似语句中声明的局部变量存在于该循环体内。
1.局部变量的作用域冲突
大型程序在不同部分为不同的变量使用相同的变量名很常见。只要变量的作用域是程序的不同部分,就不会有问题,也不会产生多义性。但要注意,同名的局部变量不能在同一作用域内声明两次,所以不能使用下面的代码:
int x = 20;
// some more code
int x = 30;
考虑下面的代码示例:
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的数字,再逆序打印0~9的数字。重要的是在同一个方法中,代码中的变量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);
}
}
如果视图编译它,就会产生如下错误:
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是在for循环开始前定义的,在执行for循环时应处于其作用域内,在Main()方法结束执行后,变量j才超出作用域,第2个j(不合法)则在循环的作用域内,该作用域嵌套在Main()方法的作用域内。因为编译器无法区分这两个变量,所以不允许声明第2个变量。
2.字段和局部变量的作用域冲突
某些情况下,可以区分名称相同(尽管其完全限定的名称不同)、作用域相同的两个标识符。此时编译器允许声明第2个变量。原因是C#在变量之间有一个基本的区分,它把在类型级别声明的变量看作字段,而把在方法中声明的变量看作局部变量。
考虑下面的代码:
class ScopeTest
{
static int j = 20;
public static void Main()
{
int j = 30;
Console.WriteLint(j);
return;
}
}
虽然在Main()方法的作用域内声明了两个变量j。这段代码也会编译--在类级上定义的j,在该类删除前是不会超出作用域的;以及在Main()中定义的j。此时,在Main()方法中声明的新变量j隐藏了同名的类级变量,所以在运行这段代码时,会显示数字30。
但是,如果要引用类级变量,该怎么办?可以使用语法object.fieldname,在对象的外部引用类或结构的字段。在上面的例子中,访问静态方法中的一个静态字段,所以不能使用类的实例,只能使用类本身的名称:
...
public static void Main()
{
int j = 30;
Console.WriteLine(j);
Console.WriteLine(ScopeTest.j);
}
...
如果要访问一个实例字段(该字段属于类的一个特定实例),就需要使用this关键字。
常量
变量是其值在使用过程中不会发生变化的变量。在生命和初始化变量,在变量的前面加上关键字const,就可以把该变量指定为一个常量:
const int a = 100;
常量具有如下特点:
(1)常量必须在声明时初始化。指定了其值后,就不能再改写了。
(2)常量的值必须能在编译时用于计算。因此,不能用从一个变量中提取的值来初始化常量。如果需要这么做,应使用只读字段。
(3)常量总是静态的。但注意,不必(实际上,是不允许)在常量生命中包含修饰符static。
在程序中使用常量至少有3个好处:
(1)由于使用易于读取的名称(名称的值易于理解)替代了较难读取的数字或字符串,常量使程序变得更易于阅读。
(2)常量使程序更易于理解。
(3)常量更容易避免程序出现错误。如果在声明常量的位置以外的某个地方将另一个值赋给常量,编译器就会报告错误。