第二讲 C#语言基础介绍
南京邮电学院 李建忠(cornyfield@263.net)
在体验C#的锐利之前,关乎语言基本知识的掌握是必不可少的一环。由于C#基本语言很多源自C/C++,在这里对那些和C/C++类似的地方仅作简单介绍,我们将体验专注于那些区别于传统C/C++的关键的语言基础知识。
数据类型 C#语言的数据类型主要分为两类:值类型和引用类型。另外一种数据类型"指针"是为unsafe上下文编程专门设定的,其中unsafe上下文指对代码进行unsafe标示以满足利用指针对内存直接进行操作要求的C#非托管代码,这些代码将失去Microsoft.NET平台的垃圾收集等CLR性质,我们放在"COM互操作 非托管编程与异常处理"专题里阐述。值类型的变量本身包含他们的数据,而引用类型的变量包含的是指向包含数据的内存块的引用或者叫句柄。从下面这幅图中可以清晰地看出两者的差别:
引用类型带来的可能的问题便是当多个变量引用同样的内存块时,对任何一个引用变量的修改都会导致该对象的值的改变。null值表示引用类型没有对任何实际地址进行引用。
值类型可分为结构类型和枚举类型。结构类型包括简单类型和用户自定义结构类型。枚举类型和用户自定义结构类型我们将在"第九讲 结构,枚举,数组与字符串"专题里详细阐述。简单类型又可分为布尔类型和数值类型。C#语言中布尔类型严格与数值类型区分,只有true和false两种取值,不存在像C/C++里那样和其他类型之间的转换。数值类型包括整值,浮点和decimal三种类型。整值类型有sbyte,byte,short,ushort,int,uint,long,ulong,char共九种。除了char类型外,其他8种两两一组分别为有符号和无符号两种。浮点值有float和double两种。decimal主要用于金融,货币等对精度要求比较高的计算环境。下表是对这些简单类型的一个详细的描述:
简单类型描 述示 例sbyte
8-bit 有符号整数
sbyte val = 12;
short
16-bit 有符号整数
short val = 12;
int
32-bit有符号整数
int val = 12;
long
64-bit有符号整数
long val1 = 12; long val2 = 34L;
byte
8-bit无符号整数
byte val1 = 12; byte val2 = 34U;
ushort
16-bit 无符号整数
ushort val1 = 12; ushort val2 = 34U;
uint
32-bit 无符号整数
uint val1 = 12; uint val2 = 34U;
ulong
64-bit 无符号整数
ulong val1 = 12; ulong val2 = 34U; ulong val3 = 56L; ulong val4 = 78UL;
float
32-bit单精度浮点数
float val = 1.23F;
double
64-bit双精度浮点数
double val1 = 1.23; double val2 = 4.56D;
l
布尔类型
bool val1 = true; bool val2 = false;
char
字符类型 ,Unicode 编码
char val = 'h';
decimal
28个有效数字的128-bit十进制类型
decimal val = 1.23M;
引用类型共分四种类型:类,接口,数组,委派。类除了我们可以定义自己的类型外,又包括两个比较特殊的类型object和string。object是C#中所有类型(包括所有的值类型和引用类型)的继承的根类。string类型是一个密封类型(不能被继承),其实例表示Unicode字符串,它和数组类型我们将放在"第九讲 结构,枚举,数组与字符串"中详述。接口类型定义一个方法的合同,我们将在"第七讲 接口 继承与多态"中讲述。委派类型是一个指向静态或实例方法的签名,类似于C/C++中的函数指针,将在"第八讲 委派与事件"中讲述。实际上我们将从后面的专题中看到这些类型都是类的某种形式的包装。
每种数据类型都有对应的缺省值。数值类型的缺省值为0或0.0,其中char的缺省为'\x0000'。布尔类型的缺省值为false。枚举类型的缺省值为0。结构类型的缺省值是将它所有的值类型的域设置为对应值类型的缺省值,将其引用类型的域设置为null。所有引用类型的缺省值为null。
不同类型的数据之间可以转换,C#的类型转换有隐含转换,明晰转换,标准转换,自定义转换共四种方式。隐含转换与明晰转换和C++里一样,数据从"小类型"到"大类型"的转换时为隐含转换,从"大类型"到"小类型"的转换为明晰转换,明晰转换需要如"(Type)data"一般的括号转换操作符。标准转换和自定义转换是针对系统内建转换和用户定义的转换而言的,两者都是对类或结构这样的自定义类型而言的。
变量与常量 变量表示存储位置,变量必须有确定的数据类型。C#的类型安全的含义之一就是确保变量的存储位置容纳着合适的类型。可以将C#中的变量分为静态变量,实例变量,传值参数,引用参数,输出参数,数组参数和本地变量共七种。本地变量则是在方法体内的临时变量。 <
静态变量和实例变量主要是针对类或结构内的数据成员(又叫域)而言的。静态变量在它寄存的类或结构类型被装载后得到存储空间,如果没有对它进行初始化赋值,静态变量的初始值将是它的类型所持有的缺省值。实例变量在它的类实例被创建后获得存储空间,如果没有经过初始化赋值,它的初始值与静态变量的定义相同。两者更详细的说明我们放在"第六讲 域 方法 属性与索引器"专题里。
传值参数,引用参数,输出参数,数组参数主要针对方法的参数类型而言的。简单的讲传值参数是对变量的值的一种传递,方法内对变量的改变在方法体外不起作用。对于传值参数本身是引用型的变量稍有不同,方法内对该引用(句柄)变量指向的数据成员即实际内存块的改变将在方法体外仍然保留改变,但对于引用(句柄)本身的改变不起作用。引用参数是对变量的句柄的一种传递,方法内对该变量的任何改变都将在方法体外保留。输出参数是C#专门为有多个返回值的方法而量身定做的,它类似于引用变量,但可以在进入方法体之前不进行初始化,而其他的参数在进入方法体内C#都要求明确的初始化。数组参数是为传递大量的数组元素而专门设计的,它从本质上讲是一种引用型变量的传值参数。它们更详细的阐述我们也放在"第六讲 域 方法 属性与索引器"专题里。
本地变量严格的讲是在C#的块语句,for语句,switch语句,using语句内声明的变量,它的生命周期严格地被限制在这些语句块内部。
常量在编译时便确定它的值,在整个程序中也不许修改。常量声明的同时必须赋值。由于它的编译时确定值的特性,引用类型可能的值只能为string和null(除string外,引用类型的构建器必须在运行时才能确定引用类型的值)。
操作符与表达式 C#保留了C++所有的操作符,其中指针操作符(*和->)与引用操作符(&)需要有unsafe的上下