3.3 变量
如前所述,变量关系到数据的存储。实际上,可以把计算机内存中的变量看成架子上的盒子。在这些盒子中,可以放入一些东西,再把它们取出来,或者只是看看盒子里是否有东西。变量也是这样,数据可放在变量中,可以从变量中取出数据或查看它们。
尽管计算机中的所有数据事实上都是相同的东西(一组0和1),但变量有不同的内涵,称为类型。下面再用盒子来类比,盒子有不同的形状和尺寸,某些东西只适合放在特定的盒子中。建立这个类型系统的原因是,不同类型的数据需要用不同的方法来处理。将变量限定为不同的类型可以避免混淆。例如,组成数字图片的0和1序列与组成声音文件的0和1序列,其处理方式是不同的。
要使用变量,需要声明它们,即给变量指定名称和类型。声明变量后,就可以把它们用作存储单元,存储所声明的数据类型的数据。
声明变量的C#语法是指定类型和变量名,如下所示:
<type> <name>;
如果使用未声明的变量,代码将无法编译,但此时编译器会告诉我们出现了什么问题,所以这不是一个灾难性错误。另外,使用未赋值的变量也会产生一个错误,编译器会检测出这个错误。
3.3.1 简单类型
简单类型就是组成应用程序中基本构件的类型,例如,数值和布尔值(true或false)。与复杂类型不同,简单类型没有子类型或特性。大多数简单类型都是存储数值的,初看起来有点奇怪,使用一种类型来存储数值不可以吗?
有很多数值类型是因为在计算机内存中,把数字作为一系列的0和1来存储。对于整数值,用一定的位(单个数字,可以是0或1)来存储,用二进制格式来表示。以N位来存储的变量可以表示任何介于0到(2N_1)之间的数。大于这个值的数因为太大,所以无法存储在这个变量中。
例如,有一个变量存储了两位,在整数和表示该整数的位之间的映射应如下所示:
0 = 00 1 = 01 2 = 10 3 = 11
如果要存储更多数字,就需要更多的位(例如,3位可以存储0到7的数)。
这样得到的结论是要存储每个可以想象得到的数,就需要非常多的位,这并不适合PC。即使可以用足够多的位来表示每一个数,用这么多的位存储一个表示范围很小的变量(例如0到10)的效率非常低下,因为存储器被浪费了。其实表示0到10之间的数,4位就足够了,这样就可以用相同的内存空间存储这个范围内的更多数值。
相反,许多不同的整数类型可用于存储不同范围的数值,占用不同的内存空间(至多64位),这些类型如表3-1所示。
表3-1 整数类型
注意:这些类型中的每一种都利用了.NET Framework中定义的标准类型。如第1章所述,使用标准类型可以在语言之间交互操作。在C#中这些类型的名称是Framework中定义的类型的别名,表3-1列出了这些类型在.NET Framework库中的名称。
一些变量名称前面的“u”是unsigned的缩写,表示不能在这些类型的变量中存储负数,参见表3-1中的“允许的值”一列。
当然,还需要存储浮点数,它们不是整数。可以使用的浮点数变量类型有3种:float、double和decimal。前两种可以用+/-m×2e的形式存储浮点数,m和e的值因类型而异。decimal使用另一种形式:+/-m×10e。这3种类型、m和e的值,以及它们在实数中的上下限如表3-2所示。
表3-2 浮点类型
除数值类型外,另外还有3种简单类型,如表3-3所示。
表3-3 文本和布尔类型
注意组成string的字符数量没有上限,因为它可以使用可变大小的内存。
布尔类型bool是C#中最常用的一种变量类型,类似的类型在其他语言的代码中非常丰富。当编写应用程序的逻辑流程时,一个可以是true或false的变量有非常重要的分支作用。例如,考虑一下有多少问题可以用true或false(或yes和no)来回答。执行变量值之间的比较或检查输入的有效性就是后面使用布尔变量的两个编程示例。
介绍了这些类型后,下面用一个简短示例来声明和使用它们。在下面的示例中,要使用一些简单的代码来声明两个变量,给它们赋值,再输出这些值。
试一试:使用简单类型的变量:Ch03Ex01\Program.cs
(1)在目录C:\BegVCSharp\Chapter03下创建一个新的控制台应用程序Ch03Ex01。
(2)在Program.cs中添加如下代码:
static void Main(string[] args) { int myInteger; string myString; myInteger = 17; myString = "\"myInteger\" is"; Console.WriteLine($"{myString} {myInteger}"); Console.ReadKey(); }
(3)运行代码,结果如图3-1所示。
图3-1
示例说明
我们添加的代码完成了以下3项任务:
● 声明两个变量
● 给这两个变量赋值
● 将这两个变量的值输出到控制台
变量声明使用下述代码:
int myInteger; string myString;
第一行声明一个类型为int的变量myInteger,第二行声明一个类型为string的变量myString。
注意:变量的命名是有限制的,不能使用任意字符序列。“变量的命名”一节将介绍变量的命名规则。
接下来的两行代码为变量赋值:
myInteger = 17; myString = "\"myInteger\" is";
使用=赋值运算符(在本章的“表达式”一节中将详细介绍)给变量分配两个固定的值(在代码中称为字面值)。把整数值17赋给myInteger,把字符串"myInteger\" is(包括引号)赋给myString。
"myInteger" is
以这种方式给字符串赋予字面值时,必须用双引号把字符串括起来。因此,如果字符串本身包含双引号,就会出现错误,必须用一些表示这些字符的其他字符(即转义序列)来替代它们。本例使用序列\"来转义双引号:
myString = "\"myInteger\" is";
如果不使用这些转义序列,而输入如下代码:
myString = ""myInteger" is";
就会出现编译错误。
注意给字符串赋予字面值时,必须小心换行—— C#编译器会拒绝分布在多行上的字符串字面值。要添加一个换行符,可在字符串中使用换行符的转义序列,即\n。例如,赋值语句:
myString = "This string has a\nline break.";
会在控制台视图中显示两行代码,如下所示:
This string has a line break.
所有转义序列都包含一个反斜杠符号,后跟一个字符组合(详见后面的内容),因为反斜杠符号的这种用途,它本身也有一个转义序列,即两个连续的反斜杠\\。
下面继续解释代码,还有一行没有说明:
Console.WriteLine($"{myString} {myInteger}");
这是C# 6中的一个新功能,称为字符串插入,它看起来类似于第一个示例中把文本写到控制台的简单方法,但本例指定了变量。这里不详细讨论这行代码,只需要知道这是本书第I部分用于给控制台窗口输出文本的一种技巧。
在后面的示例中,就使用这种给控制台输出文本的方式显示代码的输出结果。最后一行代码在前面的示例中也出现过,用于在程序结束前等待用户输入内容:
Console.ReadKey();
这里不详细探讨这行代码,但后面的示例会常常用到它。现在只需要知道,它暂停代码的执行,等待用户按下一个键。
3.3.2 变量的命名
如上一节所述,不能把任意序列的字符作为变量名。但没必要为此感到担心,因为这种命名系统仍是非常灵活的。
基本的变量命名规则如下:
● 变量名的第一个字符必须是字母、下划线(_)或@。
● 其后的字符可以是字母、下划线或数字。
另外,有一些关键字对于C#编译器而言具有特定的含义,例如前面出现的using和namespace关键字。如果错误地使用其中一个关键字,编译器会产生一个错误,我们马上就会知道出错了,所以不必担心。
例如,下面的变量名是正确的:
myBigVar VAR1 _test
下列变量名有误:
99BottlesOfBeer namespace It's-All-Over
3.3.3 字面值
在前面的示例中,有两个字面值示例:整数(17)和字符串("\"myInteger\" is")。其他变量类型也有相关的字面值,如表3-4所示。其中有许多涉及后缀,即在字面值的后面添加一些字符来指定想要的类型。一些字面值有多种类型,在编译时由编译器根据它们的上下文确定其类型(同样见表3-4)。
表3-4 字面值
字符串字面值
本章前面介绍了几个可在字符串字面值中使用的转义序列,表3-5是这些转义序列的完整列表,以便以后引用。
表3-5 字符串字面值的转义序列
表3-5中的“字符的Unicode值”列是字符在Unicode字符集中的十六进制值。除了上面这些,还可以使用Unicode转义序列指定其他任何Unicode字符,该转义序列包括标准的\字符,后跟一个u和一个4位十六进制值(例如,表3-5中x后面的4位数字)。
下面的字符串是等价的:
"Benjamin\'s string." "Benjamin\u0027s string."
显然,Unicode转义序列还有更多用途。
也可以一字不变地指定字符串,即两个双引号之间的所有字符都包含在字符串中,包括行末字符和原本需要转义的字符。唯一的例外是必须指定双引号字符的转义序列,以免结束字符串。这种方法需要在字符串之前加一个@字符:
@"Verbatim string literal."
也可以用普通方式指定这个字符串,但下面的字符串就必须使用@字符:
@"A short list: item 1 item 2"
一字不变的字符串在文件名中非常有用,因为文件名中大量使用了反斜杠字符。如果使用一般字符串,就必须在字符串中使用两个反斜杠,例如:
"C:\\Temp\\MyDir\\MyFile.doc"
而有了一字不变的字符串字面值,这段代码就更便于阅读。下面的字符串与上面的等价:
@"C:\Temp\MyDir\MyFile.doc"
注意:从本书的后面可以看出,字符串是引用类型,而本章中的其他类型都是值类型。所以,字符串也可以被赋予null值,表示字符串变量不引用字符串(或其他任何东西)。