-
1 基础知识
-
2 案例—统计平均成绩
-
3 注意事项

![]()
常量与变量
程序设计的主要目的就是解决现实世界的问题,这就需要计算机处理现实世界中提供的各种数据。因此对于数据如何在程序里表达和运用就显得格外重要了。现实世界中,常常会遇到各种不同的量,其中有的量在过程中不起变化,就将其称为常量;有的量在过程中是变化的,也就是可以取不同的数值,就将其称为变量。这些常量和变量一旦要被程序处理,就必须存放在计算机的内存里。计算机的内存是一个以数字编码为基础的一块空间,且它的每个基本单位都可以用一个数字编码表示。如果程序设计人员将数据的存入和读取的位置都用这些数字编码来表示,那么将大大降低程序设计的效率。因为用这些数字编码表示的数据很抽象,对程序设计人员来说没有任何意义,不利于问题的分析。而且在调试程序时,不容易发现错误的所在。
因此,在C#程序开发中,程序设计人员可以根据程序设计的需要,给存放常量和变量的内存地址取一个有意义的名字来表示,它们分别叫做常量名和变量名。这样,就便于记忆和理解了。
![]()
通过const关键字来定义常量,其语法如下:
const 数据类型标识符 常量名=数值或表达式;
说明:
(1)一旦定义一个常量,就要赋予其初始值,而且这个常量的值在程序的运行过程中是不允许改变的。
(2)在定义常量时,表达式中的运算符对象只允许出现常量和常数,不允许出现变量。
(3)在C#中,不管是常量还是变量,都必须是先定义,后使用。
例如:
const floatPI=3.14f;
float a=9.4;
const floatb=PI+2; //正确,PI是常量
const floatc=a+2; //错误,a是变量,表达式不允许有变量
PI=5; //错误,PI是常量,不能修改它的值
![]()
变量的定义和赋值语法如下:
数据类型标识符变量名[=数值或表达式];
说明:
(1)语法中的[]表示可选,就是说[]中的内容写或不写都不会导致语法错误。
(2)在对变量进行赋值时,数值或表达式的值类型必须同变量的类型相同。如果数值或表达式的值类型与变量的类型不相同,但数值或表达式的值类型所表示的数值范围比被赋值变量的类型所表示的范围要小,是允许赋值的。事实上,C#在内部进行了一次数值类型的转换,这种转换叫隐式转换。关于数据类型和隐式转换将在后面进行讲述。
C#变量命名规则
为变量起名时要遵守C#语言的规定,具体有以下4条。
(1)变量名必须以字母开头。
(2)变量名只能由字母、数字和下划线组成,而不能包含空格、标点符号、运算符等其他符号。
(3)变量名不能与C#中的关键字名称相同。
(4)变量名不能与C#中的库函数名称相同。
但在C#中有一点例外,那就是允许在变量名前加前缀“@”。在这种情况下,就可以使用前缀“@”加上关键字作为变量的名称。这主要是为了与其他语言进行交互时避免冲突。因为前缀“@”实际上并不是名称的一部分,其他的编程语言就会把它作为一个普通的变量名。在其他情况下,不推荐使用前缀“@”作为变量名的一部分。
例如:
int a ; //合法
int No.2; //不合法,含有非法字符
string name; //合法
char struct; //不合法,与关键字名称相同
char @use; //合法
float Main; //不合法,与函数名称相同
尽管符合了上述要求的变量名可以使用,但还是希望在给变量取名时,应给出具有描述性质的名称,这样写出来的程序便于理解。例如,一个消息字符串的名字就可以叫s_message,而e9w0P就不是一个好的变量名。
可以在一条语句中命名多个类型相同的变量,例如:
int a,b,c=50,d;
数据类型概论
计算机把数据放在内存中,但各种数据的大小并不相同,因此要放进内存时,所需的内存空间也并不完全相同。所以计算机在处理数据时,不仅要给数据取个名字,还要区分数据可能的种类,也就是所谓的数据类型。
在C#中数据类型可分为数值类型和引用类型。数值类型的变量存储在内存的“栈”区域,在运行程序的开始,计算机就已经在“栈”中分配好此变量的内存块了。当程序运行结束,为此变量分配的内存将都被释放。引用类型的变量存储在内存的“堆”区域,在程序运行时,计算机随时在“堆”中分配和释放任意长度的内存块。数值类型变量之间的赋值是赋予变量的值,而引用类型变量之间的赋值只是复制引用(相当于地址)。
数值类型:包括整数类型、字符类型、浮点数类型、布尔类型、结构类型和枚举类型。 引用类型:包括类类型(如string类)、数组类型、接口类型和代理类型。
C#的数据类型体系如图2.1所示。
|
| 图2.1 C#数据类型体系 |
说明:sbyte、ushort、uint、ulong与CLS不兼容,所以在跨语言的场合不应该使用这些整数类型。公共语言规范(Common Language Specification,CLS)和通用类型系统(Common Type System,CTS)一起确保语言的互操作性。CLS是一个最低标准集,所有面向.NET的编译器都必须支持它。
![]()
整数类型的数据值只能为整数。数学上的整数可以从负无穷大到正无穷大,但是由于计算机的存储单元是有限的,所以计算机语言提供的整数类型的值总是在一定范围之内。C#有8种整数类型:短字节型(sbyte)、字节型(byte)、短整型(short)、无符号短整型(ushort)、整型(int)、无符号整型(uint)、长整型(long)和无符号长整型(ulong)。划分的依据是根据该类型的变量在内存中所占的位数。位数的概念是按照2的指数幂来定义的,如8位整数,则它可以表示2的8次方个数值,即256。C#整数类型的取值范围如表2.1所示。
| 表2.1 整数类型列表 | |||
|---|---|---|---|
| 类型标识符 | CTS类型名 | 描 述 | 可表示的数值范围 |
| sbyte | System.Sbyte | 有符号8位整数 | -128~127 |
| byte | System.Byte | 无符号8位整数 | 0~255 |
| short | System.Intl6 | 有符号16位整数 | -32768~32767 |
| ushort | System.Uintl6 | 无符号16位整数 | 0~65535 |
| int | System.Int32 | 有符号32位整数 | -2147483648~2147483647 |
| uint | System.Uint32 | 无符号32位整数 | 0~4294967295 |
| long | System.Int64 | 有符号64位整数 | -9223372036854775808~9223372036854775807 |
| ulong | System.Uint64 | 无符号64位整数 | 0~18446744073709551615 |
.NET定义了一个称为通用类型系统的类型标准。这个类型系统不但实现了COM的变量兼容类型,而且还定义了通过用户自定义类型的方式来进行类型扩展。任何以.NET平台作为目标的语言必须建立它的数据类型与CTS的类型间的映射。所有.NET语言共享这一类型系统,实现它们之间无缝的互操作。该方案还提供了语言之间的继承性。C#中的int数据类型其实是CTS类型中Int32的一个别名。另外,7个整数数据类型也分别是其他几种结构的别名。
在声明一个C#变量时既可以使用C#中的数据类型名,如“inta;”,也可以用CTS类型名,如“System.Int32 a;”。
![]()
C#支持3种基本浮点数:表示单精度的float、表示双精度的double和表示财务计算用途的decimal。这3种不同的浮点数所占用的空间并不相同,因此它们可用来设定的数据范围也不相同,具体如表2.2所示。
| 表2.2 浮点数类型列表 | |||
|---|---|---|---|
| 类型标识符 | CTS类型名 | 描 述 | 可表示的数值范围 |
| float | System.Single | 32位单精度浮点型,精度7位 | -3.402823e38~3.402823e38 |
| double | System.Double | 64位双精度浮点型,精度15~16位 | -1.79769313486232e308~ 1.79769313486232e308 |
| decimal | System.Decimal | 128位精确小数类型或整型,精度29位 | ±1.0e-28~±7.9e28 |
说明:表中-3.402823e38表示-3.402823×1038;1.0e-28表示1.0×10-28(E、e均可),这是科学记数法。
在程序中书写一个十进制的数值常数时,C#默认按照如下方法判断一个数值常数属于哪种C#数值类型。
(1)如果一个数值常数不带小数点,如3456,则这个常数的类型是个整数。
(2)对于一个属于整型的数值常数,C#按如下顺序判断该数的类型:int,uint,long, ulong。
(3)如果一个数值常数带小数点,如1.2,则该常数的类型是浮点型中的double类型。如果不希望C#使用上述默认的方式来判读一个十进制数值常数的类型,可以通过在数值常数后加后缀的方法来指定数值常数的类型。
可以使用的数值常数后缀有以下6种。
(1)u(或U)后缀:加在整型常数后面,代表该常数是uint类型或ulong类型,具体哪种由常数的实际值决定。C#优先匹配uint类型。
(2)1(或L)后缀:加在整型常数后面,代表该常数是long类型或ulong类型,具体哪种由常数的实际值决定。C#优先匹配long类型。
(3)ul(或uL、U1、UL、lu、lU、LU)后缀:加在整型常数后面,代表该常数是ulong类型。
(4)f(或F)后缀:加在任何一个数值常数后面,代表该常数是float类型。
(5)d(或D)后缀:加在任何一个数值常数后面,代表该常数是double类型。
(6)m(或M)后缀:加在任何一个数值常数后面,代表该常数是decimal类型。
如果所指定的数据符号不能用指定类型表示,在编译时会产生错误。当用两种浮点型执行运算时,会产生以下的值:正零和负零、+Infinity和-Infinity(正无穷大或负无穷大)、NaN(非数字值)。
![]()
除了数字以外,计算机处理的信息主要就是字符了。字符包括数字字符、英文字母、表达符号等,C#提供的字符类型按照国际上公认的标准,采用Unicode字符集。一个Unicode的标准字符长度为16位,用它可以来表示世界上大多数语言。
用来表示字符数据常量时,共有以下几种不同的表示方式。
(1)用单引号将单个字符包括起来,例如:‘A’、‘n’、‘u’。
(2)用原来的数值编码来表示字符数据常量,例如:‘a’是97,‘v’是118。
(3)还可以直接通过十进制转义符(前缀\x)或Unicode表示法(前缀\u)表示字符数据常量,例如:‘\x0032’、‘\u5495’。
(4)C#提供了转义符,用于程序中指代特殊的控制字符,具体如表2.3所示。
| 表2.3 C#常用转义符 | ||
|---|---|---|
| 转 义 序 列 | 产生的字符 | 字符的Unicode值 |
| \’ | 单引号 | 0x0027 |
| \” | 双引号 | 0x0022 |
| \\ | 反斜杠 | 0x005c |
| \0 | 空 | 0x0000 |
| \a | 响铃 | 0x0007 |
| \b | 退格 | 0x0008 |
| \f | 换页 | 0x000c |
| \n | 换行 | 0x000a |
| \r | 回车 | 0x000d |
| \t | 水平制表符 | 0x0009 |
| \v | 垂直制表符 | 0x000b |
![]()
字符串类型是一个char类型的序列。
定义一个字符串的语法如下:
string 变量名[=“字符串值”];
![]()
布尔类型数据用于表示逻辑真和逻辑假,布尔类型的类型标识符是bool。布尔类型只有两个值:true和false。通常占用一个字节的存储空间,不过作为数组的基本单位元素时,却会占用两个字节的内存空间。布尔类型还有一个特点:不能进行数据类型转换。
![]()
枚举类型是一种用户自定义的数值类型,它提供了一种可以简便创建一组有结构的符号来表示常量值。例如,一个星期有7天,分别用符号Monday、Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday来表示,有助于程序设计人员更好地调试和维护程序。
(1)枚举类型的定义
用于定义新的枚举类型。枚举声明以关键字enum开始,然后定义枚举的名称、可访问性、基类型和成员。
语法如下:
[访问修饰符]enum 枚举标识名[:枚举基类型]
{枚举成员[=整型常数],[枚举成员[=整型常数],…]}[;]
说明:
访问修饰符将在后面章节中介绍,枚举声明的修饰符与类声明的修饰符具有同样的意义。然而要注意的
是,枚举声明中不允许使用abstract修饰符和sealed修饰符。枚举不能是抽象的,也不允许派生。枚举类型定义的主体用于定义零个或多个枚举成员,这些成员是该枚举类型的命名常量。任意两个枚举
成员都不能具有相同的名称。每个枚举类型都有一个相应的整型,称为该枚举类型的基类型(underlyingtype)。该基类型必须能够
表示在枚举中定义的所有枚举数值。枚举声明可以显式地声明byte、sbyte、short、ushort、int、
uint、long或ulong类型作为基类型。注意,char不能用作基类型。没有显式地声明基类型的枚举声明,其默认的基类型为int。
例如:
enum Color:ulong
{
Blue,
Red,
Green,
Black
}
上面代码声明了一个基类型为ulong的枚举。开发人员可以像本例一样选择使用ulong作为基类型,以便能够使用在ulong的范围内但不在int的范围内的值,也可以保留这种选择以便将来使用。
(2)枚举成员的赋值
在定义的枚举类型中,每一个枚举成员都有一个常量值与其对应,默认情况下枚举的基类型为int,而且规定第一个枚举成员的取值为0,它后面的每一个枚举成员的值加1递增,这样增加后的值必须在基类型可表示的值的范围内;否则,将发生编译错误。
在编程时,也可以根据实际需要为枚举成员赋值。
如果某一枚举成员被赋值了,那么该枚举成员的值就以赋的值为准。在它后面的每一个枚举成员的值加1递增,直到下一个赋值枚举成员出现为止。例如:
enum Color
{
Red,
Green=3,
Blue
White,
Yellow=-1,
Purple
}
所以Red=0,Green=3,Blue=4,White=5,Yellow=-1,Purple=0。
每个枚举成员都有一个关联的常量值。该值的类型是包含该值的枚举的基类型。每个枚举成员的常量值必须在该枚举的基类型的范围内。例如:
enum Color:uint
{
Yellow=-1,
Black=-2,
Blue=-3
}
这将导致编译时出错,因为常量值-1、-2和-3均为负数,不在基类型uint的范围内。
多个枚举成员可以共享相同的常量值。例如:
enum Color
{
Red=3,
Green,
Blue,
Min=Blue
}
上面是一个枚举举例,其中的两个枚举成员(Blue和Min)具有相同的常数值。
(3)枚举成员的访问
在C#中可以通过枚举名和枚举变量两种方式来访问枚举成员。
通过枚举名访问枚举成员的形式如下:
枚举名.枚举成员;
在通过枚举变量访问枚举成员之前,首先要定义一个枚举类型变量。语法如下:
枚举变量名.变量名;
然后再通过枚举变量访问枚举成员,语法如下:
枚举变量名.枚举成员;
例如,定义一个WeekDay枚举类型,代码如下:
enum Weekday
{Monday、Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday}
WeekDay wd;
wd=WeekDay.Tuesday;
上面示例代码中通过枚举名WeekDay访问枚举成员Tuesday,并把它赋值给枚举变量wd。
![]()
在进行一些常用的数据运算、文字处理时,简单类型似乎已经足够了。但是在日常生活中经常会碰到一些更为复杂的数据类型。例如,一个班的学生成绩记录中可以包含学生的学号、姓名和各科成绩。如果按照简单类型来管理,每一条记录都要存放到多个不同的变量当中,这样工作量很大,也不够直观。有没有更好的办法呢?在C#中可以用结构类型解决。
结构类型也是一种用户自定义的数值类型,它是指一组由各种不同数据类型的相关数据信息组合在一起而形成的组合类型。把一系列相关的变量组织成为一个单一实体的过程,称为生成结构的过程。这个单一实体的类型就叫做结构类型。
(1)结构的定义
结构的定义语法如下:
[访问修饰符]struct 结构标识名[:基接口名列表]
{
//结构成员定义
}
说明:
结构成员包括各种数据类型的变量、构造函数、方法、属性和索引器。 结构可以实现接口。
(2)结构类型成员的访问
用结构变量访问结构成员。在通过结构变量访问结构成员之前首先要定义一个结构类型变量。语法如下:
结构类型名 变量名
然后再通过结构变量访问结构成员,语法如下:
结构变量名.结构成员
![]()
在程序设计中,有时要进行数据类型的相互转换,例如,被赋值的变量或方法的形式参数的类型与实际的对象类型不同,就需要进行类型转换。C#中有两种转化方式:隐式转换和显式转换。当发生类型转换时,被赋值的变量或方法的形参的类型称为目标类型,而实际对象的类型称为源类型。
(1)隐式转换
当发生类型转换时,如果在代码中没有明确指定目标类型,则称为隐式转换。也就是说隐式转换是系统默认的、不需要加以声明即可进行的转换。在隐式转换过程中,编译器不需要对转换进行详细的检查就能安全地执行转换。
可以进行的隐式转换有以下几种:
从sbyte到short、int、long、float、double或decimal。 从byte到short、ushort、int、uint、long、ulong、float、double或decimal。 从short到int、long、float、double或decimal。 从ushort到int、uint、long、ulong、float、double或decimal。 从int到long、float、double或decimal。 从uint到long、ulong、float、double或decimal。 从long到float、double或decimal。 从ulong到float、double或decimal。 从char到ushort、int、uint、long、ulong、float、double或decimal。 从float到double。 从int、uint、long到float以及从long到double类型的转换可能会造成精度的损失,但并不会造成
数量上的损失。除此之外的其他隐式数值转换不会损失任何信息。
注意:这里不存在转到char类型的隐式数值转换,也就是说其他的整型数据不会被自动地转换为字符型数据。同时float类型不能隐式地转化为decimal类型。
(2)显式转换
当发生类型转换时,如果在代码中明确指定目标类型,则称为显式转换。显式转换也称为强制型转换,一般在不存在该类型的隐式转换时才使用。
语法格式如下:
(类型标识符)表达式
这样就可以将表达式值的数据类型转换为类型标识符的类型。例如:
(int)6.143 //把float类型的6.143转换为int类型
所有可能的显式转换如下:
从sbyte到byte、ushort、uint、ulong或char。 从byte到sbyte或char。 从short到sbyte、byte、ushort、uint、ulong或char。 从ushort到sbyte、byte、short或char。 从int到sbyte、byte、short、ushort、uint、ulong或char。 从uint到sbyte、byte、short、ushort、int或char。 从long到sbyte、byte、short、ushort、int、uint、ulong或char。 从ulong到sbyte、byte、short、ushort、int、uint、long或char。 从char到sbyte、byte或short。 从float到sbyte、byte、short、ushort、int、uint、long、ulong、char或decimal。 从double到sbyte、byte、short、ushort、int、uint、long、ulong、char、float或decimal。 从decimal到sbyte、byte、short、ushort、int、uint、long、ulong、char、float或double。
这种显式类型转换有可能丢失信息或导致异常抛出,转换过程中将按照下列规则进行:
对于从一种整型到另一种整型的转换,编译器将针对转换进行溢出检测,如果没有发生溢出,转换成
功,否则抛出一个OverflowException异常。这种检测还与编译器中是否设定了checked选项有关。对于从float、double或decimal到整型的转换,源类型的值通过舍入取最接近整型值。如果这个整型
值超出了目标类型的值域,将抛出一个OverflowException异常。对于从double到float的转换,double值通过舍入取最接近的float值。如果这个值太小,结果将变成
+0或-0;如果这个值太大,结果将变成正无穷或负无穷。如果原double值是NaN,则转换结果也是NaN。对于从float或double到decimal的转换,源类型的值将转换成小数形式并通过舍入取到小数点后28位
(如果有必要)。如果源类型的值太小,则结果为0;如果太大以致不能用小数表示,或是无穷或NaN,
将抛出InvalidCastException异常。对于从decimal到float或double的转换,小数的值通过舍入取最接近的值。这种转换可能会丢失精度,
但不会引起异常。
(3)负责数据类型转换的Convert类
Convert类用于将一个基本数据类型转换为另一个基本数据类型,返回与指定类型值等效的类型;受支持的源类型是Boolean、Char、Sbyte、Byte、Int16、Int32、Int64、Uint16、Uint32、Uint64、Single、Double、Decimal、DateTime和String。可根据不同的要使用Convert类的公共方法实现不同数据类型的转换。
无法产生有意义的结果的转换引发InvalidCastException(指定的转换无效)的异常不执行任何转换。下列转换会引发异常:从Char转换为Boolean、Single、Double、Decimal或DateTime,以及从这些类型转换为Char;还有从DateTime转换为除String之外的任何类型,以及从任何类型(String除外)转换为DateTime类型。
Convert类常用公共方法如表2.4所示。
| 表2.4 Convert类常用公共方法 | |
|---|---|
| 方 法 名 | 说 明 |
| FromBase64CharArry | 将Unicode字符数组的子集(将二进制数据编码为base64数字)转换成等效的8位无符号整数数组。参数指定输入数组的子集以及要转换的元素数 |
| FromBase64String | 将指定的String(将二进制数据编码为base64数字)转换成等效的8位无符号整数数组 |
| GetHashCode | 用作特定类型的哈希函数。GetHashCode适合在哈希算法和数据结构(如哈希表)中使用 |
| ToBase64CharArray | 将8位无符号整数数组的子集转换为用base64数字编码的Unicode字符数组的等价子集 |
| ToBase64String | 将8位无符号整数数组的值转换为它的等效String表示形式(使用base64数字编码) |
| ToBoolean | 将指定的值转换为等效的布尔值 |
| ToByte | 将指定的值转换为8位无符号整数 |
| ToChar | 将指定的值转换为Unicode字符 |
| ToDateTime | 将指定的值转换为DateTime |
| ToDecimal | 将指定的值转换为Decimal数字 |
| ToDouble | 将指定的值转换为双精度浮点数字 |
| ToInt16 | 将指定的值转换为16位有符号整数 |
| ToInt32 | 将指定的值转换为32位有符号整数 |
| ToInt64 | 将指定的值转换为64位有符号整数 |
| ToByte | 将指定的值转换为8位有符号整数 |
| ToSingle | 将指定的值转换为单精度浮点数字 |
| ToString | 将指定的值转换为其等效的String表示形式 |
| ToUint16 | 将指定的值转换为16位无符号整数 |
| ToUint32 | 将指定的值转换为32位无符号整数 |
| ToUint64 | 将指定的值转换为64位无符号整数 |
例如:
char x='1';
int i=Convert.ToInt32(x);
此时i=49,Convert.ToInt32()把字符数据类型转换成了整数数据类型。
(4)数据类型转换的Parse()方法
每个数值数据类型都包含一个Parse()方法,它允许将字符串转换成对应的数值类型。
string s1="9",s2="9.423";
int m=int.Parse(s1); //将s1转换成整数类型
float n=float.Parse(s2); //将s2转换成浮点类型







