本书以Visual Studio 2012为开发平台,以C#为开发语言,系统论述了使用ASP.NET技术进行Web应用程序设计等内容。 全书共10章,分别介绍Web基础知识、ASP.NET概述、C#程序设计基础、ASP.NET控件、ASP.NET内置对象、数据库基础知识、ASP.NET数据库编程、用户界面设计、教务管理系统实训和强大的LINQ查询等内容。每章都配有本章小结和习题,以方便读者巩固所学知识。特别地,在应用性较强的章中,多加一节具有实际应用的示例,便于读者更好地将理论与实践相结合。本书还专门设置了一章小型案例系统实训,以教务管理系统为例,通过系统分析和设计、数据库设计、网站设计和详细设计把所介绍的知识融合应用并把软件的开发流程呈现给用户,突出了系统性和实践性,使读者达到学以致用的目的。 本书适合作为普通高等院校计算机及其相关专业的教材或参考书,也可作为初、中级网站开发者及动态网页设计者或其他业余爱好者的参考用书。
目錄:
目录
第1章Web基础知识
1.1Web技术基础
1.2Web结构
1.3网页构成技术HTML
1.4静态网页和动态网页
1.4.1静态网页
1.4.2动态网页
1.5常见的网络程序设计语言
本章小结
习题
第2章ASP.NET概述
2.1.NET Framework简介
2.2ASP.NET简介
2.3ASP.NET运行及开发环境
2.3.1IIS7.5的安装
2.3.2IIS7.5的配置
2.3.3Visual Studio 2012集成开发环境
2.4第一个ASP.NET程序
2.4.1创建Web项目
2.4.2Web项目的构成
2.4.3创建ASP.NET网页
2.5创建ASP.NET程序的步骤
本章小结
习题
第3章C#程序设计基础
3.1C#基础语法
3.1.1数据类型和常量、变量
3.1.2运算符和表达式
3.2流程控制语句
3.2.1选择语句
3.2.2循环语句
3.3常用的.NET框架类
3.3.1DateTime类
3.3.2Math类
3.3.3Random类
3.4C#面向对象编程
3.4.1类与对象
3.4.2类的成员
3.4.3继承
本章小结
习题
第4章ASP.NET控件
4.1ASP.NET控件概述
4.1.1HTML控件
4.1.2HTML服务器控件
4.1.3Web服务器控件
4.2常用的Web控件
4.2.1标签控件
4.2.2文本框控件
4.2.3命令类控件
4.2.4图像控件
4.2.5超链接控件
4.2.6选择类控件
4.3其他常用控件
4.3.1容器控件
4.3.2日历控件
4.3.3文件上传控件
4.4数据验证控件
4.4.1RequiredFieldValidator非空验证
4.4.2RangeValidator范围验证
4.4.3CompareValidator比较验证
4.4.4RegularExpressionValidator规则验证
4.4.5CustomValidator自定义验证
4.4.6ValidatorSummary验证总结
4.5用户控件
4.5.1创建用户控件
4.5.2添加用户控件
4.6Web服务器控件的综合应用
本章小结
习题
第5章ASP.NET内置对象
5.1对象概述
5.2Page对象
5.2.1Page对象简介
5.2.2Page对象生命周期
5.2.3利用Page对象进行页面初始化
5.3Response对象
5.3.1Response对象简介
5.3.2Write方法和WriteFile方法
5.3.3Redirect方法
5.3.4BufferOutput属性
5.3.5End方法
5.3.6Flush方法和Clear方法
5.4Request对象
5.4.1Request对象简介
5.4.2获取用户提交的信息
5.4.3获取客户端浏览器信息
5.5Cookie对象
5.5.1Cookie对象简介
5.5.2读写Cookie
5.6Session对象
5.6.1Session对象简介
5.6.2利用Session对象存储信息
5.6.3TimeOut属性
5.6.4Abandon方法
5.7Application对象
5.7.1Application对象简介
5.7.2利用Application对象存储信息
5.7.3Global.asax文件
5.8Server对象
5.8.1Server对象简介
5.8.2ScriptTimeout属性
5.8.3HTMLEncode方法
5.8.4UrlEncode方法
5.8.5MapPath方法
5.8.6Execute方法和Transfer方法
5.9内置对象的综合应用
本章小结
习题
第6章数据库基础知识
6.1数据库概述
6.1.1数据库的基本概念
6.1.2数据管理技术的产生和发展
6.1.3数据模型
6.1.4常用的数据库管理系统
6.2创建Access数据库
6.2.1启动Access并创建数据库
6.2.2创建数据表并输入数据
6.2.3数据表的查询操作
6.3SQL语言
6.3.1SQL语言常用命令
6.3.2SELECT语句
6.3.3INSERT语句
6.3.4UPDATE语句
6.3.5DELETE语句
本章小结
习题
第7章ASP.NET数据库编程
7.1数据源控件
7.2数据绑定控件
7.2.1添加数据源控件
7.2.2添加GridView控件
7.2.3GridView分页、排序、列标题设置
7.2.4GridView选择、编辑、删除数据
7.2.5GridView超链接字段
7.3ADO.NET方式访问数据库
7.3.1ADO.NET概述
7.3.2ADO.NET数据访问流程
7.3.3常用ADO.NET对象的使用
7.3.4事务处理
7.4访问数据库的高级操作
7.4.1利用ADO.NET访问两种数据之间的转换
7.4.2在Web.config中配置数据库连接
7.4.3DataSet对象的高级应用
7.4.4GridView控件的高级应用
7.4.5其他数据绑定控件
7.5数据库访问技术的综合应用
本章小结
习题
第8章用户界面设计
8.1母版页
8.1.1母版页的基础知识
8.1.2母版页的创建
8.1.3母版页的使用
8.2站点导航
8.2.1站点地图
8.2.2站点导航控件
8.3主题
8.3.1主题概述
8.3.2创建主题
8.3.3应用主题
8.4用户界面设计的综合应用
本章小结
习题
第9章教务管理系统实训
9.1系统分析与系统设计
9.1.1系统分析
9.1.2系统设计
9.1.3系统结构图
9.1.4系统流程图
9.2数据库设计
9.3网站设计
9.3.1网站结构图
9.3.2页面功能
9.3.3站点导航设计
9.3.4母版设计
9.4详细设计
9.4.1数据库的建立
9.4.2公共类的编写
9.4.3配置文件Web.config的设置
9.4.4首页页面
9.4.5管理员页面
9.4.6教师页面
9.4.7学生页面
9.5网站发布
本章小结
习题
第10章强大的LINQ查询
10.1认识LINQ
10.2LINQ语法基础
10.3LINQ to Object
10.4LINQ to DataSet
本章小结
习题
参考文献
內容試閱:
序言前言
目前,Web应用程序设计一般都使用ASP.NET、JSP和PHP。ASP.NET由Microsoft公司提出,易学易用、开发效率高,可配合任何一种.NET语言进行开发。JSP需配合使用Java语言。PHP的优点是开源,缺点是缺乏大公司的支持。JSP和PHP较ASP.NET要难学。实际上,国内外越来越多的软件公司已应用ASP.NET技术进行Web应用程序开发。本书基于Visual Studio 2012开发环境,以C#为脚本,通过通俗易懂的语言和丰富典型的实例,由浅入深、循序渐进地讲述使用ASP.NET技术进行Web应用程序开发的方法。书中实例全部出自编者实际教学和工作过程中所采用的实例,都在Visual Studio 2012上编译通过,以方便读者自学理解。书中源程序注释清晰明了,方便读者自行修改和升级。全书共10章,分别介绍Web基础知识、ASP.NET概述、C#程序设计基础、ASP.NET控件、ASP.NET内置对象、数据库基础知识、ASP.NET数据库编程、用户界面设计、教务管理系统实训和强大的LINQ查询等内容。每章都配有本章小结和习题,以方便读者巩固所学知识。与市场上其他ASP.NET方面的图书相比,本书具有以下特点。1. 循序渐进,轻松上手本书是一线教师多年教学和实践的总结,编者长期从事.NET方向程序设计的教学和研究工作,对教学的难点和重点十分清楚,对学生的学习误区也有一定的了解。本书力求符合学生学习心理和学习习惯,合理安排各章节,以实例由浅入深地阐述如何利用ASP.NET技术以C#语言为基础进行Web应用程序的开发,让学生能够逐步体会并掌握利用.NET框架进行Web开发的精髓。2. 实例丰富,贴近实际本书每部分内容都有实例,简单易懂,帮助读者理解相关知识内容。特别地,在应用性较强的章节中,多加一节具有实际应用的示例,便于读者更好地将理论与实践相结合。3. 图文并茂,步骤详细本书讲解技术和例题时,图文并茂,步骤详细。读者只需按照步骤操作,就可以体会编程带来的乐趣和成就感。4. 完整案例,融会贯通本书专门设置了一个完整、实用的小型案例系统实训作为独立的一章,以教务管理系统为例,通过系统分析和设计、数据库设计、网站设计和详细设计把所介绍的知识融合应用并把软件的开发流程呈现给用户,突出了系统性和实践性,使读者达到学以致用的目的。本书可作为普通高等院校计算机及其相关专业的教材或参考书,也可作为初、中级网站开发者及动态网页设计者或其他业余爱好者的参考用书。清华大学出版社的网站http:www.tup.com.cn上提供本书的多媒体课件和所有例题源代码,课件等资源下载及本书使用的相关问题,请联系fuhy@tup.tsinghua.edu.cn。本书由哈尔滨金融学院张玉芬担任主编,由哈尔滨金融学院赵立波、李康乐担任副主编,哈尔滨工程大学杨萌和中国电子科技集团公司第四十九研究所李冰冰参编。其中,第1章由李冰冰编写,第6章和第7章由张玉芬编写,第3章、第5章和第10章由赵立波编写,第2章、第4章和第8章由李康乐编写,第9章由杨萌编写,全书由张玉芬统稿。由于时间仓促,编者经验有限,书中难免会有疏漏和不足之处,敬请读者和同行们予以批评指正,使本书得以改进和完善。编者联系邮箱hlg_zyf@126.com。编者2016年9月
第3章C#程序设计基础3.1C#基础语法
ASP.NET的页面与业务逻辑代码是分开的,业务逻辑代码是用面向对象语言编写的,可以用.NET系列中的任一种语言开发,包括C、C#、J#、Visual Basic等。在本书中采用的是C#语言,在本章中主要介绍语法基础。3.1.1数据类型和常量、变量1. 数据类型
按数据的存储方式划分,C#的数据类型可分为值类型和引用类型。值类型在其内存空间中包含实际的数据,而引用类型中存储的是一个指针,该指针指向存储数据的内存位置。值类型的内存开销小,访问速度快,但是缺乏面向对象的特征; 引用类型的内存开销大在堆上分配内存,访问速度稍慢。值类型包括整数类型、实数类型、字符类型、布尔类型、枚举类型和结构类型。和值类型相比,引用类型不存储它们所代表的实际数据,但它们存储实际数据的引用。一个具有引用类型的数据并不驻留在栈内存中,而是存储于堆内存中。在堆内存中分配内存空间直接存储所包含的值,而在栈内存中存放定位到存储具体值的索引位置编号。当访问一个具有引用类型的数据时,需要到栈内存中检查变量的内容,而该内容指向堆中的一个实际数据。C#的引用类型包括类、接口、委托和字符串等。2. 类型转换在编程工作中,有很多时候需要不同类型的量相互转换,例如,用户在页面上输入的所有内容均为字符串类型,但是程序却想用某些内容做数学运算,此时需要在使用输入内容之前做一个类型转换。1 隐式转换隐式转换又称为自动类型转换,若两种变量的类型是兼容的或者目标类型的取值范围大于原类型时,就可以使用隐式转换。2 显式类型转换显式类型转换又称为强制类型转换,该方式需要用户明确地指定转换的目标类型,该类型转换的一般形式为:
类型标识符表达式
例如:
int i = int7.256;将float类型的7.256转换为int类型,并赋值于int类型变量i
3 使用Parse方法进行数据类型的转换每个数值数据类型都包含一个Parse方法,它可以将特定格式的字符串转换为对应的数值类型,其使用格式为:
数值类型名称.Parse字符串型表达式
例如:
string s1 ="30",s2="3.9";
int i = int.Parses1;字符串符合整型格式,转换成功
float j = float.Parses2; 字符串符合浮点格式,转换成功
int k = float.Parses2;字符串不符合整型格式,出错
4 使用ToString方法进行数据类型的转换ToString方法可将其他数据类型的变量值转换为字符串类型,其使用格式为:
变量名称.ToString
例如:
int i = 69;
string s = i.ToString ; s = "69"
5 使用Convert类的方法进行数据类型的转换在实际编程中,基本类型之间的相互转换是一种非常常见的操作。System.Convert类就是为这个目的而设计的,其功能是将一种基本数据类型转换为另一种基本数据类型。Convert类的所有方法都是静态方法,如表31所示,其使用格式为:
Convert.方法名原始数据
表31Convert类的常用方法
名称主 要 功 能
ToBoolean将指定的值转换为等效的布尔值ToByte将指定的值转换为8位无符号整数ToChar将指定的值转换为Unicode字符续表
名称主 要 功 能
ToDateTime将指定的值转换为DateTime类型ToDecimal将指定的值转换为Decimal数字ToDouble将指定的值转换为双精度浮点数字ToInt16将指定的值转换为16位有符号整数ToInt32将指定的值转换为32位有符号整数ToInt64将指定的值转换为64位有符号整数ToSByte将指定的值转换为8位有符号整数ToSingle将指定的值转换为单精度浮点数字ToUInt16将指定的值转换为16位无符号整数ToUInt32将指定的值转换为32位无符号整数ToUInt64将指定的值转换为64位无符号整数ToString将指定的值转换为与其等效的String形式
例如:
string s = "97";
int n = Convert.ToInt32s; n = 97
char c = Convert.ToCharn; ASCII码为97的字符是a,即c = ''a''
string str="123456789.123456789";
decimal dec = Convert.ToDecimalstr;dec=123456789.123456789
double d1 = Convert.ToDoubledec; d1=123456789.123457
int i = Convert.ToInt32d1;i=123456789
3. 常量在程序运行过程中,其值保持不变的量称为常量。常量可分为直接常量和符号常量两种形式。1 直接常量所谓直接常量,就是在程序中直接给出的数据值。在C#语言中,直接常量包括整型常量、浮点型常量、小数型常量、字符型常量、字符串常量和布尔型常量。(1 整型常量。例如,5、5U、5L。(2 浮点型常量。例如,3f、3d。需要注意的是,以小数形式直接书写而未加标记时,系统将自动解释成双精度浮点型常量。例如,9.0即为双精度浮点型常量。(3 小数型常量。在C#语言中,小数型常量的后面必须添加m或M标记,否则就会被解释成标准的浮点型数据,如7.0M。(4 字符型常量。字符型常量是一个标准的Unicode字符,用来表示字符数据常量时,共有以下几种不同的表示方式。① 用单引号将一个字符包括起来,如''R''、''8''、''李''。② 用原来的数值编码来表示字符数据常量,如''A''是65,''b''是98。虽然char型数据的表示形式与ushort无符号短整型相同,但ushort与char意义不同,ushort代表的是数值本身,而char代表的则是一个字符。例如,
char m = ''A'';
int k = m 32;k的值为97
③ C#提供了转义符,用来在程序中指代特殊的控制字符,常用的转义字符如表32所示。
表32C#常用转义字符
转 义 序 列产生的字符字符的Unicode值
\单引号0x0027\双引号0x0022\\反斜杠0x005c\0空字符0x0000\a响铃符0x0007\b退格符0x0008\f换页符0x000c\n换行符0x000a\r回车符0x000d\t水平制表符0x0009\v垂直制表符0x000b
(5 字符串常量。字符串常量表示若干个Unicode字符组成的字符序列,使用两个双引号来标记,如"book"、"123"、"中国"。(6 布尔型常量。布尔型常量只有两个: 一个是true,表示逻辑真; 另一个是false,表示逻辑假。2 符号常量符号常量使用const关键字定义,格式为:
const 类型名称常量名 = 常量表达式;
常量表达式不能包含变量、函数等值会发生变化的内容,可以包含其他已定义常量。如果在程序中非常频繁地使用某一常量,可以将其定义为符号常量,例如:
const double PI = 3.1415926;
const int Months = 12,Weeks = 52,Days = 365;
4. 变量在程序运行过程中,其值可以改变的量称为变量。变量可以用来保存从外部或内部接收的数据,也可以保存在处理过程中产生的中间结果或最终结果。在C#语言中,每一个变量都必须具有变量名、存储空间和取值等属性。【例31】编写一个网页,在文本框中输入圆的半径,单击计算按钮,显示圆的面积。具体实现步骤如下。(1) 新建一个网站,在资源管理器中右击网站,然后在弹出的快捷菜单选择添加Web窗体选项,在弹出的指定名称对话框中给网页命名。(2) 在网页中添加Label控件、TextBox控件、Button控件。页面设计如图31所示。
图31例31设计界面
3 在网页设计界面,选中Button1,在属性窗口中选中闪电标志,双击Click事件,为它添加单击事件代码如下:
protected void Button1_Clickobject sender, EventArgs e
{
double s,r;
const double PI = 3.14159;
r = double.ParseTextBox1.Text;
s = PI * r * r;
Label1.Text = "圆的面积是: " s.ToString;
}
程序运行结果如图32所示。
图32圆面积的运行结果
Windows的窗体程序都是事件驱动程序。当用户使用鼠标或键盘或其他输入设备做一个动作时,对于程序来说都是一个事件。例如,单击按钮,对于程序来说是发生了一个单击事件。在C#语言中,每个控件都有已经准备好的若干事件框架,编程人员按需要决定是否响应这些事件。响应的方式是选中某一控件,在属性窗口单击事件按钮,找到对应事件,双击即生成了事件处理方法,在事件处理方法中输入处理代码即可。如果某一事件对于这个控件来说是最经常发生的,那么这个事件就是它的默认事件,对于默认事件,可以直接在设计界面双击此控件,直接进入事件代码页面,并生成事件方法。例如,对于按钮来说,单击是最常发生的,所以例31也可以双击按钮响应单击事件。变量的类型包括整数类型、实数类型、单精度类型、双精度类型、字符类型、字符串类型、布尔类型、枚举类型和结构类型。它们的类型标识符分别为int、decimal、float、double、char、string、bool、struct、enum。除此之外,还有数组。变量的命名规则是: 变量名只能由字母、数字和下画线组成,不能包含空格、标点符号、运算符等其他符号。不能以数字开头。还要注意C#严格区分大小写,name和Name对于C#是完全不同的两个变量。1 变量的声明变量的声明格式为:
类型标识符 变量列表;
例如:
int x,y,z;合法
int 3old;不合法,以数字开头
float struct; 不合法,与关键字名称相同
float Main;不合法,与函数名称相同
数组变量的声明格式为:
类型标识符[] 数组名 = new 类型标识符[数组长度];
例如:
int[] arr = new int[5];
2 变量的赋值在C#语言中,变量必须赋值后才能引用。为变量赋值,一般使用赋值号=。例如:
char ch;声明一个字符型变量
ch = ''m''; 为字符型变量ch赋值
int a,b,c;
a = b = c = 0;同时为多个变量赋相同的值
bool b1 = true,b2 = false;声明布尔型变量b1和b2,同时为其赋值
数组的赋值可以在声明的时进行初始化,例如:
int[] arr = new int[5]{1,2,3,4,5};
如果在声明的同时进行初始化,就可以省略new类型标识符[数组长度]这部分,例如:
int[] arr = {1,2,3,4,5};
如果不在声明数组的同时进行初始化,数组必须按下标一个一个进行赋值或引用。
int[] arr = new int[5];
arr[0]=1;
arr[1]= arr[0] 1;
3.1.2运算符和表达式运算符用于对操作数进行特定的运算,而表达式则是运算符和相应的操作数按照一定的规则连接而成的式子。1. 算术运算符和算术表达式算术运算符有一元运算符与二元运算符。一元运算符包括 取正、-取负、自增、--自减; 二元运算符包括 加、-减、*乘、除、%求余。2. 字符串运算符与字符串表达式字符串运算符只有一个,即 运算符,表示将两个字符串连接起来。例如:
string s1 = "计算机" "编程"; s1的值为"计算机编程"
运算符还可以将字符串型数据与一个或多个字符型数据连接在一起,例如:
string s2 = ''A'' "bcd" ''E''; s2的值为"AbcdE"
3. 关系运算符与关系表达式关系运算又称为比较运算,实际上是逻辑运算的一种,关系表达式的返回值总是布尔值。关系运算符用于对两个操作数进行比较,以判断两个操作数之间的关系。C#中定义的比较操作符有= = 等于、!=不等于、大于、=大于或等于。关系表达式的运算结果只能是布尔型值,要么是true,要么是false。4. 逻辑运算符与逻辑表达式C#语言提供了4类逻辑运算符: &&条件与或&逻辑与、||条件或或|逻辑或、!逻辑非和^逻辑异或。其中,&&、&、||、|和^都是二元操作符,而!为一元操作符。它们的操作数都是布尔类型的值或表达式。5. 赋值运算符赋值运算符=称为简单赋值运算符,它与其他算术运算符结合在一起可组成复合赋值运算符,如*==%= =-=等。6. 条件运算符条件运算符由?和:组成,其一般格式为:
关系表达式?表达式1: 表达式2
条件表达式在运算时,首先计算关系表达式的值,如果为true,则运算结果为表达式1的值,否则运算结果为表达式2的值。例如:
int x = 50 , y = 80 , m;
m = x y ? x * 5 : y 20 ;m的值为100
条件表达式也可以嵌套使用,从而实现多分支的选择判断。这些常用运算符从高到低的优先级顺序如表33所示。
表33运算符的优先级
优先级类别运算符
1初级运算符2一元运算符 正-负!~--3乘除运算符*%4加减运算符 -5位运算符6关系运算符=7关系运算符==!=8逻辑与&9逻辑异或^10逻辑或|11条件与&&12条件或||13条件运算符? :14赋值运算符=*==%= =-==&=^=|=
3.2流程控制语句
虽然C#语言是完全面向对象的语言,但在局部的语句块内,仍然要使用结构化程序设计的方法,用控制结构来控制程序的执行流程。结构化程序设计有3种基本控制结构,分别是顺序结构、选择结构和循环结构。3.2.1选择语句常用的条件语句有如下几种。1. if语句if语句是基于布尔表达式的值来判定是否执行后面的内嵌的语句块,其语法形式有3种,分别为:
if表达式
{
语句块;
}
或者
if表达式
{
语句块1;
}
else
{
语句块2;
}
或者
if表达式1
{
语句块1
}
else if(表达式2)
{
语句块2
}
else if(表达式m)
{
语句块m
}
else
{
语句n
}
【例32】输入一个整数,求绝对值。程序分析如下。如果是负数,取反; 否则,绝对值是这个数本身。网页设计如图33所示。
图33例32网页设计
程序代码如下:
protected void Button1_Clickobject sender, EventArgs e
{
int x;
x = Convert.ToInt32TextBox1.Text;
if x 上午好!";
}
else
{
Response.Write"今天是" DateTime.Now.ToLongDateString "," "下午好!";
}
}
执行上述程序的运行结果如图37所示。
图37例37运行结果
3.3.2Math类C#语言中的Math类提供了一些常用的数学方法与属性,该类属于System命名空间。Math类是一个密封类,有两个公共字段和若干静态数学方法。Math类常用的方法与属性如表38所示。
表38Math类常用方法与属性
方法与属性主 要 功 能
Math.PI得到圆周率Math.E得到自然对数Math.Abs数值参数求绝对值方法Math.Cos弧度值求余弦值方法Math.Sin弧度值求正弦值方法Math.Tan弧度值求正切值方法Math.Max数值1,数值2求最大值方法Math.Min数值1,数值2求最小值方法Math.Pow底数,指数求幂方法Math.Round实数
Math.Round实数,小数位求保留小数值方法
Math.Sqrt平方数求平方根方法
3.3.3Random类Random类提供了产生随机数的方法,该方法必须由Random类创建的对象调用。 Random类属于System命名空间,创建对象的格式为:
Random 对象名 = new Random;
Random类的常用方法如表39所示。
表39Random类的常用方法
方法主 要 功 能对象名.Next产生随机数对象名.Next正整数产生0~指定正整数之间的随机数对象名.Next整数1,整数2产生两个指定整数之间的随机整数对象名.NextDouble产生0.0~1.0之间的随机实数
需要说明的是,使用Random对象产生随机数时,下界包含在随机数内,而上界不包含在随机数内。例如:
Random r = new Random;
int n = r.Next1,10;产生的随机数包含1,但不包含10。
3.4C#面向对象编程
面向对象技术是一种新的软件技术,其概念来源于程序设计,从20世纪60年代提出面向对象的概念,到现在已发展成为一种比较成熟的编程思想,并且逐步成为目前软件开发领域的主流技术。说明: 本节是为了方便介绍类与对象的概念,所有例题采用.NET平台中的控制台应用程序。控制台应用程序是指Windows下的单机应用程序,采用控制台输出程序结果。创建控制台应用程序的方法如下。1 选择文件新建项目选项,弹出新建项目对话框,如图38所示。
图38新建项目对话框
2 在该对话框中的左侧栏选中Visual C#语言,在中间栏选择控制台应用程序。3 在下方选择好项目名称和存储位置。3.4.1类与对象对象Object是整个面向对象程序设计的核心。什么是对象?在日常生活中,每天接触的每个具体的事物就是对象。例如,你的同学张某,走到路上看到的一辆白色Jeep越野车,校园里那只流浪的小狗,你的手机等一切具体而非抽象的食物。在计算机中如何来描述这些对象呢?例如,来描述白色Jeep越野车,可以描述这种车的静态特征: 品牌、颜色、价格、车重、油耗、动力等,这些静态特征被称为属性,只描述属性并不能描述一个生动的、漂亮的白色Jeep越野车,还可以描述这辆车的动态特性,如车的启动、刹车、鸣笛等,这样的动态特性被称为行为或方法,当为属性赋予具体数值,定义具体的方法的时候,一个对象就被创造出来了。对象是所有数据及可对这些数据施加的操作结合在一起所构成的独立单位的总称,是拥有具体数据和行为的独立个体。对象Object由属性Attribute和行为Action两部分组成。对象只有在具有属性和行为的情况下才有意义,属性是用来描述对象静态特征的一个数据项,行为是用来描述对象动态特征的一个操作。对象是包含客观事物特征的实体,是属性和行为的封装体。那么在赋值之前的那个状态的事物被称为什么呢?来看这个状态的特点: 它可以描述出某一类型的事物,换言之,这个状态描述的是一个模型,是抽象的、非具体的,它被称为类。类Class是对一组客观对象的抽象,是具有共同属性和操作的多个对象的相似特性的统一体。它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分,类是对象集合的再抽象。类与对象的关系如同一个模具与用这个模具铸造出来的铸件之间的关系。类给出了属于该类的全部对象的抽象定义,而对象则是符合这种定义的一个实体。所以,一个对象又称为类的一个实例Instance。在C#语言中,使用关键字class来定义一个类,格式如下:
class 类名
{
类成员
}
【例38】定义一个Person类。
class Person
{
}
【例39】定义一个Point类,它的作用是标明一个二维坐标。
class Point
{
}
定义类的最终目的就是用类来创建对象,对象才是人们编程的核心。当定义好一个类之后,可以用它来定义变量,也可以用它来创建对象也被称为实例化一个对象,创建对象使用关键字new,一般格式如下:
类名对象名 = new 类名;
或者,将声明和实例化分为两行代码:
类名对象名;
对象名 = new 类名;
注意: 关键字new后面的类名实际应该是构造函数名,到目前为止,只知道是类名就可以了,因为构造函数与类同名。【例310】为定义的Person类创建两个对象,名为s1和s2。程序代码如下:
class Program
{
static void Main
{
Person s1=new Person;声明并实例化一个对象
Person s2;声明一个变量
s2=new Person;将声明的变量实例化为一个对象
}
}
public class Person
{
}
当使用new关键字来实例化一个对象的时候,这意味着什么呢?当人们只是声明一个变量的时候,内存中并没有为这个变量分配相应的存储空间,只有当使用new关键字来实例化它时,内存中才为这个对象分配对应大小的存储空间,在每个对象的存储空间中,根据类的定义中所包含的成员,它们都有一份自己独立的数据,根据构造函数为对象中的各实例数据成员赋值,并返回一个对实例的引用,存储空间大小由对象的成员个数及类型来决定。这个阶段称为对象的构造阶段,初始化过程由构造函数完成。在应用程序结束之前的某个时候,对象最后一次被调用之后将被删除,这个阶段称为析构阶段,此时常常需要执行一些清理工作,如释放内存,系统的运行时会在最后一次访问对象之后,自动调用析构函数完成。这个析构过程是.NET系统的垃圾自动回收机制。从构造阶段到析构阶段被称为对象的生命周期。在这个示例中,没有数据或方法与Person类关联,所以相当于实例化了一个完全没用的对象。但一般的情况是当描述一个对象时,需要使用一些数据和动作来表示对象特征,它们被称为类的成员。面向对象程序设计的优点之一就是数据的安全性,安全性通过访问限制修饰符得到了保证,访问限制修饰符规定了类的每个成员的访问级别,它们有以下几种。(1) public: 成员可以由任何代码访问。(2) private: 成员只能由类中的代码访问默认关键字。(3) internal: 成员只能被项目内部的代码访问。(4) protected: 成员只能由本类或派生类中的代码访问。internal和protected两个关键字可以合并使用,所以也有protected internal成员,这样的成员只能由本类及项目中派生类的代码来访问。除了这些修饰符,还有上面提过的关键字static,static和这些关键字是可以根据需要并列使用的。原则上,将类的数据即字段定义为私有的,只能在类中的代码对其访问,防止类外代码改变重要数据造成错误; 一些成员如方法定义为公有的,能保证类外的代码对类正常访问和使用; 一些成员定义为受保护的,使得派生类能够访问,其他类不能访问。3.4.2类的成员1. 字段
在C#语言中,字段field的定义、作用及使用方法类似于面向过程语言如C语言里面的变量,定义的时候也需要指定存储的数据类型,除此之外,由于它是类的成员,还需要指定访问级别,因此字段定义格式如下:
访问限制修饰符 数据类型 字段名;
在使用类的成员时,如果在类的内部,直接使用字段的名字; 如果是在类的外部,要通过对象来使用,格式为对象名.成员名。可以和变量一样用=为字段赋值,也可以读取字段的值。【例311】为Person类添加年龄、姓名字段,为s1对象年龄字段赋值21,为s2对象年龄字段赋值22,并输出。程序代码如下:
class Person
{
public int age;
public string name;
}
在Program中为字段赋值并读取字段的值
class Program
{
static void Main
{
Person s1=new Person;
Person s2;
s2=new Person;
s1.name="John";为s1的name字段赋值为John
s1.age=21;为s1的age字段赋值为21
s2.name="Tom"; 为s2的name字段赋值为Tom
s2.age=22;为s2的age字段赋值为22
Console.WriteLine"{0}的年龄为{1},{2}的年龄为{3}.",s1.name,s1.age,s2.name,s2.age;
}
}
执行上述程序的运行结果如图39所示。
图39程序运行结果
在例311中,代码中的Person类增加了两个成员age和name,类型分别是整型和字符串型,访问控制为public。这意味着: 首先,在用new实例化类的一个对象时,每个对象的存储空间中都有这两个成员存在,而且可以计算出每个实例化的对象在内存中所占空间大小,它们等于这两个字段所占空间大小这里比较特殊的是string类型,string类型是一个没有上限的变量类型,对象p在内存中所占大小为16字节两个double变量的大小; 同时,在类的外部的代码中,只要实例化了该类的一个对象,就能访问这个对象的公共字段,可以修改它们的值如语句s1.age=21;,也可以读取它们的值如语句Console.WriteLine"{0}的年龄为{1},{2}的年龄为{3}.",s1.name,s1.age,s2.name,s2.age;。2. 方法方法又称为函数Function,是一种组合一系列语句以执行一个特定操作或计算一个特殊结果的方式。方法的定义格式如下:
访问限制修饰符 返回值类型 方法名参数类型参数1, 参数类型参数2,,参数类型参数n
{
方法体
return 返回值;
}
其中,访问限制修饰符返回值类型方法名参数类型参数1,参数类型参数2,,参数类型参数n包括参数的顺序被称为方法的签名。访问修饰符规定了方法被访问的级别; 返回值类型表明调用并执行此方法后是否得到一个值,得到的值是什么类型; 参数是调用者与方法之间交换数据的途径。可以看出,方法的签名唯一标识了某个类中的一个方法,在一个类中,不允许有相同签名的两个方法。下面先通过一个简单的示例初步认识方法的定义。【例312】为Person类添加一个输出个人信息的方法。程序代码如下:
class Person
{
public int age;
public string name;
public void PrintInfo此方法用来输出对象的信息
{
Console.WriteLine"My name is {0},Im {1}",name,age;
}
}
在Program中使用这个方法
class Program
{
static void Main
{
Person s1=new Person;
Person s2;
s2=new Person;
s1.name="John";
s1.age=21;
s2.name="Tom";
s2.age=22;
s1.PrintInfo;调用PrintInfo函数
s2.PrintInfo;
}
}
执行上述程序的运行结果如310所示。
图310程序运行结果
在Main函数中通过对象s1和s2调用了PrintInfo函数,Main函数被称为调用者。由于方法总是和类联系一起,调用一个方法概念上等价于向一个类发送一条消息。上面的示例中,由于PrintInfo方法只是简单地输出对象的个人信息,方法既没有参数,也没有返回值返回值类型标为void。3. 构造函数构造函数Constructor是一种特殊的成员函数,它主要用于为对象分配空间,完成初始化的工作。它为程序提供一种方式,在创建对象的同时指定所需的数据。构造函数有以下几个特点。1 构造函数的名称必须与类名相同。2 构造函数可以带参数,但没有返回值。3 构造函数只能在对象定义时,由new关键字自动调用,不能显示调用。4 如果没有给类定义构造函数,编译系统会自动生成一个默认的构造函数,其形式如下:
public 类名:base{ }
5 构造函数可以重载,但不能继承。【例313】为Person类添加一个构造函数,在此构造函数中为姓名和年龄提供初始值。程序代码如下:
class Person
{
private int age;
private string name;
public Personint inAge, string inName有参构造函数
{
name=inName;
age=inAge;
}
public void PrintInfo
{
Console.WriteLine"My name is {0},Im {1}",name,age;
}
public void SetInfoint inAge, string inName通过参数设置个人信息的方法
{
name=inName;
age=inAge;
}
}
在Program中使用这个有参构造方法为s1和s2对象初始化
class Program
{
static void Main
{
Person s1=new Person21,"Tom";
Person s2=new Person22,"John";
s1.PrintInfo;
s2.PrintInfo;
}
}
执行上述程序的运行结果如图311所示:
图311程序运行结果
在上面的代码中,可以看出在Person类中添加了一个有参构造函数,因此执行了Person s1=new Person21,"Tom";语句时调用这个构造函数为字段name和age初始化。值得注意的是,因为要在类之外的代码中创建类的对象,所以构造函数的访问限制修饰符一般情况下都为public。现在,请思考一个问题,在前面的代码中并没有构造函数出现,为什么能使用new来实例化一个对象呢?这是因为当没有在代码中显式地定义任何构造函数,C#编译器在编译时会自动添加一个如下形式的构造函数:
public Person:base
{
}
这个构造函数不获取参数,所以它被称为默认构造函数Default Constructor。必须注意的是,一旦为一个类显式添加了一个构造函数,C#编译器不再提供默认构造函数,即一旦添加了一个有参的构造函数,在Main中实例化一个Person时,就必须指定名字和年龄,如果再像以前那样使用语句Person s1=new Person;来实例化一个Person时,代码在编译的时候会产生错误。因为定义了public Personint inAge, string inName之后,编译器不再添加默认构造函数Person。如果此时仍想使用语句Person s1=new Person;来实例化一个Person时,需要手动添加这样的一个构造函数。【例314】为Person类添加一个有参构造函数在此构造函数中为姓名和年龄提供初始值和一个无参构造函数。程序代码如下。
class Person
{
private int age;
private string name;
public Personint inAge, string inName有参构造函数
{
name=inName;
age=inAge;
}
public Person 无参构造函数
{
}
public void PrintInfo
{
Console.WriteLine"My name is {0},Im {1}",name,age;
}
public void SetInfoint inAge, string inName通过参数设置个人信息的方法
{
name=inName;
age=inAge;
}
}
在Program中使用这个有参构造方法为s1对象初始化
class Program
{
static void Main
{
Person s1=new Person21,"Tom";
Person s2=new Person;
s2.SetInfo22,"John";
s1.PrintInfo;
s2.PrintInfo;
}
}
执行上述程序的运行结果如图312所示。
图312程序运行结果
可以看出来,语句Person s1=new Person21,"Tom";与Person s1=new Person; s1.SetInfo21,"Tom";或者s1.age=21;s1.name="Tome";对字段初始化的效果是一样的,只是初始化的时机不同。这样一来,Person类就有了两个构造函数,同样地,这两个构造函数名称相同,参数不同,称为构造函数的重载。4. 属性类还有一种十分重要的成员是属性。属性本质上和字段作用类似,也是用来读写类中的数据的,读写的方式和字段一样。同时,由于属性拥有两个类似函数的块一个用于获取属性的值,一个用于设置属性的值,在读写数据之前,属性还可以执行一些额外的操作,如判断读写行为是否合法,最终决定是否执行数据的读写操作。因此,属性对数据具有更好的保护性。再来说说这两个块。这两个块也称为访问器,分别用get和set关键字来定义,可以用来控制对属性的访问级别。可以忽略其中的一个块来创建只读或只写属性忽略get块创建只写属性,忽略set为只读属性。属性的定义方式如下。
访问限制修饰符返回值类型属性名
{
get
{
}
set
{
}
}
【例315】为Point类添加属性成员。程序代码如下:
class Point
{
private double x,y;
public Pointdouble a,double b
{
x = a;
y = b;
}
public double XProp
{
get
{
return x;
}
set
{
x = value;
}
}
public double YProp
{
get
{
return y;
}
set
{
y = value;
}
}
public void Display
{
Console.WriteLine"横坐标是{0},纵坐标是{1}",x,y;
}
}
class Program
{
static void Main
{
Point p = new Point5,10;
p.Display;
p.XProp = -5;直接为属性赋值
p.YProp = -10;
p.Display;
Console.WriteLine"属性XProp的值为{0},属性YProp的值为{1}",p.XProp, p.YProp; 直接像字段一样读取属性的值
}
}
执行上述程序的运行结果如图313所示。
图313程序运行结果
属性不仅能控制赋值范围,而且能控制数据是否可读写,还可以控制读写的访问范围。如果只有set块,就意味着只能给该属性赋值,该属性被称为只写属性; 如果只有get块,就意味着该属性只能读取,不能修改值,被称为只读属性。只读属性:
public int AgeProp
{
get
{
return age;
}
}
又如:
public int Age
{
get
{
return birthDate.Year-DateTime.now.Year 1;
}
}
只写属性:
public int AgeProp
{
set
{
if value 0 && value