
第3章 C#语言基础
(
视频讲解:1小时43分钟)
很多人认为学习C#之前必须要学习C++,其实并非如此,产生这种错误的认识是因为很多人在学习C#之前都学过C++。事实上,C#比C++更容易掌握。要掌握并熟练应用C#,就需要对C#语言基础进行充分的了解。本章将对C#语言的基础语法进行详细的讲解,对于初学者来说,应该对本章的各个小节进行仔细地阅读和深入的思考,这样才能达到事半功倍的效果。
通过学习本章,读者主要掌握以下内容:
变量的定义及使用
熟练掌握基本的数据类型
熟悉变量的作用域
常量的概念及分类
熟练掌握两种不同的数据类型转换
3.1 为什么要使用变量

视频讲解
变量关系到数据的存储,计算机是使用内存来存储计算时所使用的数据,那么内存是如何来存储数据的呢?通过生活常识,我们知道数据是各式各样的,比如整数、小数、字符串等,那么,在内存中存储这些数据时,就首先需要根据数据的需求(即类型)为它申请一块合适的空间,然后再在这个空间中存储相应的值。实际上,内存就像一个宾馆,客人如果到一个宾馆住宿,首先需要开房间,然后再入住,而在开房间时,客人需要选择是开单人间、双人间还是总统套房等,这其实就对应一个变量的数据类型选择问题。
在内存中为数据分配一定的空间之后,如果要使用定义的这个数据,由于内存中的数据是以二进制格式进行存储的,而这些二进制数据都对应相应的内存地址,因此,必须要通过一种技术使用户能够很方便地访问到二进制数据的内存地址,这种技术就是变量!
3.2 变量是什么

视频讲解
变量主要用来存储特定类型的数据,用户可以根据需要随时改变变量中所存储的数据值。变量具有名称、类型和值,其中,变量名是变量在程序源代码中的标识,类型用来确定变量所代表的内存的大小和类型,变量值是指它所代表的内存块中的数据。在程序执行过程中,变量的值可以发生变化。使用变量之前必须先声明变量,即指定变量的类型和名称。
这里以上面的客人入住宾馆为例,说明一个变量所需要的基本要素。首先,客人需要选择房间类型,也就是确定变量类型的过程;选择房间类型后,需要选择房间号,这是确定变量的名称;完成以上操作后,这个客人就可以顺利入住,这样,这个客人就相当于这个房间中存储的数据,示意图如图3.1所示。

图3.1 变量的基本要素
3.3 变量的声明及初始化

视频讲解
好比一个新生儿必须有一个名字一样,使用变量时,也需要首先对变量进行命名,对变量命名的过程,其实就是声明一个变量。变量在使用之前,必须进行声明并初始化,本节将对变量的声明、简单数据类型、变量初始化,以及变量的作用域进行详细讲解。
3.3.1 声明变量
1. 声明变量
声明变量就是指定变量的名称和类型,变量的声明非常重要,未经声明的变量本身并不合法,也无法在程序中使用。在C#中,声明一个变量是由一个类型和跟在后面的一个或多个变量名组成,多个变量之间用逗号分开,声明变量以分号结束,语法如下:

例如,声明一个整型变量mr,然后再同时声明3个字符串变量mr_1、mr_2和mr_3,代码如下:

2. 变量的命名规则
在声明变量时,要注意变量的命名规则。C#的变量名是一种标识符,应该符合标识符的命名规则。另外,需要注意的一点是:C#中的变量名是区分大小写的,比如num和Num是两个不同的变量,在程序中使用时是有区别的。以下为变量的命名规则:
变量名只能由数字、字母和下画线组成。
变量名的第一个符号只能是字母和下画线,不能是数字。
不能使用C#中的关键字作为变量名。
一旦在一个语句块中定义了一个变量名,那么在变量的作用域内都不能再定义同名的变量。
例如,下面的变量名是正确的:

下面的变量名是不正确的:

说明
在C#语言中允许使用汉字或其他语言文字作为变量名,如“int年龄=21”,在程序运行时并不出现什么错误,但建议读者尽量不要使用这些语言文字作为变量名。
3.3.2 简单数据类型
前面提到,声明变量时,首先需要确定变量的类型,那么,开发人员可以使用哪些类型呢?实际上,可以使用的变量类型是无限多的,因为开发人员可以通过自定义类型存储各种数据,但这里要讲解的简单数据类型是C#中预定义的一些类型。
C#中的数据类型根据其定义可以分为两种,一种是值类型,另一种是引用类型。从概念上看,值类型是直接存储值,而引用类型存储的是对值的引用。C#中的数据类型结构如图3.2所示。

