文章教程

第2章C#基础

8/31/2020 9:50:22 PM 人评论 次浏览

第2章 C#基础

C#是一种由微软设计的简捷、类型安全、面向对象的语言,它松散地基于C/C++语言,并且有很多方面和Java语言类似。开发人员可以使用C#语言来构建在.NET Framework上运行的各种安全、可靠的程序。本书的ASP.NET的C#开发语言使用的环境是Visual Studio 2012。本章将简单学习C#的相关知识。

2.1 数据类型、变量与数组

在C#语言中,变量表示存储位置,数据类型决定变量的存储类型,而数组是一组数据类型相同的变量的集合。本节将学习数据类型、变量和数组这3个概念。

2.1.1 数据类型

she知识点讲解:光盘\视频讲解\第2章\数据类型.wmv

C#是一种强类型语言,所有的操作都会经过编译器的类型检查,非法操作将不能通过编译。因此,数据类型非常重要。C#语言中的每个类型都直接或间接派生于object类型,即object类型是C#语言所有类型的最终基类型。

C#语言的数据类型可以分为3种,如图2.1所示,具体说明如下。

029-1

图2.1 C#数据类型

  • 值类型(value-type):它的变量直接包含其数据。
  • 引用类型(reference-type):它的变量只存储对其数据的引用(即访问其数据的访问地址)。引用类型的变量又称为对象(object)。
  • 指针类型(point-type):和C、C++语言中的指针类似,而且只能用在不安全代码中。在C#编程中很少使用指针类型,因此在此不做详细介绍。

常用的一些数据类型及其取值范围如表2.1所示。

表2.1 数据类型及其取值范围表

030-1

注意:在计算机中,1个字节等于8位。因此,可以从表2.1中换算出各数据类型在内存中所占的字节数。

2.1.2 变量

she知识点讲解:光盘\视频讲解\第2章\变量.wmv

在C#语言中,变量就是一个数据存储空间的表示。根据不同的数据类型,在内存中给变量分配合适的空间。为了便于找到该内存空间的位置,给这个内存空间起的名字就是变量名。每一个变量的值由其数据类型确定。

1. 变量的定义

常用的变量定义形式如图2.2所示。

030-2

图2.2 变量的定义形式

注意:当在变量前面加上static关键字时,该变量就为静态变量。静态变量存在于整个程序运行期间。

【示例2-1】下面演示的是变量的定义方式。在代码中,定义了一个变量i。该变量i的数据类型为int(整型)。

031-1

2. 变量的初始化

一个变量一次只能接收一个值。常用的变量的初始化方式如图2.3所示。

031-2

图2.3 变量的初始化

【示例2-2】下面演示的是变量的初始化方式。定义了两个变量i和j,并为它们赋初值为0和10。最后一行将变量j的值赋给变量i。

031-3

在内存中,赋值前后变量i和j的值的变化如图2.4所示。

031-4

图2.4 变量i和j的值的变化

2.1.3 数组

she知识点讲解:光盘\视频讲解\第2章\数组.wmv

数组是一个有序的数据集合,通过数组可统一访问或操作这些元素。数组中每个元素的数据类型都相同。元素的数据类型被称为元素类型,它可以为任何类型,甚至可以为数组。在内存中,数组占用一块连续的内存,元素按顺序存放在一起,单独的元素并没有自己的名字,但是可以通过其位置来进行访问或修改它。

1. 数组的声明

在使用数组之前,必须先声明该数组。数组的声明方式如图2.5所示。

032-1

图2.5 数组的声明

注意:声明一个数组时,并不为其分配相应的内存空间。

【示例2-3】下面演示的是数组的声明方式。代码中声明了两个数组arr和arr2,数组arr是整型数组,而arr2是string类型的数组。

032-2

2. 数组的实例化

数组需要分配相应的内存后才能正常存取数据。实例化数组就是为数组分配相应的内存。数组的实例化方式如图2.6所示。

032-3

图2.6 数组的实例化

【示例2-4】下面演示的是数组的实例化方式。代码中,将示例2-3中声明的数组arr和arr2实例化,为两个数组分配内存。

032-4

3. 数组的初始化

数组的初始化就是初始化数组中每一个元素的值,即为数组中的每一个元素进行赋值。数组被实例化之后,每个元素都为其元素类型的默认值。一般情况下,只有初始化数组之后,该数组才具有实际运算的意义。

初始化数组有很多种方法,常用的有4种。

(1)直接赋值

直接赋值是指将一组值直接赋给数组,这组值被包括在大括号之内,每两个值之间用逗号分隔。如果直接赋值,不指定数组长度,系统会根据元素个数自动确定其值。直接赋值方式如图2.7所示。

033-1

图2.7 直接赋值

【示例2-5】下面演示的是为数组直接赋值。代码中为arr数组直接赋值为1,2,3,4,5。

033-2

(2)实例化赋值+指定长度

实例化赋值+指定长度是指给数组的每个元素赋值,而且指定数组长度。如果赋值的个数超过了数组长度,那么编译器就会报错。该赋值方式如图2.8所示。

033-3

图2.8 实例化赋值+指定长度

【示例2-6】下面演示的是为数组实例化赋值+指定长度。代码中使用实例化赋值+指定长度的方式为arr数组初始化,数组指定长度为5,即数组最多能保存5个元素。

033-4

(3)实例化赋值+省略长度

实例化赋值+省略长度是指给数组的每个元素赋值,而不指定数组长度。数组长度根据赋值的个数自动确定。该赋值方式如图2.9所示。

033-5

图2.9 实例化赋值+省略长度

