17xie > C# 2005 & .NET 3.0高级编程(第5版) > 2.4 预定义数据类型
背景:                 
[本书目录] [图书首页] [本书讨论区]  
链接地址:http://www.17xie.com/read-58396.html    注册17xie 一起来写书 实现您的出书梦想!

2.4  预定义数据类型

前面介绍了如何声明变量和常量,下面要详细讨论C#中可用的数据类型。与其他语言相比,C#对其可用的类型及其定义进行了过分的修饰。

2.4.1  值类型和引用类型

在开始介绍C#中的数据类型之前,理解C#把数据类型分为两种是非常重要的:

       值类型

       引用类型

下面几节将详细介绍值类型和引用类型的语法。从概念上看,其区别是值类型直接存储其值,而引用类型存储对值的引用。与其他语言相比,C#中的值类型基本上等价于VBC++中的简单类型(整型、浮点型,但没有指针或引用)。引用类型与VB中的引用类型相同,与C++中通过指针访问的类型类似。

这两种类型存储在内存的不同地方:值类型存储在堆栈中,而引用类型存储在托管堆上。注意区分某个类型是值类型还是引用类型,因为这种存储位置的不同会有不同的影响。例如,int是值类型,这表示下面的语句会在内存的两个地方存储值20

// i and j are both of type int

i = 20;

j = i;

但考虑下面的代码。这段代码假定已经定义了一个类VectorVector是一个引用类型,它有一个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对象。xy都指向包含该对象的内存位置。因为xy是引用类型的变量,声明这两个变量只是保留了一个引用——而不会实例化给定类型的对象。这与在C++中声明指针和VB中的对象引用是相同的——在C++VB中,都不会创建对象。要创建对象,就必须使用new关键字,如上所示。因为xy引用同一个对象,所以对x的修改会影响y,反之亦然。因此上面的代码会显示3050

注意:

C++开发人员应注意,这个语法类似于引用,而不是指针。我们使用.(句点)符号,而不是->来访问对象成员。在语法上,C#引用看起来更类似于C++引用变量。但是,抛开表面的语法,实际上它类似于C++指针。

如果变量是一个引用,就可以把其值设置为null,表示它不引用任何对象:

y = null;

这类似于Java中把引用设置为nullC++中把指针设置为NULL,或VB中把对象引用设置为Nothing。如果将引用设置为null,显然就不可能对它调用任何非静态的成员函数或字段,这么做会在运行时抛出一个异常。

在像C++这样的语言中,开发人员可以选择是直接访问某个给定的值,还是通过指针来访问。VB的限制更多:COM对象是引用类型,简单类型总是值类型。C#在这方面类似于VB:变量是值还是引用仅取决于其数据类型,所以,int总是值类型。不能把int变量声明为引用(在第5章介绍装箱时,可以在类型为object的引用中封装值类型)

C#中,基本数据类型如boollong都是值类型。如果声明一个bool变量,并给它赋予另一个bool变量的值,在内存中就会有两个bool值。如果以后修改第一个bool变量的值,第二个bool变量的值也不会改变。这些类型是通过值来复制的。

相反,大多数更复杂的C#数据类型,包括我们自己声明的类都是引用类型。它们分配在堆中,其生存期可以跨多个函数调用,可以通过一个或几个别名来访问。CLR执行一种精细的算法,来跟踪哪些引用变量仍是可以访问的,哪些引用变量已经不能访问了。CLR会定期进行清理,删除不能访问的对象,把它们占用的内存返回给操作系统。这是通过垃圾收集器实现的。

把基本类型(intbool)规定为值类型,而把包含许多字段的较大类型(通常在有类的情况下)规定为引用类型,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个是引用类型(stringobject)

2.4.3  预定义的值类型

内置的值类型表示基本数据类型,例如整型和浮点类型、字符类型和bool类型。

1. 整型

C#支持8个预定义整数类型,如表2-1所示。

  2-1

   

CTS

   

   

sbyte

System.SByte

8位有符号的整数

–128 127 (–2727–1)

short

System.Int16

16位有符号的整数

–32 768 32 767 (–215215–1)

int

System.Int32

32位有符号的整数

–2 147 483 648 2 147 483 647(–231231–1)

long

System.Int64

64位有符号的整数

–9 223 372 036 854 775 8089 223 372 036 854 775 807(–263263–1)

byte

System.Byte

8位无符号的整数

0255(028–1)

ushort

System.Uint16

16位无符号的整数

065535(0216–1)

uint

System.Uint32

32位无符号的整数

04 294 967 295(0232–1)

ulong

System.Uint64