图3.2 C#中的数据类型结构
从图3.2可以看出,值类型主要包括简单类型和复合类型两种,其中简单类型是程序中使用的最基本类型,主要包括整数类型、浮点类型、布尔类型和字符类型4种,这4种简单类型都是.NET中预定义的;而复合类型主要包括枚举类型和结构类型,这两种复合类型既可以是.NET中预定义的,也可以用户自定义。本节主要对简单类型进行详细讲解,简单类型在实际中的应用如图3.3所示。

图3.3 简单类型在实际中的应用
1. 整数类型
整数类型用来存储整数数值,即没有小数部分的数值。可以是正数,也可以是负数。整数数据在C#程序中有3种表示形式,分别为十进制、八进制和十六进制。
十进制:十进制的表现形式大家都很熟悉,如120,0,–127。
注意
不能以0作为十进制数的开头(0除外)。
八进制:以0开头的数,如0123(转换成十进制数为83)、–0123(转换成十进制数为–83)。
注意
八进制必须以0开头。
十六进制:以0x/0X开头的数,如0x25(转换成十进制数为37)、0Xb01e(转换成十进制数为45086)。
注意
十六进制必须以0X或0x开头。
C#中内置的整数类型如表3.1所示。
表3.1 C#内置的整数类型

说明
表3.1中出现了“有符号**”和“无符号**”,其中,“无符号**”是在“有符号**”类型的前面加了一个u,这里的u是unsigned的缩写。它们的主要区别是:“有符号**”既可以存储正数,也可以存储负数;“无符号**”只能存放不带符号的整数,因此,它只能存放正数。例如下面的代码:

例如,定义一个int类型的变量i和一个byte类型的变量j,并分别赋值为2017和255,代码如下:

此时,如果将byte类型的变量j赋值为256,即将代码修改如下:

此时在Visual Studio开发环境中编译程序,会出现如图3.4所示的错误提示。

图3.4 取值超出指定类型的范围时出现的错误提示
分析图3.4中出现的错误提示,主要是由于byte类型的变量是8位无符号整数,它的范围在0 ~ 255,而256这个值已经超出了byte类型的范围,所以编译程序会出现错误提示。
说明
整数类型变量的默认值为0。
2. 浮点类型
浮点类型变量主要用于处理含有小数的数据,浮点类型主要包含float和double两种类型。表3.2列出了这两种浮点类型的描述信息。
表3.2 浮点类型及描述

如果不做任何设置,包含小数点的数值都被认为是double类型,例如9.27,没有特别指定的情况下,这个数值是double类型。如果要将数值以float类型来处理,就应该通过强制使用f或F将其指定为float类型。
例如,下面的代码就是将数值强制指定为float类型。

如果要将数值强制指定为double类型,则应该使用d或D进行设置,但加不加“d”或“D”没有硬性规定,可以加也可以不加。
例如,下面的代码就是将数值强制指定为double类型。

注意
(1)需要使用float类型变量时,必须在数值的后面跟随f或F,否则编译器会直接将其作为double类型处理;另外,也可以在double类型的值前面加上(float),对其进行强制转换。
(2)浮点类型变量的默认值是0,而不是0.0。
3. decimal类型
decimal类型表示128位数据类型,它是一种精度更高的浮点类型,其精度可以达到28位,取值范围为±1.0×10–28 ~ ±7.9×1028。
技巧
由于decimal类型的高精度特性,它更合适于财务和货币计算。
如果希望一个小数被当成decimal类型使用,需要使用后缀m或M,例如:

如果小数没有后缀m或M,数值将被视为double类型,从而导致编译器错误,例如,在开发环境中运行下面代码:

将会出现如图3.5所示的错误提示。
从图3.5可以看出,3.14这个数如果没有后缀,直接被当成了double类型,所以赋值给decimal类型的变量时,就会出现错误提示。

图3.5 不加后缀m/M时,decimal出现的错误
【例3.01】创建一个控制台应用程序,声明double型变量height来记录身高,单位为米,声明int型变量weight记录体重,单位为千克,根据“BMI=体重/(身高*身高)”的公式计算BMI指数(身体质量指数),代码如下:(实例位置:资源包\源码\03\3.01)

程序运行效果如图3.6所示。

图3.6 根据身高体重计算BMI指数
4. bool类型
布尔类型主要用来表示true/false值,C#中定义布尔类型时,需要使用bool关键字。例如,下面代码定义一个布尔类型的变量:

说明
布尔类型通常被用在流程控制语句中作为判断条件。
这里需要注意的是,布尔类型变量的值只能是true或者false,不能将其他的值指定给布尔类型变量,例如,将一个整数10赋值给布尔类型变量,代码如下:

在Visual Studio开发环境中运行这句代码,会出现如图3.7所示的错误提示。

图3.7 将整数值赋值给布尔类型变量时出现的错误
说明
布尔类型变量的默认值为false。
5. 字符类型
字符类型在C#中使用Char类来表示,该类主要用来存储单个字符,它占用16位(两个字节)的内存空间。在定义字符型变量时,要以单引号(' ')表示,如'a'表示一个字符,而"a"则表示一个字符串,虽然其只有一个字符,但由于使用双引号,所以它仍然表示字符串,而不是字符。字符类型变量的声明非常简单,代码如下:

注意
Char类只能定义一个Unicode字符。Unicode字符是目前计算机中通用的字符编码,它为针对不同语言中的每个字符设定了统一的二进制编码,用于满足跨语言、跨平台的文本转换和处理的要求,这里了解Unicode即可。
Char类的使用
Char类为开发人员提供了许多的方法,可以通过这些方法灵活地对字符进行各种操作。Char类的常用方法及说明如表3.3所示。
表3.3 Char类的常用方法及说明

从表3.3可以看到,C#中的Char类提供了很多操作字符的方法,其中以Is和To开始的方法比较常用。以Is开始的方法大多是判断Unicode字符是否为某个类别,例如,是否大小写、是否是数字等;而以To开始的方法主要是对字符进行转换大小写及转换字符串的操作。
【例3.02】创建一个控制台应用程序,演示如何使用Char类提供的常见方法,代码如下:
(实例位置:资源包\源码\03\3.02)

程序的运行结果如图3.8所示。

图3.8 Char类常用方法的应用
转义字符
前面讲到了字符只能存储单个字符,但是,如果在Visual Studio开发环境中编写如下代码:

会出现如图3.9所示的错误提示。

图3.9 定义反斜线时的错误提示
从代码表面上看,反斜线“\”是一个字符,正常应该是可以定义为字符的,但为什么会出现错误呢?这里就引出了转义字符的概念。
转义字符是一种特殊的字符变量,以反斜线“\”开头,后跟一个或多个字符,也就是说,在C#中,反斜线“\”是一个转义字符,不能单独作为字符使用。因此,如果要在C#中使用反斜线,可以使用下面代码表示:

转义字符就相当于一个电源变换器,电源变换器就是通过一定的手段获得所需的电源形式,例如交流变成直流、高电压变为低电压、低频变为高频等。转义字符也是,它是将字符转换成另一种操作形式,或是将无法一起使用的字符进行组合。
注意
转义符\(单个反斜杠)只针对后面紧跟着的单个字符进行操作。
C#中的常用转义字符如表3.4所示。
表3.4 转义字符及其作用

【例3.03】创建一个控制台应用程序,通过使用转义字符在控制台窗口中输出Windows的系统目录,代码如下:(实例位置:资源包\源码\03\3.03)

程序的运行结果如图3.10所示。

图3.10 输出Windows的系统目录
技巧
例3.03中输出系统目录时,遇到反斜杠时,使用“\\”表示,但是,如果遇到下面的情况:

从上面代码看到,如果有多级目录,遇到反斜杠时,如果都使用“\\”,会显得非常麻烦,这时可以用一个@符号来进行多级转义,代码修改如下:

3.3.3 变量的初始化
变量的初始化实际上就是给变量赋值,以便在程序中使用。首先,在Visual Studio 2017开发环境中运行下面一段代码:

运行上面代码时,会出现如图3.11所示的错误提示。
从图3.11可以看出,如果直接定义一个变量进行使用,会提示使用了未赋值的变量,这说明:在程序中使用变量时,一定要对其进行赋值,也就是初始化,然后才可以使用。那么如何对变量进行初始化呢?

图3.11 变量未赋值时的错误
初始化变量有3种方法,分别是单独初始化变量、声明时初始化变量、同时初始化多个变量,下面分别进行讲解。
1. 单独初始化变量
在C#中,使用赋值运算符“=”(等号)对变量进行初始化,即将等号右边的值赋给左边的变量。
例如,声明一个变量sum,并初始化其默认值为2014,代码如下:

