数据类型和变量
一、 C#语言的数据类型主要分为两类:值类型和引用类型。值类型可分为结构类型和枚举类型。结构类型包括简单类型和用户自定义结构类型。简单类型又可分为布尔类型和数值类型。C#语言中布尔类型严格与数值类型区分,只有true和false两种取值,不存在像C/C++里那样和其他类型之间的转换。数值类型包括整值,浮点和decimal三种类型。整值类型有sbyte,byte,short,ushort,int,uint,long,ulong,char共九种。除了char类型外,其他8种两两一组分别为有符号和无符号两种。浮点值有float和double两种。decimal主要用于金融,货币等对精度要求比较高的计算环境。
引用类型共分四种类型:类,接口,数组,委派。类除了我们可以定义自己的类型外,又包括两个比较特殊的类型object和string。object是C#中所有类型(包括所有的值类型和引用类型)的继承的根类。
不同类型的数据之间可以转换,C#的类型转换有隐含转换,明晰转换,标准转换,自定义转换共四种方式。隐含转换与明晰转换和C++里一样,数据从"小类型"到"大类型"的转换时为隐含转换,从"大类型"到"小类型"的转换为明晰转换,明晰转换需要如"(Type)data"一般的括号转换操作符。标准转换和自定义转换是针对系统内建转换和用户定义的转换而言的,两者都是对类或结构这样的自定义类型而言的。
二、变量表示存储位置,变量必须有确定的数据类型。C#的类型安全的含义之一就是确保变量的存储位置容纳着合适的类型。可以将C#中的变量分为静态变量,实例变量,传值参数,引用参数,输出参数,数组参数和本地变量共七种。本地变量则是在方法体内的临时变量。
三、C#保留了C++所有的操作符,其中指针操作符(*和->)与引用操作符(&)需要有unsafe的上下文。C#摈弃了范围辨析操作符(::),一律改为单点操作符(.)。我们不再阐述那些保留的C++的操作符,这里主要介绍C#引入的具有特殊意义的几个操作符:as,is,new, typeof,sizeof,stackalloc。
as操作符用于执行兼容类型之间的转换,当转换失败时,as 操作符结果为null。
is 操作符用于检查对象的运行时类型是否与给定类型兼容,当表达式非null且可以转化为指定类型时,is操作符结果为true,否则为false。as和is操作符是基于同样的类型鉴别和转换而设计的,两者有相似的应用场合。
实际上expression as type相当于expression is type ? (type)expression : (type)null。
四、C#语句可以分为标号语句,声明语句,块语句,空语句,表达式语句,选择语句,反复语句,跳转语句,try语句,checked/unchecked语句,lock语句,using语句。
标号语句主要为goto跳转设计,C#不允许跨方法的跳转,但允许小规模的方法内的跳转。声明语句可以同时进行初始化赋值,对象的实例化声明需要new关键字。块语句采用"{"和"}"定义语句块,主要是界定局部变量的作用范围。空语句在C#中用分号";"表示,没有执行语义。表达式语句通过表达式构成语句。
选择语句有if语句和switch语句两种,与C++别无二致。反复语句除了while,do,for三种循环结构外引入了foreach语句用于遍历集合中所有的元素,但这需要特定的接口支持,我们在后面的章节里对之作详细阐述。
跳转语句有break,continue,goto,return,throw五种语句,前四种与C++里的语义相同,throw语句与后面的try语句我们将在"第十一讲 COM互操作 非托管编程与异常处理"阐述。
checked/unchecked语句主要用于数值运算中溢出检查的上下文。lock语句主要用于线程信号量的锁控制。using语句主要用于片断资源管理。这些我们在后续章节里都会有具体的涉及。
五、可空类型
简单说来,可空数据类型就是包含了所定义的数据类型或者值的空(null)的类型。
定义可空类型和非可空类型基本类似,不同的是采用了?来表示。
如定义一个整型,你可以使用简单的语句:
为了使得myInt能够存储一个空值,你可以这样声明它:
你可以看到,这两个变量看上去好像是一样的。但是,可空类型的版本是非常不同的。可空的版本事实上是一个结构,它将值类型和一个标记该值是否为空的标志位结合在一起。一个可空类型有两个公共可读的属性,HasValue和value。如果存储了一个值那么HasValue这个布尔型变量就为true。否则,如果变量是空值就是false。如果HasValue是true,你可以获取这个变量的值。如下有两个对可空变量的有效赋值:
你可以看到,myDouble被赋值了,但是也可以被赋为空。在第二个语句里,myOtherDouble被初始化一个空值,这在一个非可空类型里不能这样做的。
使用可空类型
可空类型可以像普通值类型一样的使用。事实上,可以使用内建的隐式转换来转换相同类型的可空变量和非可空变量。这意味着你可以在一个标准整型和可空整型之间相互转换:
在以上的语句里,你可以看到如果可空变量不包含空值的话是可以和非可空变量交换值的。如果它是一个空值,那么就会抛出例外。为了防止例外,你可以使用可空变量的HasValue属性:
你可以看到,如果nFirst有值赋值就会发生,否则程序会跳过此句语句。
使用可空类型的操作符
虽然可以使用相同值类型的可空和非可空变量的转换,也必须对操作符进行一些改变使得它们可以处理可空和非可空值。这些操作符被称为提升的操作符。
考虑如下代码:
在ValC里存储了什么?ValC中存储了30。标准操作符被扩展使得它们能够处理可空类型。考虑到如下的变化:
ValC这次值为多少?ValC为空。无论哪个操作数为空,提升的操作符的结果为空。即使进行加法或减法,结果也为空。
如果ValC不为可空类型呢?如下的代码会有什么样的结果?
代码将会抛出一个异常。ValA*ValB结果为空,但是不能赋值为非可空类型,这将会导致程序异常的抛出。
比较
比较将会和数学计算操作类似的方式处理。比较的操作数将同时被提升为可空的。这样就可以比较了,如果某个操作数为空,那么比较结果为false。
如果对比是否相等,两个同为空的变量将被认为是相等的。一个空变量和其他任意值的变量相比的结果是不相等。下面是一些比较的例子:
在所有的比较中,结果都是布尔型值true或者false。在做大小比较的时候,如果操作数的任意一个或者都是空值,那么结果返回的是false。如下展示了一些例子:
可空性的移去
C#在新版本中加入了一个新的操作符,它被称为空接合操作符,使用如下的格式:
这样,如果first不为空,那么它的值将返回作为returnValue的值。如果first为空,那么second的值将被返回。注意:returnValue可以为可空变量或者非可空变量。
如果你希望可空变量的值到一个非可空的版本,你可以这样做:
NewVarA的值将会为123因为ValA不是空值。NewVarb的值是-1因为ValB是空值。你看一看到,这里你将可以将变量从一个空值转化成一个缺省值。这里缺省值是-1。
|
简单类型
|
描 述
|
示 例
|
| 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#中所有类型(包括所有的值类型和引用类型)的继承的根类。
不同类型的数据之间可以转换,C#的类型转换有隐含转换,明晰转换,标准转换,自定义转换共四种方式。隐含转换与明晰转换和C++里一样,数据从"小类型"到"大类型"的转换时为隐含转换,从"大类型"到"小类型"的转换为明晰转换,明晰转换需要如"(Type)data"一般的括号转换操作符。标准转换和自定义转换是针对系统内建转换和用户定义的转换而言的,两者都是对类或结构这样的自定义类型而言的。
二、变量表示存储位置,变量必须有确定的数据类型。C#的类型安全的含义之一就是确保变量的存储位置容纳着合适的类型。可以将C#中的变量分为静态变量,实例变量,传值参数,引用参数,输出参数,数组参数和本地变量共七种。本地变量则是在方法体内的临时变量。
三、C#保留了C++所有的操作符,其中指针操作符(*和->)与引用操作符(&)需要有unsafe的上下文。C#摈弃了范围辨析操作符(::),一律改为单点操作符(.)。我们不再阐述那些保留的C++的操作符,这里主要介绍C#引入的具有特殊意义的几个操作符:as,is,new, typeof,sizeof,stackalloc。
as操作符用于执行兼容类型之间的转换,当转换失败时,as 操作符结果为null。
is 操作符用于检查对象的运行时类型是否与给定类型兼容,当表达式非null且可以转化为指定类型时,is操作符结果为true,否则为false。as和is操作符是基于同样的类型鉴别和转换而设计的,两者有相似的应用场合。
实际上expression as type相当于expression is type ? (type)expression : (type)null。
四、C#语句可以分为标号语句,声明语句,块语句,空语句,表达式语句,选择语句,反复语句,跳转语句,try语句,checked/unchecked语句,lock语句,using语句。
标号语句主要为goto跳转设计,C#不允许跨方法的跳转,但允许小规模的方法内的跳转。声明语句可以同时进行初始化赋值,对象的实例化声明需要new关键字。块语句采用"{"和"}"定义语句块,主要是界定局部变量的作用范围。空语句在C#中用分号";"表示,没有执行语义。表达式语句通过表达式构成语句。
选择语句有if语句和switch语句两种,与C++别无二致。反复语句除了while,do,for三种循环结构外引入了foreach语句用于遍历集合中所有的元素,但这需要特定的接口支持,我们在后面的章节里对之作详细阐述。
跳转语句有break,continue,goto,return,throw五种语句,前四种与C++里的语义相同,throw语句与后面的try语句我们将在"第十一讲 COM互操作 非托管编程与异常处理"阐述。
checked/unchecked语句主要用于数值运算中溢出检查的上下文。lock语句主要用于线程信号量的锁控制。using语句主要用于片断资源管理。这些我们在后续章节里都会有具体的涉及。
五、可空类型
简单说来,可空数据类型就是包含了所定义的数据类型或者值的空(null)的类型。
定义可空类型和非可空类型基本类似,不同的是采用了?来表示。
如定义一个整型,你可以使用简单的语句:
| int myInt = 1; |
为了使得myInt能够存储一个空值,你可以这样声明它:
| int? myNullableInt = 1; |
你可以看到,这两个变量看上去好像是一样的。但是,可空类型的版本是非常不同的。可空的版本事实上是一个结构,它将值类型和一个标记该值是否为空的标志位结合在一起。一个可空类型有两个公共可读的属性,HasValue和value。如果存储了一个值那么HasValue这个布尔型变量就为true。否则,如果变量是空值就是false。如果HasValue是true,你可以获取这个变量的值。如下有两个对可空变量的有效赋值:
| double? myDouble = 3.1415926; double? myOtherDouble = null; |
你可以看到,myDouble被赋值了,但是也可以被赋为空。在第二个语句里,myOtherDouble被初始化一个空值,这在一个非可空类型里不能这样做的。
使用可空类型
可空类型可以像普通值类型一样的使用。事实上,可以使用内建的隐式转换来转换相同类型的可空变量和非可空变量。这意味着你可以在一个标准整型和可空整型之间相互转换:
| int? nFirst = null; int Second = 2; nFirst = Second; // 有效 nFirst = 123; // 有效 Second = nFirst; // 同样有效 nFirst = null; // 有效 Second = nFirst; // 例外,后者是非空类型 |
在以上的语句里,你可以看到如果可空变量不包含空值的话是可以和非可空变量交换值的。如果它是一个空值,那么就会抛出例外。为了防止例外,你可以使用可空变量的HasValue属性:
| if (nFirst.HasValue) Second = nFirst; |
你可以看到,如果nFirst有值赋值就会发生,否则程序会跳过此句语句。
使用可空类型的操作符
虽然可以使用相同值类型的可空和非可空变量的转换,也必须对操作符进行一些改变使得它们可以处理可空和非可空值。这些操作符被称为提升的操作符。
考虑如下代码:
| int ValA = 10; int? ValB = 3; int? ValC = ValA * ValB; |
在ValC里存储了什么?ValC中存储了30。标准操作符被扩展使得它们能够处理可空类型。考虑到如下的变化:
| int ValA = 10; int? ValB = null; int? ValC = ValA * ValB; |
ValC这次值为多少?ValC为空。无论哪个操作数为空,提升的操作符的结果为空。即使进行加法或减法,结果也为空。
如果ValC不为可空类型呢?如下的代码会有什么样的结果?
| int ValA = 10; int? ValB = null; int ValC = ValA * ValB; // ValC 不为可空类型 |
代码将会抛出一个异常。ValA*ValB结果为空,但是不能赋值为非可空类型,这将会导致程序异常的抛出。
比较
比较将会和数学计算操作类似的方式处理。比较的操作数将同时被提升为可空的。这样就可以比较了,如果某个操作数为空,那么比较结果为false。
如果对比是否相等,两个同为空的变量将被认为是相等的。一个空变量和其他任意值的变量相比的结果是不相等。下面是一些比较的例子:
| int abc = 123; int xyz = 890; int? def = null; int? uvw = 123; Comparison Result abc == xyz // false abc == def // false def == null // true abc == uvw // true uvw == null // false uvw != null // true |
在所有的比较中,结果都是布尔型值true或者false。在做大小比较的时候,如果操作数的任意一个或者都是空值,那么结果返回的是false。如下展示了一些例子:
| Comparison Result abc > uvw // false, they are equal abc < def // false, def is null uvw < def // false, because def is null def > null // false, because right side is null uvw > null // false, because right side is null |
可空性的移去
C#在新版本中加入了一个新的操作符,它被称为空接合操作符,使用如下的格式:
| returnValue = first second; |
这样,如果first不为空,那么它的值将返回作为returnValue的值。如果first为空,那么second的值将被返回。注意:returnValue可以为可空变量或者非可空变量。
如果你希望可空变量的值到一个非可空的版本,你可以这样做:
| int? ValA= 123; int? ValB = null; int NewVarA = ValA ?? -1; int NewVarB = ValB ?? -1; |
NewVarA的值将会为123因为ValA不是空值。NewVarb的值是-1因为ValB是空值。你看一看到,这里你将可以将变量从一个空值转化成一个缺省值。这里缺省值是-1。

浙公网安备 33010602011771号