C# 从入门到项目实践(超值版)
上QQ阅读APP看书,第一时间看更新

3.3 变量的类型

C#中的变量根据定义可以分为值类型和引用类型。这两种类型的变量,差异在于数据的存储方式,值类型本身可以直接存储数据,而引用类型则存储实际数据的引用,程序通过此引用找到真正的数据。

3.3.1 值类型

C#中值类型的变量主要包括整数类型、浮点类型、decimal和bool类型等。值类型的变量都在堆栈中进行分配,因此效率很高。所以使用值类型的主要目的是为了提高性能。

值类型变量具有以下特点。

(1)值类型变量都存储在堆栈中。

(2)值类型变量不能包含null值。但是必须具有一个确定的值。

(3)复制值类型变量时,复制的是变量的值,而不是变量的地址。

(4)在访问值类型变量时,一般都是访问其实例。

(5)每个值类型变量都有自己的数据副本,因此对一个值类型变量的操作不会影响到其他变量。

注意:所有值类型都隐式派生自System.ValueType。而且不能从值类型再派生新类型。

1. 整数类型

整数类型代表的就是一个整数。当用户声明一个int类型时,系统就会分配内存来存储值。表3-2显示了整数类型的大小和范围。

表3-2 整数类型

注意:如果由整数类型所表示的值超出了ulong的范围,则将出现编译错误。

例3-6】编写程序,声明一个sbyte类型的变量和int类型的变量并输出。

(1)在Visual Studio 2017中,新建名称为“Project6”的文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本例演示了整型数据的输出。在代码中,定义了一个sbyte类型的变量x,并赋值为-100,该类型的数值范围为-128~127,如果超出这个范围,编译器就会出现错误提示;而定义int类型的y,其数值范围就比较大,所以赋值为255编译器不会报错。

在Visual Studio 2017中的运行结果如图3-6所示。

2. 浮点类型

浮点类型的变量主要是对带有小数点的数据进行处理。表3-3列出了浮点类型的精度和大致范围。

图3-6 整数类型

表3-3 浮点类型

注意:在使用float类型的变量时,必须在数值的后面跟随f或F,否则编译器会发出错误提示:无法将double类型隐式转换为float类型;请使用“F”后缀创建此类型。也可以在double类型的值前面加上(f loat)对其进行强制类型转换。

例3-7】编写程序,将数值强制指定为float和double类型。

(1)在Visual Studio 2017中,新建名称为“Project7”的文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本例演示了浮点型变量的输出。在代码中,首先定义两个变量f和d,f为float类型,d为double类型,并为它们赋值(由于变量f的精度是7位,所以最后输出了7位有效数字,最后一位进行了四舍五入。而变量d的精度是15~16位,所以最后输出了完整的数值);接着再定义两个double类型的变量x和y并赋值;最后通过强制类型转换和后缀类型指定的方法,将这两个数值指定为f loat类型,所以最后只输出了前7位有效数值。

在Visual Studio 2017中的运行结果如图3-7所示。

图3-7 浮点类型

3. decimal类型

decimal关键字指示128位数据类型。与其他浮点型相比,decimal类型具有更高的精度和更小的范围,这使它适合于财务和货币计算。decimal类型的大致范围和精度如表3-4所示。

表3-4 decimal类型

注意:decimal的默认值为0m。如果想要使用decimal类型的变量,必须使用后缀m或M。

例如:

   decimal myMoney = 100.5m;

如果没有后缀m,则数字将被视为double类型,但是会生成编译器错误。

4. bool类型

布尔类型是用来表示“真”和“假”这两个概念的。这虽然看起来很简单,但实际应用非常广泛。众所周知,计算机实际上就是用二进制来表示各种数据的,即不管何种数据,在计算机内部都是采用二进制方式处理和存储的。布尔类型表示的逻辑变量只有两种取值:“真”和“假”。在C#中,分别采用true和false两个值来表示。

注意:在C和C++中,用0来表示“假”,其他任何非0的式子都表示“真”。这种不正规的表达在C#中已经被废弃了。在C#中,true值不能被其他任何非0值所代替。在其他整数类型和布尔类型之间不再存在任何转换,将整数类型转换成布尔类型是不合法的。

   bool x=1 //错误,不存在这种写法。只能写成x=true或x=false

bool关键字是System.Boolean的别名。可以将布尔值赋给bool变量,也可以将计算结果为bool类型的表达式赋给bool变量。

例3-8】编写程序,判断一个整数是奇数还是偶数。

(1)在Visual Studio 2017中,新建名称为“Project8”的文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本例演示了bool型变量的输出。在代码中首先定义bool类型的变量b,并为其赋值为true;然后再定义一个int类型的变量value,并赋值为51;接着将value能否被2整除的bool值,赋给变量b;最后通过if语句对b进行判断,如果能被整除说明是偶数,否则就是奇数。

在Visual Studio 2017中的运行结果如图3-8所示。

图3-8 bool类型

5. 字符类型

除了数字以外,计算机处理的信息,主要就是字符了。字符包括数字字符、英文字母、表达符号等,C#提供的字符类型按照国际上公认的标准,采用Unicode字符集。一个Unicode的标准字符长度为16位,用它可以表示世界上大多数语言。

例如,给一个字符变量赋值:

   char c='A';

另外,用户还可以直接通过十进制转义符(前缀\x)或Unicode表示法给字符型变量赋值(前缀\u),如下面对字符型变量的赋值写法都是正确的:

   char a='\x0032';
   char b='\u0032';

注意:在C和C++中,字符型变量的值是该变量所代表的ASCII码,字符型变量的值作为整数的一部分,可以对字符型变量使用整数进行赋值和运算。而这在C#中是被禁止的。

3.3.2 引用类型