说明
在对变量进行初始化时,等号右边也可以是一个已经被赋值的变量。例如,首先声明两个变量sum和num,然后将变量sum赋值为2014,最后将变量sum赋值给变量num,代码如下:

2. 声明时初始化变量
声明变量时可以同时对变量进行初始化,即在每个变量名后面加上给变量赋初始值的指令。
例如,声明一个整型变量a,并且赋值为927。然后,再同时声明3个字符串型变量并初始化,代码如下:

3. 同时初始化多个变量
在对多个同类型的变量赋同一个值时,为了节省代码的行数,可以同时对多个变量进行初始化。例如,声明5个int类型的变量a,b,c,d,e,然后将这5个变量都初始化为0,代码如下:

上面讲解了初始化变量的3种方法,这时,我们对本节开始的代码段进行修改,使其能够正常运行,修改后的代码如下:

再次运行程序,即可正常运行。
3.3.4 变量的作用域
由于变量被定义后,只是暂时存储在内存中,等程序执行到某一个点后,该变量会被释放掉,也就是说变量有它的生命周期。因此,变量的作用域是指程序代码能够访问该变量的区域,如果超出该区域,则在编译时会出现错误。在程序中,一般会根据变量的“有效范围”将变量分为“成员变量”和“局部变量”。
1. 成员变量
在类体中定义的变量被称为成员变量,成员变量在整个类中都有效。类的成员变量又可以分为两种,即静态变量和实例变量。
例如,在Test类中声明静态变量和实例变量,代码如下:

其中,x为实例变量,y为静态变量(也称类变量)。如果在成员变量的类型前面加上关键字static,这样的成员变量称为静态变量。静态变量的有效范围可以跨类,甚至可达到整个应用程序之内。对于静态变量,除了能在定义它的类内存取,还能直接以“类名. 静态变量”的方式在其他类内使用。
2. 局部变量
在类的方法体中定义的变量(定义方法的“{”与“}”之间的区域)称为局部变量,局部变量只在当前代码块中有效。
在类的方法中声明的变量,包括方法的参数,都属于局部变量。局部变量只有在当前定义的方法内有效,不能用于类的其他方法中。局部变量的生命周期取决于方法,当方法被调用时,C#编译器为方法中的局部变量分配内存空间,当该方法的调用结束后,则会释放方法中局部变量占用的内存空间,局部变量也将会销毁。
变量的有效范围如图3.12所示。

图3.12 变量的有效范围
【例3.04】创建一个控制台应用程序,使用一个局部变量记录用户的登录名,代码如下:(实例位置:资源包\源码\04\3.04)

程序运行结果如图3.13所示。

图3.13 使用一个局部变量记录用户的登录名
3.4 常量

视频讲解
通过对前面知识的学习,我们知道了变量是随时可以改变值的量,那么,在遇到不允许改变值的情况时,该怎么办呢?这就是本节要讲解的常量。
3.4.1 常量是什么
常量就是程序运行过程中,值不能改变的量,比如现实生活中的居民身份证号码、数学运算中的π值等,这些都是不会发生改变的,它们都可以定义为常量。常量可以区分为不同的类型,比如98,368是整型常量,3.14,0.25是实数常量,即浮点类型的常量,m、r是字符常量。
3.4.2 常量的分类
常量主要有两种,分别是const常量和readonly常量,下面分别对这两种常量进行讲解。
1. const常量
在C#中提到常量,通常指的是const常量。const常量也叫静态常量,它在编译时就已经确定了值。const常量的值必须在声明时就进行初始化,而且之后不可以再进行更改。
例如,声明一个正确的const常量,同时再声明一个错误的const常量,以便读者对比参考,代码如下:

2. readonly常量
readonly常量是一种特殊的常量,也称为动态常量,从字面理解上看,readonly常量可以进行动态赋值,但需要注意的是,这里的动态赋值是有条件的,它只能在构造函数中进行赋值,例如下面的代码:

如果要在构造函数以外的位置修改readonly常量的值,比如,在Main方法中进行修改,代码如下:

这时再运行程序,将会出现如图3.14所示的错误提示。

图3.14 试图在构造函数以外的位置修改readonly常量值的错误提示
3. const常量与readonly常量的区别
const常量与readonly常量的主要区别如下:
const常量必须在声明时初始化,而readonly常量则可以延迟到构造函数中初始化。
const常量在编译时就被解析,即将常量的值替换成了初始化的值,而readonly常量的值需要在运行时确定。
const常量可以定义在类中或者方法体中,而readonly常量只能定义在类中。
3.5 数据类型转换

