第4章
C#程序开发基础——数据类型与运算符

学习指引

计算机是用什么来存储所使用的数据?这个问题用一句比较笼统的话就可以概括,那就是:计算机使用内存来记忆计算时所使用的数据。

现实生活中的数据各种各样,有整数、小数、字符串、字符等,它们的类型是不一样的,所以用户要想在计算机中使用这些不同类型的数据,就必须在内存中为它申请一块合适的空间。而这些数据之间是可以通过运算符进行运算的,运算符结合一个或者一个以上的操作数,便形成了表达式,并返回运算结果。本章详细地介绍数据类型和运算符的使用。

重点导读

  •  熟悉C#的数据类型。
  •  掌握C#的数据类型转换。
  •  熟悉其他常用类型。
  •  掌握C#的运算符。

4.1 数据类型概述

在C#中有两种基本类型,分别是值类型和引用类型。而值类型还可以细分为内置值类型、结构和枚举;引用类型可以细分为类类型、接口以及委托等,如图4-1所示。

1. 值类型

内置的值类型就是整型、浮点型和bool类型等,而结构是一种特殊的值类型,它是抽象类型System.ValueType的直接派生类,而System.ValueType本身又是直接从System.Object派生的,所以结构体拥有值类型所有的特权和限制。

如果需得到一个类型或一个变量在特定平台上的准确空间大小,可以使用sizeof方法。表达式sizeof(type)可以产生以字节为单位的存储对象或类型的存储空间大小。

图4-1 基本数据类型

例4-1】编写程序,输出5个常见值类型的存储空间大小。

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

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

【程序分析】本例通过使用sizeof方法,计算出byte、char、short、int和double类型在内存中的尺寸。由于sizeof方法计算出的结果是以字节为单位,所以byte是8位,刚好是1个字节,因此输出的结果是1;char是16位,刚好是2个字节;int和short都是32位,刚好是4个字节;最后的double是64位,刚好是8个字节。所以,这5个值类型的存储空间分别为1、2、4、4、8。

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

图4-2 值类型的宽度

2. 引用类型

引用类型变量又称为对象,可存储对实际数据的引用。内置的引用类型有对象类型、动态类型和字符串类型。

(1)对象类型的关键字是object,也就是System.Object的别名。在C#的统一类型系统中,所有类型都是直接或间接从Object继承的。可以将任何类型的值赋给object类型的变量。但是,在分配值之前,需要先进行类型转换。

例4-2】编写程序,定义object类型的变量用于接收任何数据类型的值。

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

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

【程序分析】本例演示了object类型的变量如何在.NET Framework中使用Object的方法。在代码中首先定义四个object类型的变量,分别为age、name、height和sex;然后分别赋予整型值、字符串值、浮点值和字符值;最后进行输出。

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

图4-3 object类型

(2)动态类型的关键字是dynamic,该类型的作用是绕过编译时类型检查,改为在运行时解析这些操作。dynamic类型简化了对COM AP(I例如Office Automation API)、动态API(例如IronPython库)和HTML文档对象模型(DOM)的访问。

在大多数情况下,dynamic类型与object类型的行为类似。但是,如果操作包含dynamic类型的表达式,那么不会通过编译器对该操作进行解析或类型检查。编译器将该操作有关信息打包在一起,之后这些信息会用于在运行时评估操作。在此过程中,dynamic类型的变量会编译为object类型的变量。因此,dynamic类型只在编译时存在,在运行时则不存在。

例4-3】编写程序,对dynamic类型的变量与object类型的变量进行对比。

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

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

【程序分析】本例演示了dynamic的用法。在代码中首先定义dynamic类型的变量dyn和object类型的变量obj,并对它们都赋值为1;然后使用GetType()方法输出代码编译时两个变量的数据类型。

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

图4-4 dynamic类型

若要查看编译时dyn与obj之间的区别,需要在例4-3的声明和WriteLine语句之间添加下列两行语句:

   dyn = dyn + 5;
   obj = obj + 5;

再次运算程序时,编译器会对表达式“obj + 5”发出错误提示:运算符“+”无法应用于“object”和“int”类型的操作数。但是,对于“dyn + 5”不会报告任何错误。在编译时不会检查包含dyn的表达式,原因是dyn的类型为dynamic。

(3)字符串类型的关键字是string,也就是System.String的另外一个名字。string类型是一个字符序列(0个或更多的Unicode字符)。“+”运算符用于连接两个或更多的字符串。

例如:

   string str = "Hello" + " World";  //输出Hello World

3. 装箱和拆箱

装箱和拆箱是值类型和引用类型之间相互转换时要执行的操作。

(1)装箱在值类型向引用类型转换时发生。

例如:

   object obj = 1;

这行语句将整型常量1赋给object类型的变量obj;众所周知,常量1是值类型,值类型是要放在栈上的,而object是引用类型,它需要放在堆上;所以,执行装箱操作时不可避免地要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,这肯定是要消耗内存和CPU资源的。

(2)拆箱在引用类型向值类型转换时发生。

例如:

   object obj = 5;
   int value = (int)obj;

这两行代码会执行一次装箱操作将整型数字常量5装箱成引用类型object变量obj;然后又执行一次拆箱操作,将存储到堆上的引用变量obj存储到局部整型变量value中。拆箱操作的执行过程和装箱操作过程正好相反,是将存储在堆上的引用类型值转换为值类型并赋给值类型变量。