64位无符号的整数

018 446 744 073 709 551 615(0264–1)

 

Windows的将来版本将支持64位处理器,可以把更大的数据块移入移出内存,获得更快的处理速度。因此,C#支持864位的有符号和无符号的整数。

当然,VB开发人员会发现有许多类型名称是新的。C++Java开发人员应注意:一些C#类型名称与C++Java类型一致,但类型有不同的定义。例如,在C#中,int总是32位带符号的整数,而在C++中,int是带符号的整数,但其位数取决于平台(Windows上是32)。在C#中,所有的数据类型都以与平台无关的方式定义,以备将来C#.NET迁移到其他平台上。

byte0~255(包括255)的标准8位类型。注意,在强调类型的安全性时,C#认为byte类型和char类型完全不同,它们之间的编程转换必须显式写出。还要注意,与整数中的其他类型不同,byte类型在默认状态下是无符号的,其有符号的版本有一个特殊的名称sbyte

.NET中,short不再很短,现在它有16位,Int类型更长,有32位。 long类型最长,有64。所有整数类型的变量都能赋予10进制或16进制的值,后者需要0x前缀:

long x = 0x12ab;

如果对一个整数是intuintlong或是ulong没有任何显式的声明则该变量默认为int类型。为了把键入的值指定为其他整数类型,可以在数字后面加上如下字符:

uint ui = 1234U;

long l = 1234L;

ulong ul = 1234UL;

也可以使用小写字母ul,但后者会与整数1混淆。

2. 浮点类型

C#提供了许多整型数据类型,也支持浮点类型,如表2-2所示。CC++程序员很熟悉     它们。

 

 

  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.
Decimal

128位高精度十进制数表示法

28

±1.0×10-28±7.9 × 1028

 

CTSC#一个重要的优点是提供了一种专用类型表示财务计算,这就是decimal类型,使用decimal类型提供的28位的方式取决于用户。换言之,可以用较大的精确度(带有美分)来表示较小的美元值,也可以在小数部分用更多的舍入来表示较大的美元值。但应注意,decimal类型不是基本类型,所以在计算时使用该类型会有性能损失。

要把数字指定为decimal类型,而不是double float或整型,可以在数字的后面加上字符M(m),如下所示。

4. bool类型

C# bool 类型用于包含booltruefalse如表2-4所示。

  2-4

     

CTS   

bool

System.Boolean

truefalse

 

bool值和整数值不能相互转换。如果变量(或函数的返回类型)声明为bool类型,就只能使用值truefalse。如果试图使用0表示false,非0值表示true,就会出错。

5. 字符类型

为了保存单个字符的值C#支持char数据类型如表2-5所示。

  2-5

    

CTS   

char

System.Char

表示一个16位的(Unicode)字符

 

虽然这个数据类型在表面上类似于CC++中的char类型但它们有重大区别。C++char表示一个8位字符,而C#char包含16位。其部分原因是不允许在char类型与8byte类型之间进行隐式转换。

尽管8位足够编码英语中的每个字符和数字0~9了,但它们不够编码更大的符号系统中的每个字符(例如中文)。为了面向全世界,计算机行业正在从8位字符集转向16位的Unicode模式,ASCII编码是Unicode的一个子集。

char类型的字面量是用单引号括起来的,例如'A'。如果把字符放在双引号中,编译器会把它看作是字符串,从而产生错误。

除了把char表示为字符字面量之外,还可以用416进制的Unicode(例如'\u0041'),带有数据类型转换的整数值(例如(char)65),或16进制数('\x0041')表示它们。它们还可以用转义序列表示,如表2-6所示。

  2-6

   

\'

单引号

\"

双引号

\\

反斜杠

\0

\a

警告

\b

退格

\f

换页

\n

换行

\r

回车

\t

水平制表符

\v

垂直制表符

 

C++开发人员应注意,因为C#本身有一个string类型,所以不需要把字符串表示为char类型的数组。

2.4.4  预定义的引用类型

C#支持两个预定义的引用类型,如表2-7所示。

 

  2-7

   

CTS

   

object

System.Object

根类型,CTS中的其他类型都是从它派生而来的(包括值类型)

string

System.String

Unicode字符串

1. object类型

