2.1 C#程序结构
•1.标识符
•2.关键字
•3.语句
•4.注释
•5.命名空间
•6.类的定义和类的成员
•7.C#程序中的方法
•1.标识符
•C#中标识符的命名约定
–类名约定首字母大写
–变量名、方法名等则约定使用Camel命名法。
2.关键字
•关键字(keyword)是C#程序语言保留作为专用的有特定意义的字符串,不能作为通常的标识符来使用。
•在C#语言中主要有如下关键字:abstract、as、base、bool、break、byte、case、catch、char、checked、、 class、const、continue、decimal、default delegate、do、double、else、enum、event、explicit、extern、false、finally、fixed、float、for、foreach、get、goto、if、implicit、in、int、interface、internal、is、lock、long、namespace、new、null、object、operator、out、override、params、private、protected、public、readonly、ref、return、sbyte、sealed、set、short、sizeof、stackalloc、static、string、struct、switch、this、throw、true、try、typeof、uint、ulong、unchecked、unsafe、using、value、virtual、volatile、while。
3.语句
•语句是应用程序中执行操作的一条命令。C#代码由一系列语句组成,每条语句都必须以分号结束。可以在一行中书写多条语句,也可以将一条语句书写在多行上。
•使用“{}”来表示应用程序中的不同层次和代码块。
4.注释
•(1)行注释:使用行注释标识符“//”,表示该标识符后的“一行”为注释部分。
•(2)块注释:块注释分别以“/*”和“*/”为开始和结束标识符,在此中间的内容,均为注释的部分。
•(3)文档注释:在C#中,还可以用“///”符号来开头。在一般情况下,编译器也会忽略它们,但可以通过配置相关工具,在编译项目时,提取注释后面的文本,创建一个特殊格式的文本文件,该文件可用于创建文档说明书。
5.命名空间
•命名空间有两种:系统命名空间和用户自定义命名空间。
•系统命名空间是一个逻辑的命名系统,用来组织庞大的系统类资源,让开发者使用起来结构清晰、层次分明、使用简单。
•同时,用户也可以使用自定义的命名空间以解决应用程序中可能出现的名称冲突。
6.类的定义和类的成员
•每一个C#应用程序都必须借助于.NET Framework类库实现,因此必须使用using关键字把.NET Framework类库相应的命名空间引入到应用程序的项目中来。
•C#的源代码必须存放到类中,一个C#应用程序至少要包括一个自定义类。自定义类使用关键字class声明,其名字是一个标识符。
•类的成员包括属性、方法和事件,主要由方法构成。例如控制台应用程序或Windows应用程序必须包含Main方法,Main方法是应用程序的入口。程序的运行时,从Main方法的第一条语句开始执行,直到执行完最后一条语句为止。
7.C#程序中的方法
•C#应用程序中的方法一般包括方法头部和方法体。
•方法头部主要包括返回值类型、方法名、形式参数(简称“形参”)类型及名称,若方法中包含多个形参,形参之间用逗号分隔。
•方法体使用一对“{}”括起来,通常包括声明部分和执行部分。声明部分用于定义变量,执行部分可以包含赋值运算、算法运算、方法调用等语句或语句块。
2.2 基本数据类型
1.数值类型
•值类型
值类型的数据存储在内存的堆栈中,可以提供快速访问。如果变量是值类型的,这个变量包含实际数据,在一个独立的内存区域保存自己的值;如果在代码中修改其值,在内存中会保存修改后的值。
当值类型在方法体内被声明时,它们都是被放置到栈上的。
值类型有时也会被放置到堆上,记住这条规则——值类型总是放在它们被声明的地方。如果一个值类型数据在方法体外被声明,且存在于一个引用类型中,那么它将被堆总的引用类型所取代。
•引用类型
引用类型指向存储在内存堆中的数据的指针或引用。与纯粹的地址不同,引用总是指向一个对象,而且这个对象具有指定的类型,并且在堆上分配了存储空间。
2.数值类型
数值类型可以分为整数类型和实数类型。
1.整数类型
•C#中支持8种整数类型:sbyte、byte、short、ushort、int、uint、long、ulong。这8种类型通过其占用存储空间的大小以及是否有符号来存储不同极值范围的数据,根据实际应用的需要,选择不同的整数类型。
•整数类型的相关说明如表2-2所示。
2.实数类型
•实数类型包括浮点型和小数型(decimal),浮点型又包括单精度浮点型(float)和双精度浮点型(double)。
•浮点型数据一般用于表示一个有确定值的小数。计算机对浮点数的运算速度大大低于对整数的运算速度,数据的精度越高,对计算机的资源要求越高。因此,在精度要求不是很高的情况下,尽量使用单精度类型(占用4个字节)。如果精度要求较高的情况,则可以使用双精度类型(占用8个字节)。
•因为使用浮点型表示小数的最高精度只能够达到16位,为了满足高精度的财务和金融计算领域的需要,C#提供了小数型(占用12个字节)。
3.字符类型
•字符类型包括字符型(char)和字符串型(string)。字符包括数字字符、英文字母、表达式符号等,C#提供的字符类型按照国际上公认的标准,采用Unicode字符集。
•字符型是指单个字符,一个Unicode的标准字符长度为16位,占用2个字节。char是类System.Char的别名。
•字符串型是指多个字符,其占用字节根据字符数量而定(允许不包含字符的空字符串)。string是类System.String的别名。
3. 变量与常量
1.变量
• 在程序运行过程中,其值可以改变的量称为变量。变量可以用来保存从外部或内部接收的数据,也可以保存在处理过程中产生的中间结果或最终结果。在C#中,每一个变量都必须具有变量名、存储空间和取值等属性。
1.变量的声明
• 要使用变量,就必须声明它们,即给变量指定一个名称和一种类型。变量命名应遵循标识符的命名规则,如:必须以字母、下划线等Unicode字符开头,不能以数字开头,不能包含空格。声明了变量后,编译器才会申请一定大小的存储空间,用来存放变量的值。
2.变量的赋值
在C#中,变量必须赋值后才能引用。为变量赋值,一般使用等号“=”。例如:
•char ch1,ch2;
•ch1 = ‘O’; ch2 = ‘K’;
•int a,b,c;
•a = b = c = 0;
3.变量的初始化
变量的初始化就是在定义变量的同时给变量赋初值,其一般形式为:
数据类型 变量名1 = 初值1 [,变量名2 = 初值2,... ];
例如:float f1=1.25, f2=3.6, f3;
2.常量
在程序运行过程中,其值保持不变的量称为常量。常量类似于数学中的常数。常量可分为直接常量和符号常量两种形式。
1.直接常量
所谓直接常量,就是在程序中直接给出的数据值。在C#中,直接常量包括整型常量、浮点型常量、小数型常量、字符型常量、字符串常量和布尔型常量。
1.直接常量
•(1)整型常量。整型常量分为有符号整型常量、无符号整型常量和长整型常量,有符号整型常量写法与数学中的常数相同,直接书写,无符号整型常量在书写时添加u或U标志,长整型常量在书写时添加l或L标记。例如,3、3U、3L。
•(2)浮点型常量。浮点型常量分为单精度浮点型常量和双精度浮点型常量。单精度浮点型常量在书写时添加f或F标记,双精度浮点型常量添加d或D标记。例如,7f、7d。
•需要注意的是,以小数形式直接书写而未加标记时,系统将自动解释成双精度浮点型常量。例如,9.0即为双精度浮点型常量。
•(3)小数型常量。在C#中,小数型常量的后面必须添加m或M标记,否则就会被解释成标准的浮点型数据。 C#中的小数和数学中的小数是有区别的。例如,5.0M。
•(4)字符型常量。字符型常量是一个标准的Unicode字符,使用两个单引号来标记。例如,’5’、’d’、’昱’ 、’#’都是标准的字符型常量。
C#还允许使用一种特殊形式的字符型常量,即以反斜杠“\”开头,后面跟字符的字符序列,这种字符型常量被称为转义字符常量。该形式的常量可以表示控制字符或不可见字符,当然也可以表示可见字符。例如,’\n’表示换行符,
•(5)字符串常量。字符串常量表示若干个Unicode字符组成的字符序列,使用两个双引号来标记。例如,“5”、“abc”、“清华大学”都是字符串。
•(6)布尔型常量。布尔型常量只有两个:一个是true ,表示逻辑真;另一个是false,表示逻辑假。
2.符号常量
•符号常量使用const关键字定义,格式为:
• const类型名称 常量名 = 常量表达式;
•“常量表达式”不能包含变量、函数等值会发生变化的内容,可以包含其他已定义常量。如果在程序中非常频繁地使用某一常量,可以将其定义为符号常量,例如:
• const double PI = 3.14159;
3.类型转换
• 在程序处理数据的过程中,经常需要将一种数据类型转化为另一种数据类型。数据类型的转换方式有两种:隐式转换和显式转换。
1.隐式转换
•隐式转换一般发生在数据进行混合运算的情况下,是由编译系统自动进行的,不需要加以声明。在该过程中,编译器无需对转换进行详细检查就能够安全地执行转换。隐式转换一般不会失败、不会出现致命隐患或造成信息丢失。例如:
•short s = 1; int i = s;
•需要注意的是,隐式转换无法完成由精度高的数据类型向精度低的类型转换。例如:
•int i = 1; short s = i; //错误,如果必须进行转换,就应该使用显式类型转换
2.显式转换
• 显式类型转换,又称为强制类型转换,该方式需要用户明确的指定转换的目标类型,该类型转换的一般形式为: (类型说明符)(需要转换的表达式)
•例如:
•short s = 7; int i = (int)s; //将s的值显式转化为int类型,并赋值于int类型变量i
显式转换包含所有的隐式类型转换,即把任何编译器允许的隐式类型转换写成显式转换都是合法的。显式类型转换并不一定总是成功,且转换过程中会出现数据丢失。
3. 使用方法进行数据类型的转换
•(1)Parse方法
• Parse方法可以将特定格式的字符串转换为数值,其使用格式为:
• 数值类型名称.Parse(字符串型表达式)
•例如:
•int i = int.Parse("100"); //字符串符合整型格式,转换成功
•int j = int.Parse(“100.0”);//字符串不符合符合整型格式,出错
•(2)Convert类的方法
•(3)ToString方法
4.引用类型
从数据存储的角度,C#的类型可分为值类型和引用类型。一个具有引用类型的数据并不驻留在栈内存中,而是存储于堆内存中。在堆内存中分配内存空间直接存储所包含的值,而在栈内存中存放定位到存储具体值的索引位置编号。
当访问一个具有引用类型的数据时,需要到栈内存中检查变量的内容,而该内容指向堆中的一个实际数据。
C#的引用类型包括类、接口、委托、数组和字符串等
1.类
•类是C#面向对象程序设计中最重要的组成部分,是最基本的编程单位,它由若干个数据成员、方法成员等组成。C#中的类需要使用class关键字来进行表示和声明,一个完整的类的定义示例如下:
class Student
{ int no; string name; char sex; int score;
public string Answer()
{
string result = "该考生信息如下:";
result += "\n学号:" + no;
result += "\n姓名:" + name;
return result;
}
}
2.接口
•接口是一种特殊的数据类型,接口与类的关系是:接口负责声明类的标准行为,而类负责实现这些行为。使用接口来设计程序的最大好处是实现了软件设计的规范化和标准化。
•在C#中,接口类型使用interface进行标识。一个完整的接口示例如下:
interfaceIStudent //声明接口
{
string Answer();
}
•注意,方法中不能够包含任何语句。
3.委托
•委托(delegate)相当于C++中指向函数的指针,但与C++的指针不同,委托完全是面向对象的,它把一个对象实例和方法都进行封装,所以委托是安全的。
•C#使用delegate来标记一个委托,其一般形式如下:
• delegate 返回值类型 委托名称(方法参数列表)
•一个完整的委托示例如下:
• delegate void MyDelegate(); //声明委托
•其中,MyDelegate是委托的名称,void表示该委托所指向的方法无返回结果,圆括号中没有方法参数列表,表示该委托指向的方法不需要参数。
4.数组
•数组是指同类数据组成的集合,它是数据最常用的存储方式之一。数组中包含的变量称为数组的元素,数组元素可以是包括数组类型在内的任何类型。
•C#支持的数组种类:
一维数组
规则的多维数组
不规则多维数组
• 数组能够存储整型、字符串等类型的数据,但是不论数组存储了多少个数据,其中的数据必须是同一种类型。
•任何数组都是System.Array的派生类
•为什么不直接使用System.Array?
–System.Array是抽象类,不能实例化
–System.Array假定成员数据类型是object
•对于值类型,会增加box/unbox的开销
•对于引用类型,使用时需显式转换
5.字符串
字符串是一个由若干个Unicode 字符组成的字符数组。字符串常量使用双引号来标记,如“string 123”就是一个字符串常量。
String和String在c#中是一样的,String类属于System命名空间,是.NET Framework提供的专门处理字符串的类库,string是String在C#中的别名。
•静态串string
String对象的内容是不可改变的,所以称做静态串。不可改变指的是只要创建一个String类型的对象,就不能再更改对象的值,任何赋值、修改操作都将被分配一个新值。
string lastName;
lastName = "Ming";
string firstName = "Zhang";
string name = firstName + " " + lastName;
string sameName = name;
char[] s2 = { '计', '算', '机', '科', '学' };
string s3 = new string(s2);
•可以使用String类的Compare、CompareOrdinal、CompareTo、Equals、EndsWith和StartsWith等方法进行字符串的比较。
•使用Copy和CopyTo方法可以将字符串或子字符串复制到另一个字符串或Char类型数组。
•使用IndexOf、IndexOfAny、LastIndexOf和LastIndexOfAny等方法可以获取字符串中的字符串。
•使用Substring和Split方法可以通过原始字符串的组成部分创建一个或多个新字符串。
•使用Concat和Join方法可以通过一个或多个子字符串创建新字符串。
•使用Insert、Replace、Remove、PadLeft、PadRight、Trim、TrimEnd、和TrimStart等方法可以修改字符串的全部或部分。
•使用ToLower、ToLowerInvariant、ToUpper和ToUpperInvariant等方法可以更改字符串中Unicode字符的大小写。
•使用Length属性可以获取字符串中Char对象的数量,使用Chars属性可以访问字符串中实际的Char对象。
•.NET Framework类库中的System.Text.StringBuilder类用来构造可变字符串,包含Length、Append、Insert、Remove、Replace、ToString等成员,分别用来获得字符串长度、追加字符、删除字符、替换字符和将StringBuilder转换成string等操作。
6.集合
•集合是通过高度结构化的方式存储任意对象的类。集合不仅能随意调整大小,而且对存储或检索存储在其中的对象提供了更高级的方法。而数组的类型必须是相同的,且创建时就必须知道数组内含有多少元素,还需要通过循环索引来访问这些元素。与数组相比,使用集合管理数据会更加方便。
• 实际上,数组是集合的一种类型。
1.集合类的选择
• 每个集合有其自身的功能及限制,集合专用性越强,其限制就越多。选择集合类时,一般要考虑以下问题:
•(1)是否需要随机访问集合中的元素。此时不能选择Queue 队列类、Stack栈类、LinkedList 双向链表类,而其余的集合可以提供随机访问。
•(2)是否需要一个序列列表。需要先进先出操作时,可使用 Queue 队列类;而需要后进先出操作时,可使用 Stack栈类。
•(3)是否包含一个值,还是一个键和一个值的集合。其中,“一个值”的集合是一种基于IList列表接口派生的集合,“一个键和一个值”的集合是一种基于IDictionary字典接口的集合。
2.集合的使用
• C#为用户提供了foreach语句,更好地支持了集合的使用。利用foreach 语句可以方便地遍历集合中的每一个元素,foreach 语句的表达式的类型必须是集合类型。
• foreach语句的格式为:
foreach ( 类型 标识符 in 表达式 )
{ 嵌入语句;
}
• foreach语句的“类型”和“标识符”声明该语句的迭代变量,迭代变量是一个其范围覆盖整个嵌入语句的只读局部变量。在foreach语句执行期间,迭代变量表示当前正在为其执行迭代的集合元素。
3.集合的创建与操作
经常使用的集合有ArrayList、Queue、Stack、Hashtable和SortedList,这些集合都位于System.Collections命名空间中,在使用时需要事先引用该命名空间。
(1)动态数组ArrayList。ArrayList的大小可根据需要自动扩充,允许在其中添加、插入或移除某一范围的元素。ArrayList的下限始终为零,且始终只是一维的。
• 创建动态数组对象的一般形式如下:
ArrayList 列表对象名 = new ArrayList( );
•
ArrayList和 Array的关系
•ArrayList和 Array 就像 StringBuilder和 String
–改固定容量为容量可变
–容量变化的方式:新生成更大容量的对象
–潜在的内存拷贝,如:Insert、Remove、RemoveAt等操作,可能会引起内存拷贝。若成员是引用类型,则仅拷贝引用
•ArrayList常用的方法和功能包括:Add,向数组中添加一个元素;Remove,删除数组中的一个元素;RemoveAt,删除数组中指定索引处的元素;Reverse,反转数组的元素;Sort,以从小到大的顺序排列数组的元素;Clone,复制一个数组。
(2)队列Queue:数据列表ArrayList的变种。
•队列是一种先进先出的数据结构,当插入或删除对象时,对象从队列的一端插入,从另外一端移除。
•创建队列对象的一般形式如下:
Queue 队列名 = new Queue([队列长度][,增长因子]);
说明:队列长度默认为32,增长因子默认为2.0。即每当队列容量不足时,队列长度调整为原来的2倍。
•队列包括Enqueue、Dequeue、Peek、Clear和Contains等方法,主要功能分别是添加队尾数据、移除并返回队头数据、返回队头数据、清空队列和检查是否包含某个数据。其中,Enqueue和Dequeue每操作一次只能添加或删除一个数据。
(3)栈Stack:数据列表ArrayList的变种。
• 栈是一种先进后出的数据结构,这种数据结构在插入或删除对象时,只能在栈顶插入或删除。
• 创建栈对象的一般形式如下:
Stack栈名 = new Stack( );
栈包括Push、Pop、Peek、Clear和Contain等方法,分别可以实现栈顶数据推进、栈顶数据弹出、返回栈顶数据、清空栈和检查栈中是否包含某个数据的操作。其中,Push和Pop每操作一次只能添加或删除一个数据。
(4)有序列表SortedList。
•列表中每个元素,对应一个key。
• SorterList类表示“键/值对”的集合,这些键和值按键排序并可按照键和索引访问。SortedList最合适对一列健/值对进行排序,在排序时,是对键进行排序。
• SortedList是Hashtable和Array的混合。当使用Item索引器属性按照元素的键访问元素时,其行为类似于Hashtable。当使用GetByIndex或SetByIndex方法按照元素的索引访问元素时,其行为类似于Array。
(5)哈希表Hashtable。
• 哈希表又称散列表,表示键/值对的集合。哈希表在保存集合元素时,首先要根据键自动计算哈希代码,以确定该元素的保存位置,再把元素的值放入相应位置所指向的存储桶中。查找时,再次通过键所对应的哈希代码到特定存储桶中搜索,这样可以极大的提高查找一个元素的效率。
• 创建哈希表对象的一般形式如下:
Hashtable哈希表名 = new Hashtable([哈希表长度][,增长因子]);
• 说明:哈希表默认长度为0,默认增长因子为1.0。
• 哈希表包括Add、Remove、Clear和Contains等方法,可以实现对哈希表数据的添加、移除、清空和查询。
5.值类型
引用类型用于存储对实际数据的引用,而值类型用于存储数据的值。值类型可以进一步划分为简单类型、枚举类型和结构类型,而简单类型包括前面介绍的整型、实数型、字符型和布尔型。
1.枚举
1.枚举类型的定义和使用
•枚举实际上是为一组在逻辑上密不可分的整数值提供便于记忆的符号,是一些取了名字的常量集合。
•例如: // 定义一个表示星期的枚举类型WeekDay
enumWeekDay { Sunday,Monday,Tuesday,Wednesday,
Thursday,Friday,Saturday };
WeekDay day; // 声明一个WeekDay枚举类型的变量day
•枚举类型定义好后,就可以用来声明变量。枚举类型的变量在某一时刻只能取枚举中某一个元素的值,如day的值要么是Sunday要么是Monday或其他的元素,但它在一个时刻只能代表具体的某一天,也不能是枚举集合以外的其他元素。
2.结构
结构类型是一种可以自己定义的数据类型,是一种可以包含不同类型数据成员的数据结构。在结构类型中可以声明多个不同数据类型的组成部分,这些组成部分被称为结构体的成员。结构体允许嵌套。
1.结构类型的定义
结构类型必须使用struct来标记。结构类型包含的成员类型没有限制,任何合法的成员都可以包含在一个结构体内。其中,数据成员表示结构的数据项,方法成员表示数据项的操作。
1.结构类型的定义
C#内置的结构类型主要有DateTime和TimeSpan。
DateTime表示某个时间点,其成员主要包括Year、Month、Day、Hour、Minute、Second、Today、Now等,分别代表年、月、日、时、分、秒、今天和现在时间。 TimeSpan代表某个时间段,其成员主要有Days、Hours、Minutes、Seconds,分表示整天数、整时数、整分数和整秒数。
3.装箱与拆箱
•在C#的程序设计中,值类型可以通过隐式或显式转换方法进行数据类型转换。对于引用类型,则可以将任何类型转换为对象,或将任何类型的对象转换为与之类型兼容的数据类型。
• C#把任何值类型转换为对象的操作称为装箱,而把对象转换为与之类型兼容的值类型的操作称为拆箱。
1.装箱
• 装箱是指将一个值类型变量转换为一个引用类型的变量。装箱的过程是:首先创建一个引用类型的实例,然后将值类型变量的内容复制给该引用类型实例。
1.装箱
• 在.NET中,Object类是所有类型的基类。所以,装箱意味着把一个值类型的数据转换为一个对象(object)类型的数据。
• 装箱过程是隐式转换过程,由系统自动完成,一般在赋值运算前完成。例如:
inti = 123;
object boxing = i;
• 上述代码运行时,先声明一个Object对象boxing,然后系统临时创建一个没有名字的Object对象,将整型变量i的值复制给它,再赋值给Object对象boxing。
2.拆箱
• 拆箱与装箱在逻辑上是一对互逆的过程。拆箱是指将一个引用类型显式地转换成一个值类型。需要指出的是,装箱操作可以隐式进行,但拆箱操作必须是显式的。
• 拆箱过程分成两步:首先,检查这个对象实例,看它是否为给定的值类型的装箱值;然后,把这个实例的值复制给相应值类型的变量。例如:
intval = 100;
object boxing = val; // 装箱
inti = (int)boxing +100; // 拆箱
• 拆箱意味着把一个对象类型数据转换为一个值类型数据,拆箱过程必须是显式转换过程。拆箱时先检查对象所引用的数据的类型,确保拆箱前后的数据类型相同,再复制出一个值类型数据。