【示例2-7】下面演示的是为数组实例化赋值+省略长度。代码使用实例化赋值+省略长度方式为数组初始化,由于未指定数组长度,因此系统根据值的个数自动确定数组的长度。

034-1

(4)直接设置数组的每一个元素值

也可以在程序中通过循环为数组中的元素进行初始化。

【示例2-8】下面演示的是直接为数组设置每一个元素的值。代码中声明了一个长度为5的整型数组,在for循环中为该数组的每一个元素进行赋值。

034-2

4. 数组的遍历

C#提供了foreach语句对数组元素进行遍历,该语句使用一种简单明了的方法来循环访问数组中的元素。foreach语句属于循环语句中的一种,它的基本格式如图2.10所示。

034-3

图2.10 foreach语句格式

用foreach语句并遍历数组(即对数组中的所有元素进行逐个代入),直到全部完成或者出现跳出语句为止。

【示例2-9】下面演示的是数组的遍历。

(1)创建一个网站,命名为“数组的遍历”。

(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面上添加一个Label控件,该页面的代码如下:

示例2-9:数组的遍历

源码路径:光盘\源文件\Chapter2\示例2-9\数组的遍历\Default.aspx

034-4

在Default.aspx页面的代码后置文件中声明一个数组,并使用foreach语句遍历数组,将数组元素显示在Label控件上。Default.aspx后置代码如下:

示例2-9:数组的遍历

源码路径:光盘\源文件\Chapter2\示例2-9\数组的遍历\Default.aspx

035-2

运行结果如图2.11所示。

035-3

图2.11 运行结果图

在该示例中,声明了一个数组arr,并为其赋初值。然后使用foreach语句遍历数组,并将数组元素显示在Label控件上。

2.2 表达式与运算符

计算机的核心功能就是运算。为了让计算机完成对应的工作,编程语言将常见的运算都预先设定好,并使用各种符号来表示,这些符号就是运算符。将这些符号和要处理的数据结合起来,就构成了表达式。本节将简单讲解C#中的各种运算符和表达式。

2.2.1 运算符

she知识点讲解:光盘\视频讲解\第2章\运算符.wmv

运算符是表达式中很重要的一部分,用于说明对表达式中的操作数进行什么样的运算,如+、-、*、/、%等。根据运算符所需操作数的个数,可以把运算符分为一元运算符、二元运算符和三元运算符。本小节介绍几种常用的运算符。

1. 算术运算符

算术运算是基本的数学运算,它包括加、减、乘、除。在C#中,分别使用+、-、*、/表示。同时,C#还使用%表示不常用的求余运算。它们的基本语法格式如图2.12所示。

036-1

图2.12 算术运算符的基本形式

2. 自增、自减运算符

自增、自减运算符都属于一元运算符,执行对操作数加1或者减1的操作。

(1)自增运算符

++运算符又称为增量运算符,其作用是对操作数加1。将++运算符放在操作数的左边,称该运算符为前缀增量运算符。否则,称为后缀增量运算符。自增运算符的语法如图2.13所示。

036-2

图2.13 自增运算符

“++ expression”表达式首先计算expression的值,然后将其值增1,并作为该表达式的结果。“expression ++”表达式首先计算expression的值,并作为该表达式的结果,然后将expression的值增1。

(2)自减运算符

--运算符又称为减量运算符,其作用是对操作数减1。将--运算符放在操作数的左边,称该运算符为前缀减量运算符。否则,称为后缀减量运算符。自减运算符的语法如图2.14所示。

037-1

图2.14 自减运算符

“-- expression”表达式首先计算expression的值,然后将其值减1,并作为该表达式的结果。“expression --”表达式首先计算expression的值,并作为该表达式的结果,然后将expression的值减1。

3. 逻辑运算符

&、^、|和!运算符称为逻辑运算符。&运算符计算两个操作数的按位逻辑与,|运算符计算两个操作数的按位逻辑或,^运算符计算两个操作数的按位逻辑异或。逻辑运算符语法如图2.15所示。

037-2

图2.15 逻辑运算符

注意:operator表示运算符,可以为&、^和|。

4. 条件运算符

?:运算符称为条件运算符,它是C#语言中唯一的一个三元运算符。条件运算符的语法如图2.16所示。

037-3

图2.16 条件运算符

该表达式首先计算条件表达式的值,如果该值为真,则计算resulta的值,并成为运算结果;否则计算resultb,并成为运算结果。

5. 关系运算符

==、!=、<、>、<=、>=运算符称为关系运算符。关系运算符的语法如图2.17所示。

037-4

图2.17 关系运算符

其中,==、!=、<、>、<=和>=运算符为比较运算符,它们的具体计算方法如下:

  • x == y,如果x等于y,则为true,否则为false。
  • x != y,如果x不等于y,则为true,否则为false。
  • x < y,如果x小于y,则为true,否则为false。
  • x > y,如果x大于y,则为true,否则为false。
  • x <= y,如果x小于等于y,则为true,否则为false。
  • x >= y,如果x大于等于y,则为true,否则为false。

6. 赋值运算符

=、*=、/=、%=、+=、-=、<<=、>>=、&=、^=和|=运算符被称为赋值运算符,它们能够为变量、属性、事件或索引器元素赋新值。赋值运算符的语法如图2.18所示。

038-1

图2.18 赋值运算符

7. 运算优先级

C#语言中的运算符存在着优先级。在计算表达式的值时,也必须遵循运算符的优先级的规则。当表达式包括多个运算符时,运算符的优先级控制各个运算符的计算顺序。根据运算符的优先级(从最高到最低),可以把C#中的运算符分为以下14类,如表2.2所示。

表2.2 运算符优先级

038-2

当操作数出现在具有相同优先级的两个运算符之间时,运算符的顺序与运算符本身的特性相关,具体说明如下所示。

  • 除了赋值运算符外,所有的二元运算符都是从左向右执行运算。如x+y+z表达式按照(x+y)+z表达式进行计算。
  • 赋值运算符和条件运算符是从右向左执行运算。如x=y=z表达式按照x=(y=z)表达式进行计算。

8. 类型转换

类型转换是指将数据从一种类型转换成另一种类型。如果被赋值的变量类型与实际的变量类型不同,就需要进行类型转换。C#提供了类型转换运算符。

(T)x运算符将表达式显式转换为指定的类型,语法如图2.19所示。

039-1

图2.19 类型转换

【示例2-10】下面使用(T)x运算符演示类型转换。首先声明一个object类型的变量,再使用类型转换符将obj转换成整型的值,赋给变量i。

039-2

2.2.2 表达式

she知识点讲解:光盘\视频讲解\第2章\表达式.wmv

表达式是一个由操作数和运算符构成的序列。运算符指定操作数的运算规则,即指示对表达式中的操作数进行什么样的运算。

注意:开发人员通过表达式计算各种数值。

C#中支持多个变量和运算符构建的表达式,比如计算变量i和j的和。具体代码如下:

039-3

在上面的代码中,i+j就是一个表达式。只要表达式成立,C#都可以计算该表达式。

2.3 语句

语句是代码最小的可执行单位,由一个或多个表达式组成。它将程序分割成一段一段的,是为了方便编程者和编译器识别代码段。C#程序是由各种各样的语句有序构建而成的,主要分为3类:标记语句、声明语句和嵌入语句。

2.3.1 基本语句

she知识点讲解:光盘\视频讲解\第2章\基本语句.wmv

C#语言包含多种常用的基本语句,如空语句、标记语句、表达式语句、声明语句和块等。本小节将简单介绍这些语句。

1. 空语句

空语句不包括任何实际性语句,它什么都不做。当在要求有语句的上下文中不执行任何操作时,使用空语句。空语句的语法形式如图2.20所示。

040-1

图2.20 空语句

2. 标记语句

标记语句可以给语句加上一个标签作为前缀,它可以出现在块中,但是不允许它们作为嵌入语句。标记语句的语法形式如图2.21所示。

040-2

图2.21 标记语句

3. 表达式语句

表达式语句是C#程序代码中最为常见的一种语句。它用来计算所给定表达式的值,而且由该表达式计算的值将被丢弃。实际上,表达式的值只是一个中间结果。表达式语句包括以下7类语句。

  • 赋值表达式:如i = 20;等。
  • 调用表达式:如Count();等。
  • 创建对象表达式:如object obi = new object();等。
  • 前缀递减表达式:如-- i;等。
  • 前缀递增表达式:如++ i;等。
  • 后缀递减表达式:如i --;等。
  • 后缀递增表达式:如i ++;等。

执行一个表达式语句,就是对该语句所包含的表达式进行计算,然后将控制转到该表达式语句的结束点。

4. 声明语句

声明语句用来声明局部变量或常量,它可以出现在块中,但不允许它们作为嵌入语句使用。声明语句包括声明局部变量语句和声明局部常量语句。声明局部变量语句用来声明一个局部变量。

5. 块

块用于编写多条语句,并且可以将整个块看成单个语句。块的语法形式如图2.22所示。

041-1

图2.22 块

注意:块中的语句列表可以缺省。语句列表是由一个或多个顺序编写的语句组成的。如果语句列表不存在,则称该块为空。

2.3.2 选择语句

she知识点讲解:光盘\视频讲解\第2章\选择语句.wmv

当程序中需要进行两个或两个以上的选择时,可以根据条件判断来选择将要执行的一组语句。C#提供的选择语句有if语句和switch语句,同时if语句又可以分为三种:简单的if语句、if...else语句和if...else...if语句。

1. 简单的if语句

if语句是最常见的选择语句,它根据布尔表达式的值来判断是否执行后面的内嵌语句。当表达式的值为真或者成立时,执行语句;表达式为假或者不成立时,跳过语句。简单if语句的流程图以及通用形式如图2.23所示。

041-2

图2.23 简单的if语句

2. if...else...语句

if...else...语句也是选择结构中常常用到的选择控制语句。if...else...语句比if语句多了一种选择情况的执行,也就是else后面语句的执行。if...else...语句的流程图及其通用形式如图2.24所示。

041-3

图2.24 if...else...语句

在图2.24中的if...else...语句中,当表达式的值为真时,则执行语句1,而当表达式的值为假时,则执行语句2。

3. if...else...if语句

if...else...if语句即多重if语句,是一种多重分支选择结构。if...else...if语句的流程图及通用形式如图2.25所示。

042-1

图2.25 if...else...if语句

在图2.25的if...else...if语句中,当表达式1的值为真,则执行语句1;若表达式1的值为假,则判断表达式2;其结果为真,则执行语句2;若表达式2的值也为假,则判断表达式3;其结果为真,则执行语句3,后面依此类推。

4. switch语句

if语句每次判断只能实现两条分支,如果要实现多种选择的功能,可以采用switch语句。switch语句根据一个控制表达式的值选择一个内嵌语句分支来执行。switch语句的流程图及其通用形式如图2.26所示。

042-2

图2.26 switch语句流程图

注意:使用switch语句时,需要注意以下几点。

(1)控制表达式的数据类型可以是sbyte、byte、short、uint、long、ulong、char、string或枚举类型。

(2)每个case标签中的常量表达式必须属于或能隐式转换为控制表达式的类型。

(3)如果有两个或者两个以上case标签中的常量表达式值相同,编译时将会报错。

(4)switch语句中最多只能有一个default标签。

(5)要求每个标签项后使用break语句或跳转语句goto(稍后将会讲到)。

2.3.3 循环语句

she知识点讲解:光盘\视频讲解\第2章\循环语句.wmv

循环语句可以实现一个程序模块的重复执行,对于简化程序、更好地组织算法有着重要的意义。C#提供了四种循环语句:while语句、do...while语句、for语句和foreach语句。它们分别适用于不同的情形。

1. while语句

while语句能够按照不同条件执行一个嵌入语句零次或多次。while语句的流程图及通用形式如图2.27所示。

043-1

图2.27 while语句

注意:在while语句中,首先计算条件表达式的值,如果条件表达式的值为真,则执行语句;否则结束while循环。

2. do...while语句

do...while语句和while语句非常相似,它能够按照不同条件执行一个嵌入语句一次或多次。do...while语句的流程图和通用形式如图2.28所示。

043-2

图2.28 do...while语句

注意:在do...while语句中,首先执行语句,然后判断条件表达式,如果条件表达式的值为真,则继续执行语句;否则结束do...while循环语句。

3. for语句

for语句一般用于执行确定次数的循环。该语句首先计算一个初始表达式序列,然后当某个条件为真时,重复执行相关的嵌入语句并计算一个迭代表达式序列。for语句的流程图及其通用形式如图2.29所示。

在图2.29所示的for循环中,表达式1和表达式2确定一个范围。表达式1用来计算该确定范围的第一个值,表达式3用于根据当前的值来计算下一个值;如果当前值在该确定范围之内,则执行语句。三个表达式都不是必需的,可以存在或者不存在。

044-1

图2.29 for循环

4. foreach语句

foreach语句用于枚举一个集合的元素,并对该集合中的每个元素执行一次相关的嵌入语句。foreach语句的流程图及其通用形式如图2.30所示。

044-2

图2.30 foreach语句

foreach语句执行期间,变量名是一个只读的局部变量,表示当前正在为其执行循环的集合元素。和for语句一样,foreach语句中嵌入语句被执行的次数也是可以事先计算出来的。在foreach语句执行过程中,永远不会发生数组索引溢出的问题。

2.3.4 跳转语句

she知识点讲解:光盘\视频讲解\第2章\跳转语句.wmv

跳转语句用于无条件地转移程序的控制。跳转语句主要包括5种语句:break语句、continue语句、return语句、throw语句和goto语句。

1. break语句

break语句用于终止最近的封闭循环或它所在的switch语句,控制传递给终止语句后面的语句(如果有的话)。break语句的通用形式如图2.31所示。

044-3

图2.31 break语句

在使用break语句时,需要注意下面两点:

(1)循环中可能有多条break语句。此时,必须要小心,因为太多的break语句可能破坏代码结构。

(2)终止switch语句的break语句只影响该switch语句,不影响包含它的外层循环。

注意:break语句只能用在switch、while、do...while、for或foreach语句中,否则编译时发生错误。如果break语句位于多层嵌套的switch、while、do...while、for或foreach语句中,break语句只能应用于最里层的语句。

【示例2-11】下面演示break语句的用法。示例代码中有嵌套循环,外层循环是for循环,内层循环是while循环。在while循环中使用break语句,当执行到此处,只会终止内层的while循环。

045-1

2. continue语句

continue语句用于跳出本次循环,开始直接封闭它的while、do...while、for或foreach语句的一次新循环。它的语法形式如图2.32所示。

045-2

图2.32 continue语句

在不同的循环中,continue语句执行的方式不一样,具体如下:

(1)在while和do...while循环中,continue语句将使得程序控制流直接跳转到条件表达式,然后继续循环过程。

(2)在for循环中,先要计算循环的迭代表达式(即for循环的表达式3),然后测试条件表达式,最后继续循环过程。

注意:continue语句只能用在while、do...while、for或foreach语句中,否则编译时发生错误。如果continue语句位于多层嵌套的while、do...while、for或foreach语句中,continue语句只能应用于最里层的语句。

3. return语句

return语句一般用于函数成员的返回操作,即将控制返回到函数成员的调用方。它的语法形式如图2.33所示。

045-3

图2.33 return语句

图2.33中的expressionopt为返回值的表达式,是一个可选表达式。

4. throw语句

throw语句用来引发一个异常。它的语法形式如图2.34所示。

046-1

图2.34 throw语句

图2.34中的expressionopt为返回值的表达式,它是一个可选表达式。

5. goto语句

使用goto语句可以无条件地将控制转移到其他程序段。goto语句的格式如图2.35所示。

046-2

图2.35 goto语句

控制转移的目标是由一个标签标记的。合法的目标位置包括goto语句当前级或当前循环的外部。

注意:goto语句一定不能跳进循环内。同时,不建议大家经常使用,因为滥用goto语句会使程序无结构性、可读性差。

2.4 类、对象、方法和属性

类是数据和相关处理的代码的封装体,它构成了面向对象编程的核心。类是从对象抽象出来的,要实现重用,必须由类构建出对应的对象。方法表示类和对象中的操作,它是包含一系列语句的代码块,通过这些代码块能够实现预先定义的计算或操作。属性是一种用于访问对象或类的特性的成员,为数据访问提供了一种简化的方式。使用属性可以增强程序的安全性。本节将学习类、对象、方法和属性。

2.4.1 类和对象

she知识点讲解:光盘\视频讲解\第2章\类和对象.wmv

类(class)是一种数据结构,它可以包含数据成员、函数成员和嵌套类型。类是从对象抽象而来的,对象是类的具体化。

1. 类

类是指整个一类事物,一个类定义了一个模板。类对数据以及处理数据的方法进行了封装,是对某一类具有相同特性和行为的事物的描述。类和变量一样,在使用之前必须声明。声明类需要使用class关键字,语法如图2.36所示。

047-1

图2.36 类的声明

在类中,有一些需要介绍的概念,具体如下:

(1)类修饰符

不同的类需要有不同的访问权限,因此使用类修饰符来限定类。C#用多种修饰符来表达类的不同性质。类修饰符放在class关键字的前面,它包括new、public、protected、internal、private、abstract和sealed 7个关键字。其中,public、protected、internal和private修饰符控制类的可访问性。它们的意义具体有如下说明:

  • public修饰符表示该类是公开的,访问不受限制。
  • protected修饰符表示该类只能被本身或其派生的类访问。
  • internal修饰符表示该类只能在当前应用程序中被访问。
  • private修饰符表示该类只能被本身访问。

而abstract修饰符指定类为抽象类;sealed修饰符指定类为密封类,即它不能被继承。

(2)继承类

在程序开发中,有一些类是通用的。就像“学生”类、“教师”类,都是基于“人”这个类,都有姓名、性别、年龄这些成员。通过继承,可以创建通用类,该类定义一组相关对象所共有的特征。然后,该类可以被其他更具体的类继承,再添加一些特有的成员。关于继承的具体知识将在2.5节介绍。

2. 对象

类是抽象的,不能赋值,对象是类的具体化。因此,要使用类的成员,必须由类构建相应的对象。这个过程称为实例化。实例化是一种操作,它可以为类的实例分配内存。实例化对象的语法如图2.37所示。

047-2

图2.37 实例化对象

注意:实例化对象以分号(;)结束,而且第二个类名后的一对括号不可省略。

【示例2-12】下面演示的是如何定义类和对象。在代码中声明了一个类Program,并为该类实例化了一个对象program1。

048-1

2.4.2 方法

she知识点讲解:光盘\视频讲解\第2章\方法.wmv

方法是类和对象中的操作,它是包含一系列语句的代码块,可以改变对象的状态。方法主要分为三种:Main()方法、静态方法和实例方法。Main()方法是C#程序的入口,必不可少;静态方法是类的方法,由类来调用;实例方法是对象的方法,由对象调用。

1. 方法的声明

方法一般在类或结构中声明,由访问级别、返回值、方法名称、方法参数和方法体组成。其中,访问级别、返回值、方法名称和方法参数统称为方法的“签名”。方法参数包括在小括弧“()”中,多个参数使用“,”(逗号)分隔。如果括弧中为空,则表示该方法不需要参数。普通的方法称为实例方法。方法的基本声明方式如图2.38所示。

048-2

图2.38 方法的基本声明形式

如果在方法的返回值前面还加了一个关键字static,则说明该方法是类的方法,即静态方法。静态方法的基本声明形式如图2.39所示。Main()方法有如图2.40所示的4个版本。

048-3

图2.39 静态方法

048-4

图2.40 Main()方法

2. 方法的调用

方法定义好了以后,方法的执行就表明方法被调用。静态方法是类的方法,由类直接调用。实例方法是对象的方法,必须由对象进行调用。Main()方法在程序开始运行时被系统调用。

【示例2-13】下面演示的是方法的调用。

049-1

该示例在Program类中定义了三个方法,一个静态方法f1(),一个实例方法f2(),一个Main()方法。Main()方法是程序的入口,必不可少。方法f1()是静态方法,由类Program调用。方法f2()是实例方法,由声明的Program类的对象p来调用。

3. 构造函数和析构函数

构造函数是类的一种特定的方法。使用构造函数有两个重要的作用:一是防止字段出现系统不合理的初始化;二是更方便对字段进行初始化,简化代码。构造函数的名称和其所属类的名称相同。在任何时候,只要创建类,就会调用它的构造函数。如果开发人员没有为类显式地提供构造函数,则默认情况下,C#编译器将为该类创建一个默认的构造函数。

(1)实例构造函数

实例构造函数是通过对象调用的,用于创建和初始化实例。该构造函数在实例化对象时被调用。示例代码如下:

049-2

(2)静态构造函数

静态构造函数用于初始化类的静态成员。在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数。它也可以用于执行仅需执行一次的特定操作。示例代码如下:

050-1

注意:静态构造函数是不可继承的,而且不能被直接调用。类的静态构造函数在给定应用程序域中至多执行一次,它在第一次创建类的实例或第一次引用类的任何静态成员时被调用。

静态构造函数具有以下5个特点:

  • 静态构造函数一般用于初始化静态字段、只读字段的值。
  • 静态构造函数既没有访问修饰符(它默认为私有的),也没有参数。声明静态构造函数时,需要添加static修饰符。
  • 无法直接调用静态构造函数,也无法控制何时执行静态构造函数。
  • 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。
  • 如果类中包含Main()方法,那么该类的静态构造函数将在调用Main()方法之前执行。如果类包含任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项。

(3)析构函数

计算机中的资源,使用完毕就应该释放掉,给别的程序使用。析构函数就是用于手动释放类中实例占用的资源,并销毁该类的实例。类的构造函数用来初始化该类的实例,析构函数和类的构造函数恰恰相反。析构函数的名称和类的名称一样,并带有~字符。通常情况下不需要使用析构函数。

2.4.3 属性

she知识点讲解:光盘\视频讲解\第2章\属性.wmv

属性是C#语言所特有的一种机制,它可以用来访问对象或类的特性成员。属性和变量非常相似,而且访问属性和变量的语法相同。但是,属性不表示存储位置(变量表示一个存储位置)。属性通过一种被称为访问器的机制来获取或修改其值。

1. 访问器

访问器本质是一种方法。获取属性的值的访问器被称为get访问器,修改属性的值的访问器被称为set访问器。get访问器和set访问器的具体说明如下:

  • get访问器相当于一个无参数方法,且该访问的返回值的类型和属性的类型相同。在get访问器中,必须包含return语句,返回该属性的值。
  • set访问器相当于一个返回类型为void的方法,且该方法只有一个参数,参数的类型和属性的类型相同。特别地,该方法的参数名称始终约定为value。

如果在声明属性时使用了static修饰符,则称该属性为静态属性,否则称该属性为实例属性。静态属性与实例属性的差别和静态变量与实例变量的差别相似。

2. 属性的声明

属性同变量一样,在使用之前需要声明。属性有名称,可以包含get访问器或set访问器。以读写属性(同时具有get访问器和set访问器的属性)为例,属性的声明方式如图2.41所示。

051-1

图2.41 属性的声明

注意:定义属性的目的在于方便一些私有字段的访问,因此建议大家在命名属性时采用与字段名相关联的属性名。

【示例2-14】下面演示的是属性的声明,示例代码如下:

在该示例中,首先声明了一个私有变量i,然后声明了一个属性I。该属性I是一个读写属性,具有get和set访问器。

051-2

3. 属性的访问

属性声明后,只要出现属性名都将引发相应的访问器调用。

【示例2-15】下面演示的是属性的访问。示例代码如下:

051-3

在该示例中,声明了一个属性I,并通过实例化的对象p调用属性I,继而调用了访问器。当为属性I赋值的时候,调用的是set访问器;需要获取属性I的值时,调用的是get访问器。

4. 属性的分类

根据访问器的不同,属性可以分为三类:只读属性、只写属性和读写属性。具体如图2.42所示。

052-2

图2.42 属性的分类

注意:在一个属性中,最多只能包含一个get访问器和一个set服务器,否则会提示错误。

2.5 继承

she知识点讲解:光盘\视频讲解\第2章\继承.wmv

继承是面向对象程序设计的一个重要特征。类支持继承,继承是一种机制,它使派生类可以对基类进行扩展和专用化。继承最主要的好处就是能够实现代码重用,新出现的类可以利用已定义类的成员。

谈到继承就肯定会涉及基类和派生类。被继承的类称为基类,继承的类称为派生类。

1. 派生类的声明

派生类声明后即可继承基类的特性,同时还可以根据需要定义自己的特性。派生类的声明方式如图2.43所示。

053-1

图2.43 派生类

2. 可继承性

在C#中,有的成员可以继承,有的成员却不能继承。C#的可继承性如图2.44所示。

053-2

图2.44 可继承性

3. C#继承规则

在C#中继承的规则有三点,如图2.45所示。

053-3

图2.45 C#继承规则

4. 继承示例

使用继承后,派生类可以直接使用基类的成员,而不需要再次声明。下面看一个继承的示例。

【示例2-16】下面演示的是在派生类中直接使用基类的方法。

053-4

在该示例中,Program类是Test类的基类,在Program类中声明了一个实例方法f(),在派生类中即可直接进行调用。这样就实现了代码重用的目的。

2.6 委托和事件

委托是指一个能够引用方法的对象调用它所指向的方法。事件构建在委托的基础上,它是一种信号机制。当事件触发时,将调用该事件事先定义的方法。本节将简单了解委托和事件。

2.6.1 委托

she知识点讲解:光盘\视频讲解\第2章\委托.wmv

委托是一个方法链条,通过调用委托,可以调用不同的方法。所有委托类型的基类是System.Delegate类。

1. 委托的声明

委托同类一样,在使用之前需要进行声明。委托的声明需要使用delegate关键字,通用形式如图2.46所示。

054-2

图2.46 委托的声明

【示例2-17】下面演示的是委托的声明,示例代码如下:

054-3

在该示例中声明了一个委托MyDelegate。该委托是公有类型,返回值类型是整型,具有一个类型为整型的参数。

注意:委托声明后,需要向委托注册方法,即添加方法列表。注册到委托中的方法的签名(签名包括方法的访问级别、返回值类型、参数类型和个数),必须同该委托指定的前面完全匹配。

2. 委托的创建

委托是一个类,有两种创建方式。第一种,可以使用new关键字创建委托的实例。委托构造函数只有一个参数,即添加到该委托的第一个方法。委托的创建方式如图2.47所示。

055-1

图2.47 委托的创建方式一

第二种创建委托的方式不需要使用new关键字。创建方式如图2.48所示。

055-2

图2.48 委托的创建方式二

【示例2-18】下面演示的是委托的创建。

055-3

在该示例中,首先声明一个委托MyDelegate,然后在Program类中定义了一个方法f(),方法f()的签名必须与委托匹配,最后使用两种方法创建委托实例del和del2。

3. 委托的调用

委托是一个方法链条,因此,调用委托实例就相当于调用该委托实例包含的所有方法。

【示例2-19】下面演示的是委托的调用。

示例2-19:委托的调用

源码路径:光盘\源文件\Chapter2\示例2-19\示例2-19\Program.cs

055-4

运行结果如图2.49所示。

056-2

图2.49 运行结果图

在该示例中,声明了一个委托MyDelegate,定义了两个方法f()和f2(),然后创建了四个委托实例del、del2、del3、del4,最后,分别调用委托del、del2、del3、del4。调用委托实例即调用了该委托实例包含的所有方法。因此,调用del即调用了方法f(),调用del2即调用了方法f2(),调用del3即调用了方法f()和f2(),调用del4即依次调用了方法f()、f2()、f()、f2()。

2.6.2 事件

she知识点讲解:光盘\视频讲解\第2章\事件.wmv

事件构建在委托的基础上。事件在某些操作发生时自动地发出通知。

1. 事件的声明

事件同委托一样,使用之前需要声明。事件的声明方式如图2.50所示。

057-1

图2.50 事件的声明

【示例2-20】下面演示的是事件的声明。

057-2

在该示例中,首先声明一个委托MyDelegate,然后声明一个事件MyEvent。

注意:因为事件是基于委托的,所以需要先声明委托。

2. 事件的订阅与取消订阅

引发事件的类称为“发行者”,处理事件的类称为“订阅者”。因此,“发行者”和“订阅者”之间是多对多的关系。一个订阅者可以订阅多个不同的事件。事件的订阅是通过运算符+=来实现的。而事件的取消订阅是通过运算符-=来实现的。

(1)事件的订阅

事件订阅的一般格式如图2.51所示。

057-3

图2.51 订阅事件

(2)事件的取消订阅

事件取消订阅的一般格式如图2.52所示。

058-1

图2.52 取消订阅事件

【示例2-21】下面演示的是事件的订阅与取消订阅。

058-2

在该示例中,声明了一个委托MyDelegate、一个事件MyEvent、两个方法f()和f2()。在Main()方法中创建了两个委托实例del、del2,使用+=运算符实现事件MyEvent的订阅,使用-=运算符实现事件MyEvent的取消订阅。

3. 事件的调用

声明一个事件之后,如果没有向该事件注册方法,那么该事件的值为空(null)。因此,在调用事件时,往往需要检查该事件是否为空。下面以一个示例说明事件的调用方式。

【示例2-22】在示例2-21的基础上,演示事件的调用方法。

示例2-22:事件的调用方法

源码路径:光盘\源文件\Chapter2\示例2-22\示例2-22\Program.cs

059-1

运行结果如图2.53所示。

060-2

图2.53 运行结果图

在该示例中,使用if语句判断事件不为空时,调用事件,调用语句为p.MyEvent(10);。第一次调用事件时,事件中包含两个方法f()和f2(),依次调用这两个方法。第二次调用事件时,事件中只包含一个方法f2(),因此,只调用方法f2()。

2.7 命名空间

she知识点讲解:光盘\视频讲解\第2章\命名空间.wmv

命名空间提供了一种区分不同程序段的声明区域,所有的C#程序都以命名空间进行区分。不同命名空间中相同名称的成员不会发生冲突。本节将简单了解C#的命名空间。

1. 命名空间的声明

命名空间在使用之前,也需要进行声明。命名空间的声明方式如图2.54所示。

060-3

图2.54 命名空间的声明

其中,命名空间的成员可以是用户声明的类、结构、委托、枚举和接口,也可以是另一个命名空间。

2. 命名空间的使用

命名空间的存在,就像一个一个的房间,各个房间的东西可以重名,但不相互冲突。下面以一个示例来说明命名空间的使用方法。

【示例2-23】下面演示命名空间的使用方法。

示例2-23:命名空间的使用方法

源码路径:光盘\源文件\Chapter2\示例2-23\示例2-23\Program.cs

061-1

运行结果如图2.55所示。在该示例中,声明了两个命名空间“示例2_23”和“A”。在这两个命名空间中都有类Class1。但是因为这两个类不在同一个命名空间中,因此不会发生冲突。如果两个相同的成员名位于同一个命名空间中,系统会提示错误。

062-1

图2.55 运行结果图

3. using指令

在示例2-23中,使用命名空间A中的类Class1时,需要使用A.Class1去限定。using指令的出现使得可以不使用“.”运算符限定类。using指令的一般形式如图2.56所示。

062-2

图2.56 using指令的形式

使用using指令可以将命名空间中所包含的类型导入到指定的程序体或文件中。使该程序体或文件中的代码可以直接访问该命名空间中的类型,而不需要加上类型的“.”限定符。下面以一个示例说明using指令的用法。

【示例2-24】下面演示的是using指令的用法。

示例2-24:using指令的用法

源码路径:光盘\源文件\Chapter2\示例2-24\示例2-24\Program.cs

062-3

运行结果如图2.57所示。

063-2

图2.57 运行结果图

在该示例中,使用using指令导入自定义的命名空间A。在Main()函数中使用类Class1的时候就不需要使用“.”运算符去限定命名空间A。可以直接使用类名Class1。

2.8 泛型

she知识点讲解:光盘\视频讲解\第2章\泛型.wmv

泛型是一种具有占位符的类、方法等。C#的泛型可以将数据类型作为参数来传递。泛型使用一个通用的数据类型T来代替object。在类实例化的时候知道T的类型,运行时自动替换T,提高了代码的质量和运行效率。本节将简单了解一下泛型。

1. 类型形参

类型形参是一个简单标识符,它只起占位符的作用。类型形参放置在类名后,并在“<”和“>”分隔符中指定。通常情况下使用T来作类型形参。

2. 泛型类

一个泛型类一般至少包含一个类型形参。同普通类一样,泛型类在使用之前必须声明。泛型类的声明方式如图2.58所示。泛型类是特殊的类,拥有自己的成员,如变量、方法等。泛型类的所有成员都可以直接使用其所属类的类型形参。

064-1

图2.58 泛型类的声明

3. 泛型方法

泛型方法指的是使用类型形参的方法。泛型方法可以在泛型类或非泛型类中声明。泛型方法和普通方法一样,在使用之前必须声明。泛型方法的声明方式如图2.59所示。

064-2

图2.59 泛型方法声明

【示例2-25】下面演示的是泛型类与泛型方法的使用方法。

示例2-25:泛型类及其泛型方法的使用

源码路径:光盘\源文件\Chapter2\示例2-25\示例2-25\Program.cs

064-3

运行结果如图2.60所示。

065-2

图2.60 运行结果图

在该示例中,定义了一个泛型类Program和一个非泛型类Test,在两个类中各定义了泛型方法f()。泛型类Program中的方法f()实现的是交换两个变量的值;非泛型类Test中的方法f()实现的是输出形参的值。

2.9 小结

本章主要介绍了ASP.NET配套的C#基础知识,包括数据类型、变量、数组、表达式、运算符、语句、类、对象、方法、属性、继承、委托、事件、命名空间和泛型。本章内容是使用ASP.NET制作网站所需要的基本知识,希望读者认真学习。

2.10 本章习题

习题2-1 在Visual Studio 2012中新建一个控制台应用程序,命名为chapter2_1。项目创建成功后的“解决方案资源管理器”面板如图2.61所示。在该Program类中声明一个string类型的数组,赋初值为hello、everyone、!。最后使用foreach语句遍历数组,打印输出数组元素,运行结果如图2.62所示。

066-1

图2.61 解决方案资源管理器

066-2

图2.62 运行结果图

【分析】本题目主要考查的是对数组的声明、初始化和遍历的掌握情况。使用Visual Studio 2012创建控制台应用程序时,选择“文件”|“新建项目”命令,后续步骤如图2.63所示。

066-3

图2.63 创建控制台应用程序

【关键代码】代码如下:

            string[] str = { "hello", "everyone","!" };
            Console.WriteLine("数组元素如下:");
            foreach (string s in str)
            {
                Console.Write(s+" " );
            }

注意:需要在Main()函数中添加Console.Read();代码,否则看不到运行结果。

习题2-2 在Visual Studio 2012中新建一个控制台应用程序,命名为chapter2_2。该程序在for语句中嵌套于一个if...else...if语句,以判断数字是正数、负数或正负数分界线。数字范围是-3到5,运行结果如图2.64所示。

067-1

图2.64 运行结果图

【分析】本题目主要考查的是对if...else...if语句和for语句的掌握情况。if...else...if语句是选择语句,for语句是循环语句。

【关键代码】代码如下:

            for (int i = -3;i <= 5; i++)
            {
                Console.Write("数字" + i + ":");
                if (i < 0)
                    Console.WriteLine("是负数。");
                else if (i == 0)
                    Console.WriteLine("正负数分界线。");
                else
                    Console.WriteLine("是正数。");
            }

注意:需要在Main()函数中添加Console.Read();代码,否则看不到运行结果。

习题2-3 在Visual Studio 2012中新建一个控制台应用程序,命名为chapter2_3。该程序中声明了一个静态方法S()和一个实例方法F()。在Main()函数中分别调用这两个方法,运行结果如图2.65所示。

068-1

图2.65 运行结果图

【分析】本题目主要考查的是对静态方法和实例方法的掌握情况。静态方法是类的方法,通过类名直接调用。实例方法是对象的方法,通过对象进行调用。

【关键代码】代码如下:

        public static void S()
        {
            Console.WriteLine("调用方法S()");
        }
        public void F()
        {
            Console.WriteLine("调用方法F()");
        }
        static void Main(string[] args)
        {
            Program.S();
            Program p = new Program();
            p.F();
            Console.Read();
        }

注意:需要在Main()函数中添加Console.Read();代码,否则看不到运行结果。

习题2-4 在Visual Studio 2012中新建一个控制台应用程序,命名为chapter2_4。该程序中声明了一个委托Delegate,该委托的返回值为空,无参数。然后定义了一个与委托匹配的方法F()。最后在Main()函数中创建了一个委托的实例d,d可以调用方法F()。运行结果如图2.66所示。

068-2

图2.66 运行结果图

【分析】本题目主要考查的是对委托的声明、创建委托实例的掌握情况。委托的声明需要使用关键字delegate。

【关键代码】代码如下:

        public delegate void Delegate();
        public void F()
        {
            Console.WriteLine("调用方法F()");
        }
        static void Main(string[] args)
        {
            Program p = new Program();
            Delegate d = new Delegate(p.F);
            d();
            Console.Read();
        }

注意:需要在Main()函数中添加Console.Read();代码,否则看不到运行结果。

习题2-5 在Visual Studio 2012中新建一个控制台应用程序,命名为chapter2_5。该程序中有两个命名空间,一个是chapter2_5,一个是Namespace。在Namespace命名空间中定义了一个类Test,该类中定义了一个整型变量i和一个返回值为空的方法F(),在方法F()中打印输出i的值。在chapter2_5命名空间中定义了一个类Program,该类中写的是Main()方法。Main()方法中创建了一个Test类的对象t,使用t调用方法F(),在控制台打印输出“i的值是:10”字符串。运行结果如图2.67所示。

068-3

图2.67 运行结果图

【分析】本题目主要考查的是对命名空间的掌握情况。

【关键代码】代码如下:

using Namespace;
namespace chapter2_5
{
    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
            t.F();
            Console.Read();
        }
    }
}
namespace Namespace
{
    class Test
    {
        int i = 10;
        public void F()
        {
            Console.WriteLine( "i的值是:" + i );
        }
    }
}

教程类别