许多编程语言和类结构都提供了根类型,层次结构中的其他对象都从它派生而来。C#.NET也不例外。在C#中,object类型就是最终的父类型,所有内在和用户定义的类型都从它派生而来。这是C#的一个重要特性,它把C#VBC++区分开来,但其行为与Java非常类似。所有的类型都隐含地最终派生于System.Object类,这样,object类型就可以用于两个目的:

       可以使用object引用绑定任何子类型的对象。例如,第5章将说明如何使用object类型把堆栈中的一个值对象装箱,再移动到堆中。对象引用也可以用于反射,此时必须有代码来处理类型未知的对象。这类似于C++中的void指针或VB中的Variant数据类型。

       object类型执行许多基本的一般用途的方法,包括Equals()GetHashCode()GetType()ToString()。用户定义的类需要使用一种面向对象技术—— 重写(见第4),提供其中一些方法的替代执行代码。例如,重写ToString()时,要给类提供一个方法,提供类本身的字符串表示。如果类中没有提供这些方法的实现代码,编译器就会使用object类型中的实现代码,它们在类中的执行不一定正确。

后面的章节将详细讨论object类型。

2. string类型

CC++开发经验的人员可能在使用C风格的字符串时不太顺利。CC++字符串不过是一个字符数组,因此客户机程序员必须做许多工作,才能把一个字符串复制到另一个字符串上,或者连接两个字符串。实际上,对于一般的C++程序员来说,执行包装了这些操作细节的字符串类是一个非常头痛的耗时过程。VB程序员的工作就比较简单,只需使用string类型即可。而Java程序员就更幸运了,其String类在许多方面都类似于C#字符串。

C#string关键字,在翻译为.NET类时,它就是System.String。有了它,像字符串连接和字符串复制这样的操作就很简单了:

string str1 = "Hello ";

string str2 = "World";

string str3 = str1 + str2; // string concatenation

尽管这是一个值类型的赋值,但string是一个引用类型。String对象保留在堆上,而不是堆栈上。因此,当把一个字符串变量赋给另一个字符串时,会得到对内存中同一个字符串的两个引用。但是,string与引用类型在常见的操作上有一些区别。例如,修改其中一个字符串,就会创建一个全新的string对象,而另一个字符串没有改变。考虑下面的代码:

using System;

 

class StringExample

{

   public static int Main()

   {

      string s1 = "a string";

      string s2 = s1;

      Console.WriteLine("s1 is " + s1);

      Console.WriteLine("s2 is " + s2);

      s1 = "another string";

      Console.WriteLine("s1 is now " + s1);

      Console.WriteLine("s2 is now " + s2);

      return 0;

   }

}

其输出结果为:

s1 is a string

s2 is a string

s1 is now another string

s2 is now a string

换言之,改变s1的值对s2没有影响,这与我们期待的引用类型正好相反。当用值"a string"初始化s1时,就在堆上分配了一个string对象。在初始化s2时,引用也指向这个对象,所以s2的值也是"a string"。但是现在要改变s1的值,而不是替换原来的值时,堆上就会为新值分配一个新对象。s2变量仍指向原来的对象,所以它的值没有改变。这实际上是运算符重载的结果,运算符重载详见第5章。基本上,string类实现为其语义遵循一般的、直观的字符串规则。

字符串字面量放在双引号中("...");如果试图把字符串放在单引号中,编译器就会把它当作char,从而引发错误。C#字符串和char一样,可以包含Unicode16进制数转义序列。因为这些转义序列以一个反斜杠开头,所以不能在字符串中使用这个非转义的反斜杠字符。而需要用两个反斜杠字符(\\)来表示它:

string filepath = "C:\\ProCSharp\\First.cs";

即使用户相信自己可以在任何情况下都记住要这么做,但键入两个反斜杠字符会令人迷惑。幸好,C#提供了另一种替代方式。可以在字符串字面量的前面加上字符@,在这个字符后的所有字符都看作是其原来的含义——它们不会解释为转义字符:

string filepath = @"C:\ProCSharp\First.cs";

甚至允许在字符串字面量中包含换行符:

string jabberwocky = @"'Twas brillig and the slithy toves

Did gyre and gimble in the wabe.";

那么jabberwocky的值就是:

'Twas brillig and the slithy toves

Did gyre and gimble in the wabe.


字数:8720    最后更新:7个月以前 [04-10 20:53]happyskynet 修改
本页编辑者:happyskynet  
[前一页]:2.3 变量  [后一页]:2.5 流控制
[在本页中加入书签] [收藏本书] [推荐本书]
  17xie论坛 > 本书讨论区 > 本页评论   (共0条)
发表评论

用户名称 匿名发表
评论内容
验证码

关于我们 | 版权声明 | 免责声明 | 诚聘英才 | 联系我们 | 合作伙伴 | 友情链接 | 广告合作 | 提交意见
Copyright © 2007 17xie.com 互联网协同写书平台 京ICP备08002671号