C#的另一大数据类型是引用类型。“引用”这个词在这里的含义是,该类型的变量不直接存储包含的值,而是指向它所要存储的值。也就是说,引用类型存储实际数据的引用值的地址。

引用类型具有如下特征。

(1)必须使用new关键字来创建引用类型的变量。

(2)必须在托管堆中为引用类型变量分配内存。

(3)引用类型变量是由垃圾回收机制来管理的。

(4)引用类型被赋值前的值都是null。

(5)在托管堆中分配每个对象都有与之相关联的附加成员,这些成员必须被初始化。

(6)多个引用类型变量可以引用同一对象,在这种情形下,对一个变量执行的操作会影响另一个变量所引用的对象。

在C#中所有的类都是引用类型,主要包括类、委托、数组和接口。

例3-9】编写程序,输出值类型和引用类型的变量。

① 在Visual Studio 2017中,新建名称为“Project9”的文件。

② 在代码编辑区域输入以下代码。

【程序分析】本例演示了引用类型变量的输出。由于引用类型的变量存储是对其数据(对象)的引用,而值类型的变量直接包含其数据,对于引用类型,两种变量可引用同一对象,因此,对一个变量执行的操作会影响另一个变量所引用的对象;对于值类型,每个变量都具有其自己的数据副本,对一个变量执行的操作不会影响另一个变量。所以,变量a的值没有受到变量b的影响,最后输出a与b的值不相同。

在Visual Studio 2017中的运行结果如图3-9所示。

图3-9 引用类型

3.3.3 值类型与引用类型的区别

C#里面把数据类型分为两大类,分别为值类型和引用类型。值类型包括基本数据类型(int、char、double等)、结构和枚举。引用类型包括接口、数组、类等。下面列出了这两大类的区别。

1. 值类型

值类型就是一个包含实际数据的量。即当定义一个值类型的变量时,C#会根据它所声明的类型,以栈方式分配一块大小相适应的存储区域给这个变量,随后对这个变量的读或写操作就直接在这块内存区域进行。

例如:

   int a = 10;
   int b = a;
   b = 9;

变量的存储形式如图3-10所示。

图3-10 值类型的存储

2. 引用类型

一个引用类型的变量不存储它们所代表的实际数据,而是存储实际数据的引用。引用类型分两步创建,首先在栈上创建一个引用变量,然后在堆上创建对象本身,再把这个内存的句柄(也是内存的首地址)赋给引用变量。

例如:

这段代码表示通过类Myclass创建了两个对象a和b,然后使用new为a在堆上分配空间。语句“b = a;”表示把b指向a,也就是说两者在栈中都存的是同一个引用,当b.value改变时,a里面的值也会改变,变量的存储形式如图3-11所示。

图3-11 引用类型的存储

3.3.4 枚举类型

枚举类型也称为枚举,用于定义一组在逻辑上密不可分的整数值提供便于记忆的符号。枚举类型使用enum关键字声明。

声明枚举类型的语法格式如下:

其中,大括号“{}”中的内容为枚举值列表,每个枚举值都对应一个枚举值名称,value1到valueN为整数,list1到listN为枚举值的标识名称。

例如,声明一个代表星期的枚举类型的变量:

   enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

默认情况下,第一个枚举数的值为0,后面每个枚举数的值依次递增1。因此,Sat是0,Sun是1,Mon是2,以此类推。

枚举类型的变量也可用初始值来重写默认值。

例如:

   enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

在此枚举中,强制元素序列从1而不是0开始。

注意:

(1)枚举数的名称中不能包含空白。

(2)最好是在命名空间内直接定义枚举,以便该命名空间中的所有类都能够同样方便地访问它。

(3)默认情况下,枚举中每个元素的基础类型是int,也可以使用冒号指定另一种整数值类型。

例如:

   enum Months : byte { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

准许使用的类型有byte、sbyte、short、ushort、int、uint、long或ulong。

例3-10】编写程序,定义一个枚举类型并输出。

(1)在Visual Studio 2017中,新建名称为“Project10”的文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本例演示了枚举类型变量的输出。在代码中首先创建一个Days枚举,该枚举有7个值,分别表示周一到周日;然后通过逗号运算符访问Days枚举的Mon和Fri,并将这两个枚举值分别赋给变量start和end;最后输出变量start和end。

在Visual Studio 2017中的运行结果如图3-12所示。

图3-12 枚举类型

3.3.5 变量的作用域

变量的作用域指的是能够使用该变量的区域。作用域既作用于方法,也作用于变量。一个标识符的作用域是从声明该标识符的那个位置开始的,不管是变量还是方法都应如此。在C#中通常用“{”和“}”来界定变量的作用范围。

变量的作用域有着严格的要求,在声明变量时需要注意两点,首先变量名不要重复,其次声明变量的位置很重要。

例如:

这是一个for循环语句,该语句的作用是循环输出5次变量s的值,而变量s在for循环外则并不能访问到。因为变量s在for循环内部声明,所以只能在for循环内部访问。

如果要让变量s在for循环外面也能访问,那么一开始就要在for循环外声明好变量s。

例如:

由于变量s在for循环的外部进行了声明,所以代码运行不会出现错误。但还需要注意以下两个细节:

(1)在for循环外声明变量s后,如果认为“既然在for循环内声明的变量只有在for循环内有效,那么是否可以在for循环内再写一个int s = 100;”,这样做肯定是错误的。即便作用域不一样,但也无法让for循环内外声明的变量名出现相同的情况。

(2)如果在for循环外声明变量s时没有赋初值,例如上面代码中的“int s = 0;”改成“int s;”,那么虽然在for循环内部给变量s赋值了,但是编译器还会发出错误提示“使用了未赋值的局部变量s”。所以如果要在for循环结构外部使用到这个变量,还是应该在开始就给变量赋初值。