视频讲解
类型转换是将一个值从一种数据类型更改为另一种数据类型的过程。例如,可以将string类型数据457转换为一个int类型,而且可以将任意类型的数据转换为string类型。
数据类型转换有两种方式,即隐式转换与显式转换。如果从低精度数据类型向高精度数据类型转换,则永远不会溢出,并且总是成功的;而把高精度数据类型向低精度数据类型转换,则必然会有信息丢失,甚至有可能失败,这种转换规则就像如图3.15所示的两个场景,高精度相当于一个大水杯,低精度相当于一个小水杯,大水杯可以轻松装下小水杯中所有的水,但小水杯无法装下大水杯中所有的水,装不下的部分必然会溢出。

图3.15 大小水杯转换类比数据类型转换的示意图
3.5.1 隐式类型转换
隐式类型转换就是不需要声明就能进行的转换,进行隐式类型转换时,编译器不需要进行检查就能自动进行转换。下列基本数据类型会涉及数据转换(不包括逻辑类型),这些类型按精度从“低”到“高”排列的顺序为byte < short < int < long < float < double,可对照图3.16,其中char类型比较特殊,它可以与部分int型数字兼容,且不会发生精度变化。

图3.16 自动转换的兼容顺序图
例如,将int类型的值隐式转换成long类型,代码如下:

3.5.2 显式类型转换
有很多场合不能隐式的进行类型转换,否则编译器会出现错误,例如,下面的类型在进行隐式转换时会出现错误:
int转换为short—会丢失数据。
int转换为uint—会丢失数据。
float转换为int—会丢失小数点后面的所有数据。
double转换为int—会丢失小数点后面的所有数据。
数值类型转换为char—会丢失数据。
decimal转换为其他数值类型—decimal类型的内部结构不同于整数和浮点数。
如果遇到上面类型之间的转换,就需要用到C#中的显式类型转换。显式类型转换也称为强制类型转换,它需要在代码中明确地声明要转换的类型。如果要把高精度的变量转换为低精度的变量,就需要使用显式类型转换。
显式类型转换的一般形式为:

其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。
例如,下面的代码用来把x转换为float类型:

通过显式类型转换,就可以解决高精度数据向低精度转换的问题,例如,将double类型的值4.5赋值给int类型变量时,可以使用下面的代码实现:

3.5.3 使用Convert类进行转换
3.5.2节中讲解了使用“(类型说明符)表达式”可以进行显式类型转换,下面使用这种方式实现下面的类型转换:

按照代码的本意,i的值应该是3000000000,但在运行上面两行代码时,却发现i的值是–1294967296,这主要是由于int类型的最大值为2147483647,很明显,3000000000要比2147483647大,所以在使用上面代码进行显式类型转换时,出现了与预期不符的结果,但是程序并没有报告错误,如果在实际开发中遇到这种情况,可能会引起大的BUG,那么,在遇到这种类型的错误时,有没有一种方式能够向开发人员报告错误呢?答案是肯定的。C#中提供了Convert类,该类也可以进行显式类型转换,它的主要作用是将一个基本数据类型转换为另一个基本数据类型。Convert类的常用方法及说明如表3.5所示。
表3.5 Convert类的常用方法及说明

例如,定义一个double类型的变量x,并赋值为198.99,使用Convert类将其显式转换为int类型,代码如下:

下面使用Convert类的ToInt32对本节开始的两行代码进行修改,修改后的代码如下:

再次运行这两行代码,则会出现如图3.17所示的错误提示。

图3.17 显式类型转换的错误提示
这样,开发人员即可根据图3.17中的错误提示对程序代码进行修改,避免程序出现逻辑错误。
3.6 小结
本章向读者介绍的是C#的基础语法,其中需要读者重点掌握的是C#中的基本数据类型、变量与常量以及数据类型的转换等内容。在使用变量时,需要读者注意的是变量的有效范围,否则在使用时会出现编译错误或浪费内存资源。
3.7 实战
3.7.1 实战一:打印保险单详细列表
打印保险单详细列表时,使用Char类型记录用户的性别是M(男)还是W(女),效果如图3.18所示。(实例位置:资源包\源码\03\实战\01)

图3.18 使用字符记录用户性别
3.7.2 实战二:记录京东618节日名称
使用一个int类型的变量记录每年京东的年中促销活动节日名称(提示:618),运行效果如图3.19所示。(实例位置:资源包\源码\03\实战\02)

图3.19 使用变量记录京东618节日名称