2.4 预定义数据类型
前面介绍了如何声明变量和常量,下面要详细讨论C#中可用的数据类型。与其他语言相比,C#对其可用的类型及其定义进行了过分的修饰。
2.4.1 值类型和引用类型
在开始介绍C#中的数据类型之前,理解C#把数据类型分为两种是非常重要的:
● 值类型
● 引用类型
下面几节将详细介绍值类型和引用类型的语法。从概念上看,其区别是值类型直接存储其值,而引用类型存储对值的引用。与其他语言相比,C#中的值类型基本上等价于VB或C++中的简单类型(整型、浮点型,但没有指针或引用)。引用类型与VB中的引用类型相同,与C++中通过指针访问的类型类似。
这两种类型存储在内存的不同地方:值类型存储在堆栈中,而引用类型存储在托管堆上。注意区分某个类型是值类型还是引用类型,因为这种存储位置的不同会有不同的影响。例如,int是值类型,这表示下面的语句会在内存的两个地方存储值20:
// i and j are both of type int
i = 20;
j = i;
但考虑下面的代码。这段代码假定已经定义了一个类Vector,Vector是一个引用类型,它有一个int类型的成员变量Value:
Vector x, y
x = new Vector ();
x.Value = 30; // Value is a field defined in Vector class
y = x;
Console.WriteLine(y.Value);
y.Value = 50;
Console.WriteLine(x.Value);
要理解的重要一点是在执行这段代码后,只有一个Vector对象。x和y都指向包含该对象的内存位置。因为x和y是引用类型的变量,声明这两个变量只是保留了一个引用——而不会实例化给定类型的对象。这与在C++中声明指针和VB中的对象引用是相同的——在C++和VB中,都不会创建对象。要创建对象,就必须使用new关键字,如上所示。因为x和y引用同一个对象,所以对x的修改会影响y,反之亦然。因此上面的代码会显示30和50。
注意:
C++开发人员应注意,这个语法类似于引用,而不是指针。我们使用.(句点)符号,而不是->来访问对象成员。在语法上,C#引用看起来更类似于C++引用变量。但是,抛开表面的语法,实际上它类似于C++指针。
如果变量是一个引用,就可以把其值设置为null,表示它不引用任何对象:
这类似于Java中把引用设置为null,C++中把指针设置为NULL,或VB中把对象引用设置为Nothing。如果将引用设置为null,显然就不可能对它调用任何非静态的成员函数或字段,这么做会在运行时抛出一个异常。
在像C++这样的语言中,开发人员可以选择是直接访问某个给定的值,还是通过指针来访问。VB的限制更多:COM对象是引用类型,简单类型总是值类型。C#在这方面类似于VB:变量是值还是引用仅取决于其数据类型,所以,int总是值类型。不能把int变量声明为引用(在第5章介绍装箱时,可以在类型为object的引用中封装值类型)。
在C#中,基本数据类型如bool和long都是值类型。如果声明一个bool变量,并给它赋予另一个bool变量的值,在内存中就会有两个bool值。如果以后修改第一个bool变量的值,第二个bool变量的值也不会改变。这些类型是通过值来复制的。
相反,大多数更复杂的C#数据类型,包括我们自己声明的类都是引用类型。它们分配在堆中,其生存期可以跨多个函数调用,可以通过一个或几个别名来访问。CLR执行一种精细的算法,来跟踪哪些引用变量仍是可以访问的,哪些引用变量已经不能访问了。CLR会定期进行清理,删除不能访问的对象,把它们占用的内存返回给操作系统。这是通过垃圾收集器实现的。
把基本类型(如int和bool)规定为值类型,而把包含许多字段的较大类型(通常在有类的情况下)规定为引用类型,C#设计这种方式的原因是可以得到最佳性能。如果要把自己的类型定义为值类型,就应把它声明为一个结构。
2.4.2 CTS类型
如第1章所述,C#认可的基本预定义类型并没有内置于语言中,而是内置于.NET Framework中。例如,在C#中声明一个int类型的数据时,声明的实际上是.NET结构System.Int32的一个实例。这听起来似乎很深奥,但其意义深远:这表示在语法上,可以把所有的基本数据类型看作是支持某些方法的类。例如,要把int i转换为string,可以编写下面的代码:
string s = i.ToString();
应强调的是,在这种便利语法的背后,类型实际上仍存储为基本类型。基本类型在概念上用.NET结构表示,所以肯定没有性能损失。
下面看看C#中定义的类型。我们将列出每个类型,以及它们的定义和对应.NET类型(CTS 类型)的名称。C#有15个预定义类型,其中13个是值类型,2个是引用类型(string和object)。
2.4.3 预定义的值类型
内置的值类型表示基本数据类型,例如整型和浮点类型、字符类型和bool类型。
1. 整型
C#支持8个预定义整数类型,如表2-1所示。
表 2-1
|
名 称 |
CTS 类 型 |
说 明 |
范 围 |
|
sbyte |
System.SByte |
8位有符号的整数 |
–128 到 127 (–27到27–1) |
|
short |
System.Int16 |
16位有符号的整数 |
–32 768 到 32 767 (–215到215–1) |
|
int |
System.Int32 |
32位有符号的整数 |
–2 147 483 648 到 2 147 483 647(–231到231–1) |
|
long |
System.Int64 |
64位有符号的整数 |
–9 223 372 036 854 775 808到9 223 372 036 854 775 807(–263到263–1) |
|
byte |
System.Byte |
8位无符号的整数 |
0到255(0到28–1) |
|
ushort |
System.Uint16 |
16位无符号的整数 |
0到65535(0到216–1) |
|
uint |
System.Uint32 |
32位无符号的整数 |
0到4 294 967 295(0到232–1) |
|
ulong |
System.Uint64 |
64位无符号的整数 |
0到18 446 744 073 709 551 615(0到264–1) |
Windows的将来版本将支持64位处理器,可以把更大的数据块移入移出内存,获得更快的处理速度。因此,C#支持8至64位的有符号和无符号的整数。
当然,VB开发人员会发现有许多类型名称是新的。C++和Java开发人员应注意:一些C#类型名称与C++和Java类型一致,但类型有不同的定义。例如,在C#中,int总是32位带符号的整数,而在C++中,int是带符号的整数,但其位数取决于平台(在Windows上是32位)。在C#中,所有的数据类型都以与平台无关的方式定义,以备将来C#和.NET迁移到其他平台上。
byte是0~255(包括255)的标准8位类型。注意,在强调类型的安全性时,C#认为byte类型和char类型完全不同,它们之间的编程转换必须显式写出。还要注意,与整数中的其他类型不同,byte类型在默认状态下是无符号的,其有符号的版本有一个特殊的名称sbyte。
在.NET中,short不再很短,现在它有16位,Int类型更长,有32位。 long类型最长,有64位。所有整数类型的变量都能赋予10进制或16进制的值,后者需要0x前缀:
long x = 0x12ab;
如果对一个整数是int、uint、long或是ulong没有任何显式的声明,则该变量默认为int类型。为了把键入的值指定为其他整数类型,可以在数字后面加上如下字符:
uint ui = 1234U;
long l = 1234L;
ulong ul = 1234UL;
也可以使用小写字母u和l,但后者会与整数1混淆。
2. 浮点类型
C#提供了许多整型数据类型,也支持浮点类型,如表2-2所示。C和C++程序员很熟悉 它们。
表 2-2
|
名称 |
CTS类型 |
说 明 |
位 数 |
范围 (大致) |
|
float |
System.Single |
32位单精度浮点数 |
7 |
±1.5 × 10-45 到 ±3.4 × 1038 |
|
double |
System.Double |
64位双精度浮点数 |
15/16 |
±5.0 × 10-324到 ±1.7 × 10308 |
float数据类型用于较小的浮点数,因为它要求的精度较低。double数据类型比float数据类型大,提供的精度也大一倍(15位)。
如果在代码中没有对某个非整数值(如12.3)硬编码,则编译器一般假定该变量是double。如果想指定值为float,可以在其后加上字符F(或f):
float f = 12.3F;
3. decimal类型
另外,decimal类型表示精度更高的浮点数,如表2-3所示。
表 2-3
|
名 称 |
CTS类型 |
说 明 |
位 数 |
范围(大致) |
|
decimal |
System. |
128位高精度十进制数表示法 |
28 |
±1.0×10-28到±7.9 × 1028 |
CTS和C#一个重要的优点是提供了一种专用类型表示财务计算,这就是decimal类型,使用decimal类型提供的28位的方式取决于用户。换言之,可以用较大的精确度(带有美分)来表示较小的美元值,也可以在小数部分用更多的舍入来表示较大的美元值。但应注意,decimal类型不是基本类型,所以在计算时使用该类型会有性能损失。
要把数字指定为decimal类型,而不是double、 float或整型,可以在数字的后面加上字符M(或m),如下所示。
decimal d = 12.30M;






