精华内容
下载资源
问答
  • 7 内存分配 基本的内存分配问题 7.1 为什么这段代码不行?char*answer;printf("Typesomething:\n");gets(answer);printf("Youtyped\"%s\"\n",answer); 7.2 我的strcat()不行。我试了下面的代码:char*s1=...
  • 2019工程伦理慕课答案(2019秋)习题及期末答案

    万次阅读 多人点赞 2019-11-08 18:19:53
    习题(下) 单选题 (1/1 point) 下列哪一项不是工程与技术的区别 内容和性质 目的 活动主体 任务、对象和思维方式 单选题 (1/1 point) 下列哪一项不是工程活动的特征 自主性 创造性 ...

    第一章~第十章习题(上)及期末考试答案

    下面是第一章~第十章习题答案(下)

    第一章习题(下)

    单选题 (1/1 point)

    下列哪一项不是工程与技术的区别

    • 内容和性质
    • 目的
    • 活动主体
    • 任务、对象和思维方式

    单选题 (1/1 point)

    下列哪一项不是工程活动的特征

    • 自主性
    • 创造性
    • 社会性
    • 确定性

    多选题 (1points)

    下列哪项是工程的完整生命周期中的环节

    • 计划
    • 设计
    • 评估
    • 完成

    判断题 (1/1 point)

    计划、设计、建造、使用和结束构成了工程完整的生命周期

    • 正确
    • 错误

    判断题 (1/1 point)

    工程包括哲学、技术、经济、管理、社会、生态、伦理这7个维度。

    • 正确
    • 错误

    You have used 1 of 1 submissions
    单选题 (1points)

    以下哪项不是规范伦理学的立场

    • 功利论 功利论
    • 义务论
    • 契约论
    • 存在论

    单选题 (1points)

    下列哪项不是罗斯为直觉主义义务论提出的道德原则

    • 忠诚
    • 公平
    • 感恩
    • 不行恶

    单选题 (1/1 point)

    下列职业社团名称缩写错误的是

    • 美国土木工程师协会(ASCE)
    • 电子和电子工程师协会(IEEE)
    • 美国机械工程师学会(ABET)
    • 美国化学工程师学会(AIChE)

    判断题 (1/1 point)

    根据伦理规范得到社会认可和被制度化的程度,我们可以把伦理规范分为制度性的伦理规范和描述性的伦理规范两种情况。

    • 正确
    • 错误

    判断题 (1/1 point)

    伦理是个体性、主观性的,侧重个体的意识、行为与准则、法则的关系;道德则是社会性和客观性的,侧重社会“共体”。

    • 正确
    • 错误

    多选题 (1/1 point)

    当工程实践出现“超越于道德的”的情形时,我们可以通过道德慎思为自己的伦理行为划分优先顺序,审慎地思考和处理存在的几对重要的伦理关系。这几对重要的伦理关系指的是

    • 自主与责任
    • 效率与公正
    • 个人与集体
    • 环境与社会

    单选题 (1/1 point)

    下列哪项不是工程伦理问题的特点

    • 历史性
    • 社会性
    • 复杂性
    • 简明性

    单选题 (1/1 point)

    下列哪项不是处理工程伦理问题的基本原则

    • 人道主义
    • 功利主义
    • 社会公正
    • 人与自然和谐发展

    判断题 (1/1 point)

    工程活动是一种集成多种自然与社会资源,协调多种利益诉求和冲突的社会活动,是一种极其复杂的社会实践,需要众多的行动者参与。

    • 正确
    • 错误

    单选题 (1/1 point)

    下列哪项不是依据工程伦理问题的对象来划分伦理问题的?

    • 因伦理意识缺失或者对行为后果估计不足导致的问题
    • 因工程相关的各方利益冲突所造成的伦理困境
    • 工程共同体内部意见不合,或者工程共同体的伦理准则与规范等与其他伦理原则之间不一致导致的问题
    • 因工程伦理章程缺乏或者不足而导致的问题

    多选题 (1/1 point)

    “博帕尔MIC毒气泄漏事件”主要涉及哪几方面的问题

    • 企业和政府在维护公共安全和环境安全方面所肩负的责任和各自扮演的角色问题
    • 跨国工程活动中发达国家向发展中国家的环境成本转移问题
    • 跨国公司的双重技术标准问题
    • 如何通过相关法规和制度来保障工程活动中的弱势群体权益的问题

    判断题 (1/1 point)

    从处理工程与人、社会和自然的关系的三个层面看,处理工程中伦理问题要坚持以下三个基本原则:人道主义———处理工程与人关系的基本原则;社会公正———处理工程与社会关系的基本原则;人与自然和谐发展———处理工程与自然关系的基本原则。

    • 正确
    • 错误

    第二章习题(下)

    判断题 (1/1 分数)
    从普利高津耗散结构理论的视角来看,工程必然会伴随风险的发生

    • 正确
    • 错误

    判断题 (1/1 分数)
    在讨论工程设计理念时,只需要工程师代表参与决策

    • 正确
    • 错误

    单选题 (1/1 分数)
    意外风险的应对通常采取的措施包括风险回避、风险转移等,其中风险转移的目的是?

    • 降低风险发生的概率
    • 降低不利后果的大小
    • 在风险发生时将损失分散
    • 保护工人的安全

    单选题 (1/1 分数)
    在制定事故应急预案时,不恰当的做法是?()

    • 预防为主,防治结合
    • 保留现场,等待救援
    • 以人为本,生命第一
    • 统一指挥,协同联动

    单选题 (1/1 分数)
    下列哪项属于工程风险中的技术因素?()

    • 自然灾害
    • 工程设计理念的缺陷
    • 施工质量缺陷
    • 控制系统失灵

    多选题 (1/1 分数)
    工程风险的来源有哪些?()

    • 技术因素的不确定性
    • 环境因素的不确定性
    • 文化因素的不确定性
    • 人为因素的不确定性

    多选题 (1/1 分数)
    对于不能及时进行处理的工程质量缺陷,应填写质量缺陷备案表,表中应包括哪些信息?()

    • 缺陷产生的部位和原因
    • 对工程安全性的分析
    • 使用功能和运用影响分析
    • 处理方案和不处理原因分析

    文字填空题 (1/1 分数)
    操作人员是预防工程风险的核心环节,也是防止工程风险发生的最后一道屏障,必须要加强对操作人员安全意识的教育,时时刻刻以“ ”为行动准则。(四个字)

    答案:安全第一

    判断题 (1/1 分数)
    在制度层面,目前公众参与工程主要以网络投票为参与途径。

    • 正确
    • 错误

    判断题 (1/1 分数)
    公众参与工程风险伦理评估的前提是信息的公开。

    • 正确
    • 错误

    单选题 (1/1 分数)
    下列工程风险的伦理评估原则中哪项属于预防为主的原则?

    • 尊重当事人的“知情同意”权
    • 要加强安全知识教育,提升人们的安全意识
    • 考虑对社会环境和生态环境造成的影响
    • 建立并落实安全生产问责机制

    单选题 (1/1 分数)
    下列属于工程风险的外部评估主体的是()?

    • 工程师
    • 社会公众
    • 工人
    • 管理者

    单选题 (1/1 分数)
    对于可接受风险,以下哪种视角是专家视角()?

    • 可接受的风险是这样风险,在可以选择的情况下,伤害的风险至少相等于产生收益的可能性
    • 可接受的风险指的是这样风险,它是通过行使自由和知情同意权而自愿认可的,或者它是得到适当赔偿的,并且它是公正地分配的
    • 可接受的风险是这样风险,其保护公众免遭伤害的重要性远远超过了使公众获利的重要性
    • 采取任何手段,只要让公众自愿接收的风险

    判断题 (1/1 分数)
    当工程风险发生时,往往不能把全部责任归结于某一个人,而需要工程共同体共同承担

    • 正确
    • 错误

    判断题 (1/1 分数)
    伦理责任就是法律责任

    • 正确
    • 错误

    单选题 (1/1 分数)
    下列不属于“职业”的特征是?()

    • 理智性
    • 长期性
    • 自主性
    • 非垄断性

    单选题 (1满分)
    下列哪项不属于工程伦理责任类型?()

    • 职业伦理责任
    • 社会伦理责任
    • 环境伦理责任
    • 家庭伦理责任

    单选题 (1/1 分数)
    下列哪项超出了工程伦理责任类型?()

    • 过失-责任
    • 赔偿-责任
    • 角色-责任
    • 义务-责任

    第三章习题(下)

    判断题 (1/1 point)
    由工程的目标价值导向性引出一个重要的伦理问题,这就是工程为什么人服务,为什么目的服务?()

    • 正确
    • 错误

    单选题 (1/1 point)
    下列哪一项体现了工程的文化价值?()

    • 标志性工程是所属民族的精神纽带
    • 工程的“普遍接入”和“普遍服务”
    • 工程制造的科学仪器
    • 环境保护和生态修复工程

    判断题 (1/1 point)
    工程社会价值的一个极端表现是其军事价值。()

    • 正确
    • 错误

    单选题 (1/1 point)
    工程的()价值具有非道德性质,这决定了工程的最终价值取决于工程应用于什么目的,即工程的实际价值取决于社会的要求和社会环境。

    • 工具价值
    • 内在价值
    • 伦理价值
    • 社会价值

    多选题 (1/1 point)
    工程具有多元的价值,主要包括()

    • 科学价值
    • 政治价值
    • 社会价值
    • 文化价值

    单选题 (1/1 point)
    如何确定什么人可以首先享受到工程成果,或者如何确定人们享受的顺序,这属于工程的()问题。

    • 管理问题
    • 技术问题
    • 资源分配问题
    • 制度问题

    判断题 (1/1 point)
    讨论工程的利益分配可以从宏观和微观两个层面来进行,宏观层面是指企业内部工程项目的活动情况。

    • 正确
    • 错误

    单选题 (1/1 point)
    产品价格过高,会使得普通大众难以分享工程的好处,妨碍了实现工程成果为更多人()。

    • 所及和普惠
    • 接受
    • 认可
    • 理解

    判断题 (1/1 point)
    工程受益人群的确定由市场来调控,我们把不能获得工程产品和服务的现象,称作“排除”。()

    • 正确
    • 错误

    多选题 (1/1 point)
    下列哪些因素会影响工程产品和服务的可及性和普惠性?()

    • 用户的经济状况
    • 用户的知识水平
    • 用户的技术水平
    • 用户的能力问题

    判断题 (1满分)
    “邻避”行为突出反映了工程项目建设的利益—损失分配不公正的问题。()

    • 正确
    • 错误

    单选题 (1/1 point)
    “邻避”事件发生的原因很复杂,不一定是现实的危害,而是()。

    • 利益分配不公平
    • 居民对危害的心理担忧和风险感知
    • 利益补偿不合理
    • 公众参与不充分

    判断题 (1/1 point)
    传统的工程观主要考虑企业本身的收益和付出,很少考虑用户为工程付出的代价。()

    • 正确
    • 错误

    单选题 (1/1 point)
    一些工程建设引起的大规模的拆迁移民,可能增加社会秩序的不安定因素,这属于工程对()的影响。

    • 环境
    • 经济
    • 社会
    • 资源

    多选题 (1/1 point)
    米切尔认为,利益相关者必须具备以下哪些条件。()

    • 创造力
    • 影响力
    • 合法性
    • 紧迫性

    文字填空题 (1/1 point)
    公正是指,每个人都应获得其应得的权益,对平等的事物平等对待,不平等的事物()对待。(两个字)
    答案:区别

    单选题 (1满分)
    分配公正的基本实现途径是在不同利益与价值追求的个人与团体间的对话的基础上,达成有普遍约束力的()原则。

    • 社会公正
    • 利益协调
    • 分配与补偿
    • 道德伦理

    判断题 (1/1 point)
    吸收利益攸关方参加工程的决策、建设、运营是为了保证补偿公正。()

    • 正确
    • 错误

    多选题 (1/1 point)
    美国伦理学家理查德·T.德·乔治将公正分为以下哪些类型?()

    • 补偿公正
    • 惩罚公正
    • 分配公正
    • 程序公正

    多选题 (1/1 point)
    下列哪些属于工程项目社会评价的社会公平指标?()

    • 基尼系数
    • 恩格尔系数
    • 就业率
    • 公众参与度

    第四章习题(下)

    单选题
    提出保护资源的目的是为了自然本身的利益,而不是人类的利用,这是()主义的主张。

    • 社会保护主义
    • 环境保护主义
    • 资源保护主义
    • 自然保护主义

    单选题
    ()主张把道德关怀的范围从人类扩展到非人类的生命或自然存在物上。

    • 人类中心主义者
    • 非人类中心主义者
    • 价值主观论者
    • 价值客观论者

    判断题
    “大地伦理”的提出者利奥波德认为:一件事情当有助于保护自然的和谐、稳定和美丽时,它就是正确的,反之,就是错误的。()

    • 正确
    • 错误

    文字填空题
    如果我们承认了自然事物拥有内在价值,我们就认可了自然事物的(),我们就与道德义务维护自然事物。(四个字)
    答案:道德权利

    判断题
    认为自然界的价值不依人的存在或人的评价而存在,只要对地球生态系统的完善和健康有益的事物就有价值,是价值客观论者的主要观点。()

    • 正确
    • 错误

    多选题
    "尊重自然"的伦理体系包括以下哪几部分?()

    • 尊重自然的态度
    • 深层生态学
    • 生物中心主义
    • 人类中心主义

    多选题
    下列哪些选项属于非人类中心主义思想?()

    • 动物解放论
    • 动物权利论
    • 生物中心主义
    • 生态中心主义

    文字填空题
    对工程活动进行分析、预测和评估,提出预防或者减轻不良环境影响的对策和措施,这是对工程进行()评价。(四个字)
    答案: 环境影响

    判断题
    工程理念是工程活动的出发点和归宿,是工程活动的灵魂。()

    • 正确
    • 错误

    单选题
    好的工程会把()和合人的目的性有机结合起来。

    • 合工程的目的性
    • 合自然的规律性
    • 合社会的进步性
    • 合技术的发展性

    单选题
    在权衡人与自然利益的优先秩序上应遵循()高于基本需要、基本需要高于非基本需要。

    • 社会需要
    • 伦理需要
    • 生存需要
    • 发展需要

    单选题
    ()原则是我们对自然环境的首要态度,也是我们行动的首要原则。

    • 公平性原则
    • 整体性原则
    • 尊重原则
    • 平等性原则

    多选题
    工程建设对环境产生的影响主要包括()。

    • 消耗能源和资源
    • 造成的固废污染和水污染
    • 噪声和振动影响
    • 排出的有害气体威胁人们的健康

    单选题
    工程共同体的环境伦理主要指,工程过程应切实考虑()及社会的承受性。

    • 政府
    • 自然生态
    • 雇主
    • 公众

    文字填空题
    随着工程对自然的干预和破坏能力越来越巨大、后果越来越危险,工程师需要发展一种新的责任意识,即()(六个字)
    答案:环境伦理责任

    单选题
    工程师的环境伦理责任不只是赋予工程师责任和义务,同时也赋予他相应的(),使得他在必要时及时中止他的责任和义务。

    • 资源
    • 利益
    • 自由
    • 权利

    判断题
    对工程师的评价标准不是工程师是否把工作做好了,而是是否做了一项好到工作,既促进了经济发展,又避免了环境遭到破坏。()

    • 正确
    • 错误

    判断题
    工程共同体通常由项目投资人、设计者、工程师、工人构成。()

    • 正确
    • 错误

    多选题
    下列选项哪些属于工程师的环境伦理责任?()

    • 维护人类健康
    • 维护自然生态环境
    • 忠诚于雇主
    • 维护公司的利益

    第五章习题(下)

    判断题
    职业的行为规范强调的是“自我机制”,技术规范强调“社会机制”

    • 正确
    • 错误

    文字填空题
    工程领域中的“职业”是指“那些涉及高深的专业知识、()和对公共善协调服务的工作方式”。(四个字)
    自我管理

    单选题
    以下哪项是对职业共同体中工程师负责任的职业行为的错误理解?()

    • 工程师的责任就是他或她在工程生活中必须履行的角色责任
    • 工程师可以对履行特定义务作出回应
    • 工程师接受自己的工作职责和社会责任,并自觉地为实现这些义务努力
    • 工程师须对行为的危害承担责任,不能为自己行为的功绩要求荣誉

    判断题
    工程社团是工程职业的生活形态。

    • 正确
    • 错误

    单选题
    工程职业制度不包括()

    • 职业准入制度
    • 职业资格制度
    • 执业资格制度
    • 职业准出制度

    单选题
    以下哪项是注册工程师执业资格制度的首要环节?()

    • 职业实践
    • 资格考试
    • 高校工程专业教育
    • 注册执业管理

    多选题
    有关职业伦理章程的正确描述是()

    • 职业伦理章程一般采用规范和准则的形式
    • 职业伦理章程为职业行为提供一种普遍的和协商一致的标准
    • 职业伦理章程的主要关注点是促进负责任的职业行为
    • 职业伦理章程表达了对职业共同体内从业者的职业行为的期待

    判断题
    作为职业伦理的工程伦理是一种预防性、规范性的实践伦理

    • 正确
    • 错误

    单选题
    以下哪项是工程师的首要责任?()

    • 公众的安全、健康及福祉
    • 雇主的利益
    • 客户的利益
    • 可持续发展

    单选题
    以下哪项是工程师最新的职业责任观?()

    • 雇主的命令
    • 工程师的反叛
    • 承担社会责任
    • 对自然和生态负责

    多选题
    以下哪项是对工程师自律的正确理解?()

    • 工程师的自律是对职业责任的主动担当
    • 自律是工程师对工程-人-自然-社会整体必然存在的一种道德自觉
    • 自律使工程师从朦胧未显的工程伦理意识走向明确自主的对责任的担当
    • 工程师的自律表现为一种从向善到行善的自觉、自愿与自然的职业精神

    判断题
    举报是举报者一种最明智之举

    • 正确
    • 错误

    单选题
    作为职业人员,工程师不能享有以下哪项权利:()

    • 在规定范围内从事执业活动
    • 对他人执业活动进行解释和辩护
    • 接受继续教育
    • 在本人执业活动中形成的文件上签字并加盖执业印章

    单选题
    “工程师遵守甚至超越职业标准的积极责任”属于工程师的哪一种责任?()

    • 义务-责任
    • 过失-责任
    • 角色-责任
    • 岗位-责任

    单选题
    从职业伦理的角度看,工程师的()美德体现了工程伦理的核心

    • 诚实可靠
    • 忠实服务
    • 尽职尽责
    • 善良

    文字填空题
    工程师最综合的美德是()的职业精神。(三个字)
    答案:负责任
    判断题
    服务是工程师开展职业活动的一项基本内容和基本方式。

    • 正确
    • 错误

    单选题
    以下哪项不属于工程师的角色冲突?

    • 工程师与企业雇员
    • 工程师与管理者
    • 工程师与社会公众
    • 工程师与同事

    单选题
    工程中利益冲突的种类不包括()

    • 个体利益之间的冲突
    • 个体利益与群体利益之间的冲突
    • 个体利益与整体利益之间的冲突
    • 群体利益与整体利益之间的冲突

    第六章习题(下)

    多选题 (1/1 point)
    水利工程的影响深远体现在()

    • 水利工程对于促进国民经济和社会发展具有全方位的深远影响。
    • 水利工程会引起空间大范围的连锁反应。
    • 水利工程的影响常常跨越数十年甚至上百年。
    • 水利工程对人文和生态环境均产生深远影响。

    文字填空题 (1/1 point)
    水利工程就是对自然界中的水资源进行有效控制、按需调配、持续利用及()的工程。(四个字)
    答案: 全面保护
    判断题 (1/1 point)
    我国水资源丰富,人均水资源占有量为世界平均水平的四分之一。

    • 正确
    • 错误

    单选题 (1/1 point)
    以下哪项是新时期水利工程的崭新内容?

    • 港口与航道工程
    • 水力发电工程
    • 河湖环境生态工程
    • 水土保持工程

    判断题 (1/1 point)
    中国水利工程建设经历了工程水利、资源水利和生态水利三个阶段的演变

    • 正确
    • 错误

    判断题 (1/1 point)
    政府的行政意愿对水利工程建设的影响巨大。

    • 正确
    • 错误

    单选题 (1/1 point)
    水权的核心是哪项权利?()

    • 所有权
    • 配置权
    • 经营权
    • 使用权

    多选题 (1/1 point)
    水资源公正配置的原则包括()

    • 邻近优先
    • 尊重历史
    • 利益补偿
    • 重视生态

    单选题 (1/1 point)
    以下哪项是对跨地区调水的错误理解?()

    • 实施跨地区调水时,水源地的用水需求应该优先得到满足
    • 跨地区调水时要统筹兼顾调出和调入流域的用水需要
    • 水资源配置向水资源调出区倾斜时很有可能会成为利益驱动下的默认选择
    • 在具体工作中,并非总能优先考虑水源地的用水需求和实际利益

    单选题 (1/1 point)
    以下哪项说法是正确的?

    • 涉及水资源分配的国际水法条款与国际水条约非常多
    • 存在普适的水资源分配法则
    • 现有国际水法过多地强调上游国家对水资源地利用
    • 通过协商来解决水资源的分配与利用问题常常是唯一可行的途径

    判断题 (1/1 point)
    在我国,工业用水一直占水资源总量的最大比重。

    • 正确
    • 错误

    判断题 (1/1 point)
    对水利工程风险进行完全的定量评价是现实的。

    • 正确
    • 错误

    判断题 (1/1 point)
    我国已建成的水利工程在数量和规模上均居世界第二。

    • 正确
    • 错误

    单选题 (1/1 point)
    以下哪项不是从人类生存和发展的维度看河流的价值?()

    • 政治
    • 生态
    • 审美
    • 文化

    单选题 (1/1 point)
    以下哪项不是从河流的外在价值观察到的河流生命的特征?()

    • 周期性
    • 柔性
    • 独特性
    • 庄严性

    判断题 (1/1 point)
    水利工程对河流健康生命没有正面的促进作用。

    • 正确
    • 错误

    单选题 (1/1 point)
    以下哪项是对水利工程对河流健康生命造成巨大冲击的错误表述?()

    • 水利工程导致诸多天然美景的消失
    • 水利工程破化了河流生态系统的空间连续性
    • 水利工程减弱甚至切断了物质和能量沿水流的传递
    • 水利工程对河流生态系统的扰动是不可恢复的。

    文字填空题 (1满分)
    在实践中,通过法律、技术和()手段,努力维护河流健康生命。(两个字)
    答案: 管理

    单选题 (1/1 point)
    以下哪项原则是移民工作成败的核心?()

    • 顾全大局
    • 公平公正
    • 因地制宜
    • 可持续发展

    单选题 (1/1 point)
    以下哪项是对核定补偿标准的错误描述?()

    • 无形的损失难以估量
    • 要避免同一工程的不同区域、不同群体补偿方面存在不公平的现象
    • 要避免不同时间段(主要指可比的相邻时段内)移民补偿存在不公平的现象。
    • 与其他行业相比,水利工程补偿标准偏高。

    判断题 (1/1 point)
    通过努力,水利工程师在职业生涯中可以充分掌握全部的知识。

    • 正确
    • 错误

    第七章习题(下)

    多选题 (1满分)
    生物医药工程伦理问题包括()

    • 不可接受的“风险-受益”比
    • 环境污染问题
    • 学术不端问题
    • 公平可及性问题

    多选题 (1/1 point)
    生物医药工程中造成不可接受的“风险-受益”比的原因包括:()

    • 科研人员伦理意识淡薄
    • 伦理审查不规范
    • 没有把受试者安全放在首位
    • 忽视受试者的权益

    判断题 (1/1 point)
    现有的多数药品是针对成人进行的临床试验,增加了患儿的用药风险

    • 正确
    • 错误

    判断题 (1/1 point)
    多数制药企业喜欢投资孤儿药品。

    • 正确
    • 错误

    多选题 (1满分)
    生物医药研发和应用领域的知情同意问题十分突出,表现在()

    • 有些生物医药工程技术研发人员有意抹杀了“治疗”与“研究”的区别
    • 淡化了临床研究的潜在疗效
    • 夸大了可能的风险
    • 误导受试者

    文字填空题 (1/1 point)
    公正原则包括:程序公正、回报公正和()(四个字)
    答案:分配公正

    多选题 (1/1 point)
    生物医药研发和应用应尽量减少的风险包括()

    • 对受试人群的身心伤害
    • 给受试人群带来的经济负担
    • 造成的公共卫生风险
    • 造成的生态环境风险

    文字填空题 (1/1 point)
    自主性是指有行为能力的人在不受干扰的状态下,自愿选择行动方案的意识和()。(两个字)
    答案: 能力

    单选题 (1满分)
    以下哪项可以从"受益最大化原则"从推导出来?()

    • 生物医药研发和应用应将预防作为主要目标
    • 将工程的社会经济效益放在首位
    • 当经济回报、优先权、奖励等与受试者利益冲突时,把受试者的权益放在首位。
    • 国家利益始终大于患者健康需要的满足。

    单选题 (1满分)
    生物医药工程的伦理问题的主要来源不包括()

    • 因经济利益冲突而提出的“应该”问题
    • 因政治利益冲突而提出的“应该”问题
    • 因道德义务冲突而引起的伦理难题
    • 因宗教文化冲突而产生的伦理“应该”问题。

    单选题 (1/1 point)
    伦理审查委员会的基本职能不包括()

    • 改变研究方案
    • 监督
    • 咨询
    • 指导

    单选题 (1满分)
    动物研究的伦理要求实行3R原则不包括()

    • 考虑能够不使用动物
    • 减少动物使用量及使用中减少动物的痛苦
    • 权衡对动物的可能伤害及人类的受益以及动物伦理审查委员会的独立审查。
    • 考虑能否循环使用动物

    单选题 (1/1 point)
    医疗器械临床试验伦理审查要点不包括()

    • 试验的科学设计
    • 试验的风险和受益
    • 平等对待受试者
    • 知情同意

    判断题 (1/1 point)
    基因是有“好”或“坏”、“优”或“劣”之分的。

    • 正确
    • 错误

    判断题 (1/1 point)
    获知遗传基因信息不一定有利于个人对自己的生活作出理性的安排

    • 正确
    • 错误

    多选题 (1/1 point)
    基因信息泄露会引发哪些问题:()

    • 泄露家族成员的遗传倾向。
    • 可能在就学、就业、婚姻等方面受到歧视
    • 如果有基因缺陷,有些保险公司可能会拒绝为他们在医疗
    • 引发个人的不安和焦虑

    单选题 (1/1 point)

    • 以下哪项是对基因治疗临床试验的伦理审查的错误表述?()
    • 慎重选择受试者,确立准入和排除的严格标准。
    • 筛选程序要公平,并接受审查和监督。
    • 要预先进行方案的风险-受益分析
    • 有其他替代的有效常规疗法时,也可以使用基因治疗临床方案。

    单选题 (1满分)
    以下哪项不属于不征求提供者再次同意的条件:()

    • 样本是匿名的,不与其他可识别信息相联系
    • 样本提供者有机会自由撤回样本
    • 不征求再次同意对提供者更有利
    • 满足上述条件,就可以不接受伦理审查委员会的审查和批准。

    单选题 (1/1 point)
    以下哪项是对“治疗”和“研究”的错误理解?()

    • 治疗是将已被证明有效的方法用于病人
    • 病人从治疗中受益,病人应该付费。
    • 研究者无需给予受试者补偿
    • 研究如果对受试者造成损害,研究者应予以赔偿。

    文字填空题 (1满分)
    捐赠器官问题有两种同意方式:自愿捐献和()(四个字)
    答案:推定同意

    判断题 (1/1 point)
    活体器官移植是解决器官供应的根本途径。

    • 正确
    • 错误

    多选题 (1/1 point)
    以下哪项是对制药工程的正确表述?()

    • 制药工程以提升广大患者的健康需求为导向
    • 制药工程涉及众多利益主体,各方的角色分工和利益诉求不同,需要协同互助,信守承诺。
    • 制药工程受到市场需求、国家政策、研发资金等诸多因素的综合影响。
    • 制药企业是制药工程创新的主体,制药工程人员要有企业家精神。

    单选题 (1/1 point)
    以下哪项是对中国制药企业的社会责任的正确表述?()

    • 社会责任履行情况整体较好。
    • 新药研发水平相对滞后,资金投入不足,创新能力较低。
    • 制药企业或药物研发机构一般会主动公开负面报道。
    • 药品质量事故较少。

    第八章习题(下)

    判断题 (1/1 point)
    中国是仅次于美国的第二大二氧化碳排放国

    • 正确
    • 错误

    判断题 (1/1 point)
    煤电比核电更清洁

    • 正确
    • 错误

    单选题 (1/1 point)
    我国第一颗原子弹爆炸成功是在哪一年()

    • 1950
    • 1964
    • 1967
    • 1971

    单选题 (1满分)
    核工程应遵循的伦理原则包括

    • 以人为本原则
    • 可持续发展原则
    • 生态原则
    • 公正原则

    多选题 (1/1 point)
    核工程应该遵循的伦理原则主要有()
    以人为本原则, 可持续发展原则, 生态原则, 公正原则, - 正确

    • 以人为本原则
    • 可持续发展原则
    • 生态原则
    • 公正原则

    单选题 (1/1 point)
    对伦理规范的审查应该在项目建设的什么时期()

    • 初期
    • 中期
    • 运行期
    • 后期

    判断题 (1/1 point)
    “零风险”是可以实现的

    • 正确
    • 错误

    多选题 (1/1 point)
    风险主要包括哪些要素()

    • 事件诱因
    • 事件概率
    • 事件后果
    • 事件过程

    判断题 (1满分)
    系统的正反馈效应能够维持系统平衡与稳定

    • 正确
    • 错误

    多选题 (1/1 point)
    公众在核工程中的权利有()

    • 知情权
    • 决策权
    • 参与权
    • 讨论权

    多选题 (1/1 point)
    影响核事故信息公开的主要因素有()

    • 政治因素
    • 经济因素
    • 社会因素
    • 个人因素

    多选题 (1满分)
    核安全基本原则主要有()

    • 管理责任
    • 纵深防御
    • 技术防御
    • 公民参与

    单选题 (1/1 point)
    核安全文化的主导和核心是()

    • 系统
    • 设备
    • 环境

    多选题 (1满分)
    放射性废物主要造成的危害有()

    • 职业照射
    • 公众照射
    • 持续急性照射
    • 隐性照射

    多选题 (1满分)
    核电站的环境影响主要包括()

    • 放射性污染
    • 热污染
    • 内陆核电站的影响
    • 硫化物污染

    判断题 (1/1 point)
    工程师可以代替民众进行工程决策

    • 正确
    • 错误

    第九章习题(下)

    多选题 (1/1 分数)
    下列哪几项属于信息技术的特点

    • 连接能力
    • 交互能力
    • 渗透特性
    • 融合能力

    判断题 (1/1 分数)
    “互联网+”指以互联网为主的一整套信息技术在经济、社会生活各部门的扩散和应用过程,其本质是传统产业和生产过程的在线化、数据化。

    • 正确
    • 错误

    多选题 (1/1 分数)
    下列哪几项属于大数据的特点

    • 数量大
    • 类别多
    • 增长速度快
    • 真实可信

    多选题 (1/1 分数)
    大数据时代背景下,我们将面临哪些新的、更为集中的伦理挑战

    • 身份困境
    • 隐私边界
    • 数据权利
    • 数据治理

    判断题 (1/1 分数)
    大数据与以往数据应用不同之处表现在三方面:可以获得全体数据而非采样数据;允许获取的数据呈现混乱、复杂状态而不再强求干净、精确,即大方向的正确比微观精准更重要;聚焦发现和分析事物的相关性而非因果性。

    • 正确
    • 错误

    单选题 (1/1 分数)
    下列哪项不是“数字身份”的特点

    • 多样性
    • 可变性
    • 允许匿名和假名
    • 唯一性

    多选题 (1/1 分数)
    在计算机和网络应用中,常见的用户身份管理技术有

    • 用户名+密码+校验码
    • 第三方认证,如 U 盾
    • 预存的个性化问题
    • 生物特征

    判断题 (1/1 分数)
    人的身份(identity),用来界定一个人是谁或是什么,具有可识别性、独特性、唯一性。

    • 正确
    • 错误

    多选题 (1/1 分数)
    由于网络和信息技术的特点,保护数据隐私面临一系列技术和非技术的挑战,下列哪项属于所面临的挑战?

    • 可信性与可靠性
    • 快速扩散性与放大器效应
    • 挖掘技术与关联发现
    • 身份盗窃与冒用

    多选题 (1/1 分数)
    大数据时代,收集个人数据包括哪些手段?

    • 开放API
    • Web应用
    • 黑客攻击
    • 交易(公开或秘密)

    判断题 (1/1 分数)
    隐私权指自然人享有的私人生活安宁与私人信息依法受保护,不被非法侵扰、知悉、搜集、利用和公开的一种人格权.

    • 正确
    • 错误

    判断题 (1/1 分数)
    要为大数据创新应用提供高质量的数据基础,必须坚持尊重、公平交易、诚信这三个价值判断原则,谨慎对待各方数据权利

    • 正确
    • 错误

    判断题 (1/1 分数)
    个人信息是指与特定个人相关联的、反映个体特征的具有可识别性的符号系统,包括个人身份、工作、家庭、财产、健康等各方面的信息

    • 正确
    • 错误

    多选题 (1/1 分数)
    脱胎于“家国天下”传统社会伦理的中华价值观,表现出了哪些特色

    • 责任先于自由
    • 义务先于权利
    • 群体高于个人
    • 和谐高于冲突

    单选题 (1/1 分数)
    数据伦理责任是具有普遍意义的伦理责任在大数据时代的具体化,那么,下列哪项不属于大数据伦理责任的特点?

    • 自律性
    • 强制性
    • 广泛性
    • 实践性

    判断题 (1/1 分数)
    大数据创新科技人员的伦理责任主要表现在尊重个人自由、 强化技术保护、严格操作规程、加强行业自律、承担社会责任这五个方面

    • 正确
    • 错误

    多选题 (1满分)

    • 人际关系虚拟化
    • 正当的网络行为
    • 平等与公正、知识产权争议
    • 全球化信息交互与治理困境

    第十章习题(下)

    下一个
    单选题 (1/1 point)
    环境工程是人类为减少工业化生产过程和人类生活过程对环境的影响进行()的工程手段。

    • 政策治理
    • 经济治理
    • 社会治理
    • 污染治理

    单选题 (1/1 point)
    环境问题主要是指由于人类经济和社会活动引起的环境破坏,实质是经济发展与环境保护的冲突,是()关系的失调。

    • 经济与社会
    • 自然与社会
    • 人与自然
    • 人与社会

    判断题 (1/1 point)
    任何物质的创造都会使用资源、消耗资源,在消耗资源的过程中必然会有废弃物的排放。()

    • 正确
    • 错误

    判断题 (1/1 point)
    由于环境工程师的工作直接涉及环境保护,相对其他工程师及非环境工作者来说,环境工程师应该负有更加特殊和更加重要的环境伦理责任。

    • 正确
    • 错误

    文字填空题 (1/1 point)
    环境正义的实现应该以()为本位,所有公民(不包括后代人和自然体)对大自然都负有环境保护的责任和义务。(四个字)
    答案:环境义务

    单选题 (1/1 point)
    现代工业生产活动是人、机器与环境共同存在、相互影响的系统,()保证了系统的可靠。

    • 制度管理
    • 安全生产
    • 科学技术
    • 应急防范

    单选题 (1/1 point)
    公共安全问题主要发生在公共工程运营中,是由于其()给非工程直接利益相关的社会公众带来的安全问题。

    • 社会性
    • 特殊性
    • 风险性
    • 公共性

    多选题 (1满分)
    工程师作为工程设计的主要承担者和执行者,下列哪些工程阶段会面临遵守职业规范和服从雇主命令之间的冲突。

    • 工程设计阶段
    • 工程保养阶段
    • 工程建造和生产阶段
    • 工程维护阶段

    判断题 (1满分)
    在企业发生的工伤事故中,70%左右的事故在不同程度上与人的失误有关,而出现这些问题的最根本的原因是环境保护意识薄弱。()

    • 正确
    • 错误

    PS: 实在不懂工伤事故的根本原因为什么是环境保护意识薄弱,找不到逻辑在哪?要么就是答案错了……

    判断题 (1/1 point)
    所有对社会有价值的存在形式都应得到尊重和保护,每个个体都会对环境产生影响并应承担相应的责任。

    • 正确
    • 错误

    单选题 (1/1 point)
    企业应保持寻求自身发展与社会经济可持续发展目标的一致性,把()纳入生产成本中。

    • 工程代价
    • 经济代价
    • 社会代价
    • 环境代价

    单选题 (1/1 point)
    环境工程的社会责任是在()的同时,不阻碍或促进经济的健康发展,以及保护其他社会利益。

    • 企业管理
    • 保护环境
    • 工程建设
    • 社会生产

    多选题 (1/1 point)
    环境问题涉及哪些利益相互协调的问题?()

    • 社会公共利益
    • 政治利益
    • 生态利益
    • 经济利益

    判断题 (1/1 point)
    环境工程师可以通过环保工程改善环境,也可能因为采用的技术或实施过程的不合理而破坏环境。()

    • 正确
    • 错误

    单选题 (1/1 point)
    现代工程需要广阔的基础知识,因此要求环境工程师必须具备自然科学知识、社会科学知识等基础知识和较高的()。

    • 环保意识
    • 职业精神
    • 专业知识
    • 科学态度

    单选题 (1/1 point)
    环境工程作为调节人与人、人与社会之间关于()利益关系的工程,其基本原则就是生态整体利益和长远利益高于一切。

    • 社会发展
    • 生态环境
    • 经济增长
    • 工程管理

    多选题 (1/1 point)
    下列哪些行为属于不诚实的表现形式?()

    • 篡改数据
    • 伪造数据
    • 修饰拼凑
    • 抄袭剽窃

    判断题 (1满分)
    作为环境保护技术的主体,尽管环境工程师可以通过各种环保工程建设来影响人类社会,但宣传环保知识不属于环境工程师应该承担的责任。()

    • 正确
    • 错误

    判断题 (1/1 point)
    有意不传达听众所合理期望的不被省略的信息,是工程师的不诚实行为。()

    • 正确
    • 错误

    最后恭喜大家完成工程伦理慕课的学习!!!

    展开全文
  • 测试开发笔记

    万次阅读 多人点赞 2019-11-14 17:11:58
    测试基础 7 什么是软件测试: 7 ★软件测试的目的、意义:(怎么做好软件测试) 7 3.软件生命周期: 7 测试过程 8 1.测试模型 8 H模型: 8 V模型 9 2.内部测试 10 3外部测试: 10 验收测试:(在系统...

    测试开发笔记

    第一章 测试基础
    1.什么是软件测试
    2.软件测试的目的、意义(怎么做好软件测试)
    3.软件生命周期
    第二章 测试过程
    1.测试模型
    H模型
    V模型
    2.内部测试
    3外部测试
    验收测试(在系统测试之后)
    回归测试
    4.测试过程(干什么,怎么干)
    5.各阶段输入、输出标准以及入口、出口准则:(测试阶段过程要素)
    第三章 测试方法
    1.测试方法对比
    2.测试方法组合
    第四章 软件质量
    1.什么是软件质量
    2.质量要素
    3. 6大特性27个子特性ISO国际标准组织CMM/CMMI(Capability maturity model)能力程度度模型
    4.CMMI把企业分为5个等级
    5. CMM与CMMI的区别
    第五章 SQL
    约束
    1主键约束
    2 非空约束 not null
    3 外键约束 FOREIGN KEY
    4 默认约束
    5 检查约束 check
    6 唯一约束 unique
    SQL语句
    创建数据库.
    表、字段、类型
    查询
    批量处理
    视图/虚表 view
    索引
    存储过程 procedure
    事务 transaction
    触发器 trigger
    练习
    一、单表查询练习
    二、聚合函数练习
    三、分组查询练习
    四、嵌套查询练习
    五、联接查询练习
    六、外联接查询
    七、补充提高
    第六章 C语言
    C语言中的存储
    数据类型
    常量
    结构体
    条件/分支逻辑
    Switch
    If
    循环
    For
    while
    do…while
    函数
    第七章 Windows环境搭建
    一、名词注解与定义:
    C/S
    B/S
    进销存系统
    OA系统
    第八章 需求管理
    1.什么是需求
    2. 需求工程在做什么
    3. ★需求变更
    4.★需求的跟踪
    需求跟踪矩阵的作用
    需求的特点
    需求工程
    变更控制流程图
    第九章 缺陷管理
    缺陷相关概念
    缺陷管理相关概念
    BUG管理基本流程
    BUG单
    第十章 测试需求分析
    概念
    ★如何做测试需求分析
    ★UML统一建模语言(Unified Modeling Language)
    第十一章 配置管理
    1.什么是配置管理
    2.配置管理流程
    配置管理工具
    SVN操作过程手册
    一、 如何创建“project”项目版本库
    二、 如何查看创建的“project”项目版本库
    三、 在版本浏览器里面,创建文件,并进行检出
    四、 如何对该项目入基线
    五、 分支文件进行合并
    六、 分支冲突的解决
    第十二章 系统测试
    概念:
    分类:
    功能测试(Function testing中国 Feature testing国际)
    性能测试(Sercarity testing)
    安全性测试(Security Testing)
    安装测试
    GUI测试(Graphical user interface)
    可用性测试(Usability testing)
    异常性测试
    文档测试
    备份测试
    配置测试
    网络测试
    第十三章 用例设计
    等价类
    练习
    1.1年龄注册
    1.2.年龄注册
    1.3.扩充
    边界值
    2.1.年龄
    2.2.用户名注册
    2.3.变量命名
    2.4.进销存价格
    2.5.Windows文件命名
    总结
    边界值
    第十四章 系统测试执行
    测试环境搭建文档
    用例执行
    填BUG报告
    第十五章 QC(Quality Center)
    QC后台:
    QC前台:
    Requirements 需求模块
    Test Plan 测试用例模块
    Test Lab 测试执行模块
    第十六章 PYTHON
    Python的安装
    Python的集成环境
    数据类型
    运算符
    缩进
    控制语句
    IF条件
    WHILE循环
    FOR循环
    BREAK \ CONTINUE
    函数
    定义
    调用
    第十七章 单元测试
    单元测试概念
    单元测试静态测试
    单元测试动态测试
    测试评价准则
    逻辑覆盖率
    单元测试策略
    ⑴ 孤立测试
    ⑵自顶向下的单元测试策略
    ⑶自底向上的单元测试方法
    单元测试用例设计(基本路径覆盖法)
    程序控制流图
    单元测试执行
    单元测试框架
    第十八章 集成测试
    第一阶段总结
    Test platform
    Bug的其他说法
    第二阶段项目笔记
    一.建立项目JXC
    二.布置JXC
    三.配置SVN
    四.访问SVN
    进销存项目
    进销存项目总结
    测试需求分析
    1、定义测试范围
    2、建立需求项
    3、细化需求项
    4、需求覆盖率分析
    判定表
    3.1.读书选择
    3.2.Counter
    3.3:word中的判定表举例
    3.4.合并判定表
    3.4.密码修改
    3.5.进销存
    3.6.总结
    因果图
    4.1.字母判定
    4.2.自动售货机
    状态迁移
    5.1.飞机售票系统
    5.2.缺陷跟踪
    流程分析
    6.1.处理流程
    6.2.系统登录
    6.3.字母判断
    6.4.组合查询
    正交试验
    7.1.环境搭建
    7.2.Counter
    7.3.组合
    7.4.环境搭建
    其他
    输入域
    输出域
    异常分析
    错误猜测

    第一阶段
    第一章 测试基础
    1.什么是软件测试:
    两个依据(需求、测试用例),两个方法(手工、自动),一个对比(预期结果和实际结果的对比)
    2.软件测试的目的、意义:(怎么做好软件测试)
    初期: 尽量多的发现缺陷生成相关规范
    中期: 尽量早的发现缺陷
    后期: 尽量预防问题:通过以往的经验积累
    控制成本(贯穿始终)尽量少的时间和人力发现更多的缺陷
    3.软件生命周期:软件的产生直到报废或停止使用的生命周期。软件生命周期内有问题定义、可行性分析、总体描述、系统设计、编码、调试和测试、验收与运行、维护升级到废弃等阶段,也有将以上阶段的活动组合在内的迭代阶段,即迭代作为生命周期的阶段。

    如何尽量多的发现缺陷?
    沟通
    在测试前期与开发沟通 确认测试重点 确认测试的优先级
    了解开发人员技术和业务背景 业务水平 技术水平 代码质量 人员流动性
    在测试结束后
    对已发现的bug进行统计 知道高发概率bug 在新项目中要进行重点测试
    针对代码 代码复杂度
    版本管理
    针对基础测试基础版本要进行充分的测试
    验收前的最后一个版本一定要进行完全重复测试
    测试方法
    黑盒方法 功能问题 无法保证所有的代码逻辑都被执行到 用白盒测试思想补充黑盒测试
    静态测试方法 文档评审 代码走查
    测试过程
    上一阶段为下个阶段提供重点指导
    用户参与的测试或用户反映回来的错误和问题为下次测试的或测试补充的必备内容

    第二章 测试过程
    1.测试模型
    H模型:

    H模型图
    优点:
    1 介入早 与开发并行 更早的发现问题
    2 测试过程独立于开发过程 更客观 更主动
    V模型

    双V模型图
    ㈠需求阶段
    产品经理,项目经理,产品工程师写《需求规格说明书》Software Reqwirment Specaficalion(SRS)
    内容:需求项(业务,主要功能)需求子项,对子项的详细描述
    测试的工作:对需求进行测试和评审A系统测试计划《系统测试计划书》B系统测试计划《系统测试方案书》C系统测试实现《系统测试用例》
    ㈡设计阶段
    开发经理,架构师,开发工程师写出《概要设计说明书》High-level design(HLD)
    内容:系统程序中的模块,子模块和他们之间的关系和接口
    测试的工作:对HLD进行测试和评审A集成测试计划《集成测试计划书》B集成测试设计《集成测试方案书》C集成测试实现《集成测试用例》
    ㈢详细设计阶段
    开发工程师,架构师,写出《详细设计说明书》Low-level desragn(LLD)
    内容:函数 代码 逻辑
    测试工作:对LLD进行测试和评审A单元测试计划《单元测试计划书》B单元测试设计《单元测试方案书》C《单元测试用例》
    ㈣编码阶段
    开发工程师写代码
    优点:介入早,提高测试质量; 分成三个阶段,发现问题更有针对性;测试与开发并行,更好的利用项目资源。
    缺点:项目成本高;技术要求高,对人员要求高;并行工作中,一方未完成就会对整个造成延误。
    适用范围:规模大、软件成熟度高的项目。
    2.内部测试
    测试阶段 测试对象 测试方法 测试目的 经济价值 优点 缺点 必要性 资源
    系统测试
    system testing(ST) 整个系统
    (整个产品) 黑盒测试 验证产品是否符合需求规格说明书 能够保证产品以较高的的质量尽早的上市销售,从而使公司获取利润 1简单
    2技术要求低 1测试介入时间晚,修改成本高
    2有一些问题可能被遗留不会被修改 必须保证 1对被测产品
    2需求规格说明书
    3系统测试工程师
    4需求开发人员
    集成测试
    integration testing(IT) 模块
    子模块
    接口 灰盒测试 验证模块、子模块、接口是否符合
    概要设计说明书 能够帮助更准确的 定位缺陷的所在,从而降低了定位缺陷的成本 定位准确快速 1接口测试有技术要求,技术实现难度大
    2接口太多,数量庞大,做所有接口的集成测试成本高 不是必须做的,
    必须做测试的
    1公共的主要模块
    2核心模块
    3和外界软件接口模块 1被测的产品
    2概要设计说明书
    3集成测试工程师
    4概要设计人员
    单元测试
    unit testing(UT) 函数
    代码
    逻辑 白盒测试 验证函数代码逻辑是否符合详细设计说明书 能够最早的开展测试工作,降低修复成本,防止缺钱被扩大化(注意:加以重视:1公共的模块2全局性的数据结构3重要的使用频率较高的功能4以往项目经常出错的严重问题5复杂度较高的模块6当开发人员业务不熟悉编码不熟练的模块要进行单元测试) 介入时间早,发现问题早,修改成本低。 1技术难度高
    2工作量太大 不是必须的 1开发环境
    2LLD
    3单元测试工程师
    4架构师(详细设计人员)
    3外部测试:
    使用验收测试的原因
    1内部测试只能模拟用户使用却不能代替用户使用
    2由于专业不同业务背景不同无法模拟用户使用的习惯
    3测试人员和用户对产品的理解可能不同
    验收测试:(在系统测试之后)
    α测试:由用户组织一部分人在开发环境下来对产品进行测试 如网游的内侧
    β测试:所有系统使用者都可以参加的测试(在实际使用环境下) 如网游的公测
    分类 测试过程 参与人员 目的 过程主要内容
    针对项目类软件 验收测试 开发人员:提供满足验收要求的软件或系统,或用户需要的相关开发文档
    测试人员:
    1、搭建验收测试环境
    2、准备验收测试用例
    3、准备用户需要的相关测试文档
    4、组织人员进行验收演示
    用户代表:对系统进行一定的试用
    客户代表:签字确认验收是否通过
    行业:负责在验收过程中提出问题并协助用户和客户检查系统是否满足需求 1、检查软件的功能是否与用户最初需求相一致
    2、是客户回款的标志 1、进行验收前准备
    A、准备相关的资料
    B、搭建验收测试环境
    C、指定相关的验收参与人
    2、进行验收演示
    A 、对产品使用进行演示
    B、回答专家、用户的提问
    3、签署验收报告
    针对产品类软件 α测试 开发人员:
    1、提供可以进行α测试的软件
    2、负责修改用户代表发现的问题
    测试人员:
    1、检查或协助用户填写缺陷报告
    2、向用户学习相关的使用关注点
    邀请的用户或客户代表(付费)
    1、按照自己的操作习惯使用软件,提出易用性等方面的问题和改进建议 明确用户的使用体验,提高产品的适用范围和使用质量标准 1、明确进行α测试的版本
    2、邀请潜在用户进行使用体验
    3、针对用户提出的问题进行修复或改进
    β测试 潜在用户:
    1、安装软件并使用
    客服人员:
    记录并反馈用户的问题 提前占领市场 1、发布一个下载地址
    2、用户进行软件下载并使用
    回归测试:
    回归测试可以发生在任何一个阶段
    分为完全回归和选择回归
    回归范围 回归分类 特点 优点 缺点 适用范围
    完全回归 完全重复法 每次回归测试都要执行全部测试用例 回归测试充分,覆盖面广,不容遗漏 工作量大,时间长,成本高 时间充裕且测试资源较充分时,第一次和最后一次做回归测试的时候用这种方法
    选择性回归 覆盖修改法 每次回归测试时只执行发现错误的用例 时间最短,成本最低,简单效率高 回归测试不充分,漏洞较多 时间较紧且人力资源不足时,中间版本的测试轮次可以使用,关联度比较小的模块和功能
    周边影响法 每次回归除了执行发现bug的用例外,还要执行与其相关的用例 在考虑了测试成本的基础上有效提高了回归测试的质量 效率 很难确定影响的周边范围,相关用例定位较困难 适合于全局数据结构被修改或公共模块被修改,或核心算法业务被修改时,公用的模块,关系、关联复杂的模块
    指标达成法 每次回归测试达到规定的语气指标
    就可以停止测试了 所有的测试都可度量 1指标生成需要很长的周期,
    很多的项目区累计经验
    2要有比较稳定的团队这个指标才有意义 成熟度较高的测试团队应用于指标达成法
    (适用度很低,很少有公司使用)

    分类 步骤 优点
    确定周边
    范围的方法 界面检查法 1明确被修改的功能 简单
    2修改功能的上下游功能
    3调用修改功能的功能和
    修改功能调用了的功能
    4和修改功能游相同输入输出的功能
    5在测试中执行上诉关联的用例
    代码检查法 1明确被修改的函数和代码 准确,全面
    2在整个系统中检查所有
    调用了修改函数的函数
    3明确上诉所有函数对应的界面
    4测试上诉界面测试用例
    4.测试过程(干什么,怎么干)
    整个系统的内容 需求项(业务、主要功能) 需求项 测试计划 测试需求项 系统测试阶段
    需求子项 测试方案 测试需求子项
    详细内容 测试用例 具体如何进行测试
    整个系统的集成 概要设计 概要设计项 测试计划 集成测试阶段
    概要设计子项 测试方案
    具体内容 测试用例
    整个系统最小单元 详细设计 函数 测试计划 单元测试
    逻辑 测试方案
    代码 测试用例

    5.各阶段输入、输出标准以及入口、出口准则:(测试阶段过程要素)
    系统测试 入口准则 输入文档 输出文档 出口准则
    系统测试计划 开发计划通过评审并入基线
    需求规格说明书通过评审并入基线 开发计划书
    需求规格说明书 系统测试计划书 系统测试计划书通过评审并入基线
    系统测试设计 系统测试计划书通过评审并入基线 需求规格说明书
    开发计划书
    系统测试计划书 系统测试方案书 系统测试方案书通过评审并入基线
    系统测试实现 系统测试方案书通过评审并入基线 需求规格说明书
    系统测试计划书
    系统测试方案书 系统测试用例
    预测试项 系统测试用例、预测试项通过评审并入基线
    系统测试执行 系统测试用例、预测试项通过评审并入基线
    集成测试报告通过评审并入基线 需求规格说明书
    系统测试计划书
    系统测试方案书
    系统测试用例
    预测试项 缺陷报告
    预测试项报告
    系统测试报告 系统测试报告、预测试项报告、缺陷报告通过评审并入基线
    集成测试 入口准则 输入文档 输出文档 出口准则
    集成测试计划 概要设计说明书通过评审并入基线 概要设计说明书 集成测试计划书 集成测试计划书通过评审并入基线
    集成测试设计 集成测试计划书通过评审并入基线 集成测试计划书
    概要设计说明书 集成测试方案书 集成测试方案书通过评审并入基线
    集成测试实现 集成测试方案书通过评审并入基线 集成测试计划书
    集成测试方案书
    概要设计说明书 集成测试用例 集成测试用例通过评审并入基线
    集成测试执行 集成测试用例通过评审并入基线
    单元测试报告通过评审并入基线 集成测试计划书
    集成测试方案书
    集成测试用例
    概要设计说明书 集成测试报告
    缺陷报告 集成测试报告、缺陷报告通过评审并入基线
    单元测试 入口准则 输入文档 输出文档 出口准则
    单元测试计划 详细设计说明书通过评审并入基线 详细设计说明书 单元测试计划 单元测试计划通过评审并入基线
    单元测试设计 单元测试计划通过评审并入基线 详细设计说明书
    单元测试计划书 单元测试方案书 单元测试方案书通过评审并入基线
    单元测试实现 单元测试方案书通过评审并入基线 详细设计说明书
    单元测试计划书
    单元测试方案书 单元测试用例 单元测试用例通过评审并入基线
    单元测试执行 单元测试用例通过评审并入基线 详细设计说明书
    单元测试计划书
    单元测试方案书
    单元测试用例 单元测试报告
    缺陷报告 单元测试报告、缺陷报告通过评审并入基线

    第三章 测试方法
    测试方法对比
    分类方法 测试方法名称 依据 测试对象 理论上的测试目的 实际工作中的测试目的 测试评估标准 测试环境 测试工作介入点 优点 缺点 适用范围
    按照不同的测试对象划分(黑白灰盒的区别) 黑盒 SRS 整个软件产品 检查软件的功能实现是否与SRS相一致 尽早进行验收,收回开发成本 需求覆盖率 尽量与用户环境相一致 只要功能可以进行操作 简单,测试效率高 1、无法保证所有的代码逻辑都被测试到
    2、后台相关的非界面处理可能会遗漏(文件、数据库)
    3、当前功能与其他功能有联系的部分可能也会被遗漏 适合进行功能、性能等使用和外部特性的测试适用范围广泛,适用所有可见功能
    白盒 LLD 代码逻辑函数 检查代码的逻辑实现是否与LLD相一致 尽早发现问题缺陷,降低缺陷修复成本.便于定位问题 逻辑覆盖率
    语句覆盖
    分支覆盖
    条件覆盖
    分支-条件覆盖
    路径覆盖 开发环境 只要独立的函数或类代码编写完成后 覆盖充分,可以覆盖到每行代码 技术较难
    效率较低
    成本较高 针对核心业务、复杂算法、公共模块、全局数据结构、新增功能
    灰盒 HLD 模块\子模块接口 检查接口实现是否与HLD相一致 逐步集成,降低缺陷定位成本 接口覆盖率 子系统集成尽可能和用户环境一致,模块内部接口以及模块间接口可以在开发环境下进行
    子系统间的接口最后要在与用户环境下测试 进行测试的接口模块已完成 可以提早定位和发现问题 技术最难
    成本最高 公共模块之间的调用,复杂度较高的模块调用、使用频率较高的模块调用

    特点 分类 优点 缺点 适用范围
    按照是否运行程序划分 静态 不执行程序 1、文档评审
    A、正规检视
    B、技术评审
    C、同行评审
    2、静态分析技术
    A、控制流分析
    可以发现以下缺陷
    1、死循环
    2、执行不到的语句
    3、不存在的语句
    B、数据流分析
    可以发现以下缺陷
    1、变量未定义被使用
    2、变量已定义未使用
    C、信息流分析
    可以帮助开发人员定位缺陷
    1、输入变量与语句的关系
    2、输出变量与语句的关系
    3、输入变量与输出变量的关系 较动态测试时间早,不用写代码 工作量大 重要的功能模块、核心的业务、算法
    公共模块
    动态 执行程序 黑和测试
    动态白盒:插装—在代码中加入print打印语句,检查程序的中间运行结果 复杂,效率高 测试较晚,写代码 所有功能

    优点 缺点 适用范围
    按照不同的测试手段划分 手工 能够主动的发现bug 重复工作量大,容易引入疲劳缺陷,只能依靠见到的界面 绝大多数的场合
    自动化 可以无限制不断重复,把人从劳动里解放出来,提高劳动效率,提高了测试质量,能发现人不能发现的错误 无法发现脚本中未写明的缺陷 GUI界面稳定
    回归阶段
    需求稳定且功能已实现时才进行脚本的编写
    性能测试工具:提取相关的系统数据,构造并发用户
    测试方法组合
    测试方法组合 典型案例 使用时机 特点
    黑盒
    黑盒静态手工      
    黑盒静态自动化      
    黑盒动态手工      
    黑盒动态自动化功能测试 Mercury的QTP:用于检测应用程序是否能够达到预期的功能及正常运行
    通过自动录制、检测和回放用户的应用操作 1、能够有效地帮助测试人员对复杂的企业级应用的不同发布版进行测试
    2、提高测试人员的工作效率和质量,确保跨平台的、复杂的企业级应用无故障发布及长期稳定运行
    IBM Rational Robot 是功能测试工具 它集成在测试人员的桌面 IBM Rational TestManager 上,在这里测试人员可以计划、组织、执行、管理和报告所有测试活动,包括手动测试报告。这种测试和管理的双重功能是自动化测试的理想开始。
    Borland SilkTest属于软件功能测试工具 是Borland公司所提出软件质量管理解决方案的套件之一。这个工具采用精灵设定与自动化执行测试,无论是程序设计新手或资深的专家都能快速建立功能测试,并分析功能错误。
    基于Java语言的功能和性能测试工具 JMeter是Apache组织的开放源代码项目 主要针对Java语言 它是功能和性能测试的工具,100%的用java实现
    黑盒动态自动化性能测试 Mercury的LoadRunner:是一种预测系统行为和性能的负载测试工具。 通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题 能够对整个企业架构进行测试。通过使用LoadRunner ,企业能最大限度地缩短测试时间,优化性能和加速应用系统的发布周期。
    Microsoft Web Application Stress Tool 是由微软的网站测试人员所开发,专门用来进行实际网站压力测试的一套工具。 功能强大的压力测试工具 您可以使用少量的Client端计算机仿真大量用户上线对网站服务所可能造成的影响
    webload是RadView公司推出的一个性能测试和分析工具 它让web应用程序开发者自动执行压力测试; webload通过模拟真实用户的操作,生成压力负载来测试web的性能。
    白盒
    白盒静态手工      
    白盒静态自动化   检查语法规范、语法逻辑  
    白盒动态手工 目前的最流行的单元测试工具是xUnit系列框架 常用的根据语言不同分为JUnit(java),CppUnit(C++),DUnit(Delphi ),NUnit(.net),PhpUnit(Php )等等。 该测试框架的第一个和最杰出的应用就是由Erich Gamma (《设计模式》的作者)和Kent Beck(XP(Extreme Programming)的创始人 )提供的开放源代码的JUnit。
    白盒动态自动化 Jtest是parasoft公司推出的一款针对java语言的自动化白盒测试工具,它通过自动实现java的单元测试和代码标准校验,来提高代码的可靠性。parasoft同时出品的还有C++ test,是一款C/C++白盒测试工具    
    灰盒
    灰盒静态手工      
    灰盒静态自动化      
    灰盒动态手工      
    灰盒动态自动化 BMC的APPSight 系统会将问题发生的相关信息完整录制下来,包括问题发生的现场场景、信息及分析等,从而快速切入到问题根源  
    测试管理工具 是业界第一个基于Web的测试管理系统,它可以在您公司内部或外部进行全球范围内测试的管理。通过在一个整体的应用系统中集成了测试管理的各个部分,包括需求管理,测试计划,测试执行以及错误跟踪等功能,TestDirector极大地加速了测试过程。

    1自动化测试就是用程序驱动程序的测试
    2黑白灰测试的区别
    测试的对象不一样,对于代码实现逻辑程度不一样(黑盒不需要了解代码实现,白盒需要完全了解代码实现,灰盒需要部分了解代码实现)
    3静态与动态测试的区别
    被测程序执行与否 静态不执行程序包括文档评审静态分析技术代码走读,动态包括黑盒测试和动态分析技术
    4自动化合手工测试的不同
    测试手段不同

    第四章 软件质量
    1.什么是软件质量
    质量:确定一个实体的特性满足需求的程度
    内部质量:软件研发过程中,评价的软件质量
    外部质量:软件上市后,用户评价的质量
    过程质量:评价软件研发中每个过程的质量
    软件质量的三个层次
    ⑴流程质量,领导关注 ⑵产品质量 测试工程师关注 ⑶使用质量 用户关注
    2.质量要素
    质量铁三角 : 技术 过程 组织
    3. 6大特性27个子特性ISO国际标准组织CMM/CMMI(Capability maturity model)能力程度度模型

    质量模型列表
    质量模型特性 子特性 特点 常见测试点 案例说明
    功能性 适合性 合适的功能(用户提出要有哪些功能)功能的必要性 验证功能是否满足需求的要求,检测做没做 打电话、听音乐、发信息
    准确性 正确的功能 需求文档中的预期动作和预期输出,做对没有 信息的发送内容是否正确
    互操作性 和其他软件的互相操作 第三方软件的交互 word文档对打印机驱动程序的操作
    保密安全性 保护信息和数据 保护得到授权的人或者系统能正常访问相关的信息或数据 1、登录的用户名和密码
    2、权限使用
    3、防止DOS攻击(拒绝访问攻击)4、系统数据的保护和加密,如密码的加密
    5、传输加密,如密码的网络传输
    6、防病毒
    7、放溢出,如char与varchar的字符数
    保证未授权的人或系统无法看到相关的信息或数据
    功能性的依从性 遵循功能性相关的标准、约定或法规 是否符合国家法律规定 如色情网站
    可靠性 成熟性 缺陷尽可能的少    
    容错性 提前考察的异常情况出错问题 整个系统的外部接口 如word打印时,打印机死机出现报错,但不影响word的使用
    易恢复性 失效后恢复原有功能、性能 系统的性能测试 如网游延迟卡死现象。系统提示内存不足。银行系统的心跳监听。灾难备份。
    可靠性的依从性 法律法规   灾难备份。
    易用性(CUI测试) 易理解性 (快速理解) 系统交互的信息是否准确、清晰、易懂,指导下一步操作。 系统提示信息是否准确 如网银密码超出位数报错
    易学性 (快速上手) 易用好学 是否有说明书、是否在线帮助、是否有提示信息 msn的帮助手册
    易操作性 (快速做完) 方便快速使用 操作的直观程度,操作步骤、操作动作多少与时间长短 鼠标、gui层数、安装过程
    易测试性 软件可控 提供工具给测试工程师,可以控制系统运行,以达到测试目的 windows的性能工具与服务管理工具
    软件可观察 通过辅助手段可  
    吸引性 外观 外观  
    易用性的依从性 法律法规    
    可移植性 适应性 (跨平台、跨语言) 软件产品无需采用有别于为考虑该软件的目的而准备的活动或手段就可能适应不同的指定环境的能力;是否适应其他系统环境 软件、硬件、外设、数据库 微软与苹果的前期竞争。主板与CPU
    易安装性 在指定环境中是否易于安装 主流平台和系统100%测试用例,非主流10% flash安装
    共存性 不同的其他系统能共同运行 1、功能是否能正常运行满足要求
    2、系统性能能满足要求 是否会抢占资源。迅雷和pplive抢占资源。杀毒软件,瑞星和金山不能共存
    易替换性 替代为其他相同功能的产品的能力 升级过后的系统是否会造成系统崩溃 软件升级补丁升级
    可移植性的依从性 法律法规    
    效率-性能 时间效率 规定条件下,软件产品执行其功能时,提供适当的响应和处理时间以及吞吐率的能力 系统的反应时间 提款机取款时间的快慢
    资源效率 在规定条件下,软件产品执行其功能时,使用合适的资源数量和类别的能力 做一件事所占用的系统资源 电器所消耗的电能多少
    效率依从性 法律法规    
    维护性-维护的难易程度与成本 易分析性 软件产品诊断软件中的缺陷或失效原因或识别待修改部分的能力 辅助工具或者日志文件或者常用问题帮助手册 qq异常退出的帮助文件
    易改变性 代码容易被修复或修改 高内聚,低耦合  
    稳定性 软件产品避免由于软件修改而造成意外结果的能力 长期的监控一个系统的运行情况和系统的资源情况 淘宝的系统监控
    维护性的依从性 法律法规

    配置管理
    配置工具 有的话 用的工具叫什么名字
    安装------B/S(浏览器)(check in /check out:原理) C/S(客户端)

    4.CMMI把企业分为5个等级

    5.CMM与CMMI的区别
    cmmi:是不同cmm的集成,集成并发扬cmm的优点,并借鉴其他模型的优点融入新理论和实际研究成果,不仅能应用于软件领域,而且能应用于系统过程和其他过程领域,Cmmi和cmm最大不同: Cmmi1.1版本包含4个成分:系统工程(SE)、软件工程(SW)、应用集成产品和过程开发(IPPD)、供应商外包管理(SS) Cmmi有2种表示方法: 阶段式 连续式

    第五章 SQL
    数据库的价值目标:
    数据库的技术(不只是界面 还要知道数据库逻辑 1.要对数据库的设计理解 2.还有数据库对象的关系3.数据库的常见命令)
    常见数据库:MySQL Access(单机) MS-SQL(交互好) Oracle Sybase DB2
    MySQL 小巧 效率高 免费
    后三种Oracle Sybase DB2是大型收费,数据安全和备份好
    数据库作用:组织、存储、处理
    关系型数据库
    第一日:
    关系型数据库
    数据库的作用

    索引
    视图
    存储过程
    触发器
    事务 对象 优缺点 使用范围
    SQL Server具体操作
    建库
    建表
    备份 恢复 操作手册: 建库 建表 备份 恢复
    第二日:
    查询命令
    单表查询
    多表查询
    查询 统计功能测试点
    第三日:
    新增功能 新增功能测试点
    更新命令 修改功能测试点
    删除命令 删除功能测试点
    补:
    存储过程 学会构造大量测试数据
    触发器 了解 看懂 如何测试

    RDBMS 关系型数据库
    SQL Structured Query Language 结构化查询语言
    C/S Client/Server 客户 服务器
    B/S Browser/Server 浏览器 服务器
    第一天:
    文件类型:文件存储位置改变 程序代码更新 对大量数据量处理不恰当
    数据库的意义”
    1数据的重用(硬盘)
    2检索速度要提高(分类存储)
    3把数据与代码的耦合度降低(数据存放位置与代码无关)
    数据库管理系统 SQLServer
    Oracle
    MySQL
    DB2
    数据库的表结构
    1.数据分类
    2.数据关联
    数据库设计评审点
    1.数据存储是否有重复现象 不同表中是否存在相同字段(该字段既不是主键也不是外键)
    2.是否符合范式要求 同一个表中存在数据重复字段不要超过两个以上 可以保证冗余数据很少
    可以考虑有适当冗余
    3.对于业务有频繁查询要求的数据表
    4.表间关系是否正确 是否按照业务要求进行了数据关联
    5.数据库字段以及表的设计是否充分 数据字段内容是否涵盖需求要求的所有数据
    数据字段类型 及长度是否符合需求

    表 基本组成单元 有记录和 字段组成
    表中每一条数据
    存储数据
    数据分类
    字段位置变化 不影响程序
    索引 建立在字段上 可以对字段进行排序 同一张表索引
    更新索引字段值的时候 新增会打乱索引的顺序时
    视图 建立在数据库上 封装查询命令 存储数据 方便查询调用 提示安全性 显示结果固定
    如果在执行事务时没有执行完就实行事务回滚
    事务:是有两条以上的数据库命令组成的原子集合.该事务中的那条命令要么一次性执行成功,要么都不执行,如果在执行过程中执行失败那么该事务就进行回滚,将数据恢复到执行之前的状态
    是否存在某些业务要封装成事务?
    优点:可以保证有关联的数据库操作所对应的关联所需要的完整性和统一性
    缺点:不要将不相关的操作放在同一个事务中,否则会降低执行成功率和效率

    存储过程 存储过程封装了多条SQL命令,必须存放在数据库服务端
    优点: 减少了网络传输SQL命令的压力
    提高了访问的安全性
    SQL 命令存放在服务器端执行效率高
    缺点:需要编写和设计调试 它是一段程序 对于大批量的数据较弱 避免分支和循环
    触发器 自动执行 并且只能对一张表中进行触发 当某一张中发生操作时需要同时对其让表的数据进行操作可以考虑创建触发器

    过程:
    检查SQL服务是否启动
    图形界面建库 建表
    企业管理器
    命令行方式
    查询分析
    主键 用一标记该表中的记录 不能重复 不能为空
    外键 通过外键与其他表中的数据进行关联 只能是以存在主键值

    当前表的数据要被其他表使用时 要有主键
    外键 有主键存在时就要设置对应的外键

    搭建测试环境时或给用户安装产品
    海达票务处理
    C/B 建立空库hdpw1
    恢复
    安装服务器端
    安装客户端
    admin admin

    约束:
    1主键约束
    1 主键约束 primary key
    –特点:约束的字段数据,不能为空、不能重复。
    如果插入或者更新的数据为空或者重复将不允许进行操作。

    –语法:
    1)不命名的主键约束
    create table student
    (
    No int primary key,
    Name char(10),
    Sex char(2),
    ruxu datetime
    );
    2)命名的主键约束
    create table student
    (
    No int,
    Name char(10),
    Sex char(2),
    ruxu datetime,
    constraint pk_001 primary key(No)
    );
    3)删除主键约束
    –语法:alter table 表名 drop constraint 约束名;
    alter table student drop constraint pk_001;
    4)改主键约束
    –语法:alter table 表名 alter column 字段名 类型 not null;
    –alter table 表名 add constraint 约束名 primary key (字段名);
    alter table student alter column No int not null;
    alter table student add constraint PK_002 primary key(No);
    2 非空约束 not null
    1)增加非空约束
    create table student1
    (
    No int not null,
    Name char(10),
    Sex char(2),
    ruxu datetime
    );

    2)改非空约束
    alter table student1 alter column Name char(10) not null;
    3 外键约束 FOREIGN KEY
    –新增外键关系
    1)非命名的外键约束
    create table grade
    (
    no int foreign key references student(No),-- 字段名 字段类型 foreign key references 主表(主键字段)
    grade float
    );
    2)命名的外键约束
    create table grade
    (
    No int,
    grade float,
    constraint fk_001 foreign key (no) references student(no)
    );
    3)删除外键约束
    alter table grade drop constraint fk_002;
    4)改外键约束
    alter table grade add constraint FK_002 foreign key(no) references student(no) on delete cascade; --当主表进行数据删除时,从表一起删除
    alter table grade add constraint FK_002 foreign key(no) references student(no) on update cascade; --当主表进行数据更新时,从表一起更新
    alter table grade add constraint FK_002 foreign key(no) references student(no) on delete no action; --当主表进行数据删除时,违反了外键约束,拒绝进行删除操作
    alter table grade add constraint FK_002 foreign key(no) references student(no) on update no action; --当主表进行数据更新时,违反了外键约束,拒绝进行更新操作
    4 默认约束
    –default 当 一个字段不填写内为空时,默认插入一条数据
    1)增加default 约束
    create table sex
    (
    id int,
    sex char(2) default ‘男’
    );
    2)改 default 约束
    5 检查约束 check
    –只有满足检查约束的数据才能添加到表中,不满足的加不进来
    1)不命名的检查约束
    create table balance
    (
    id int,
    money float check (money >= 0)
    );

    2)命名的检查约束
    create table balance
    (
    id int,
    money float,
    constraint CK_001 check (money >= 0)
    );
    3)删除检查约束
    alter table balance drop constraint ck_001;

    4)改检查约束
    alter table balance add constraint CK_002 check (money >= 0);
    6 唯一约束 unique
    –对于字段输入的内容必须是唯一的,不能重复,但可以为空,多个字段都可以输入唯一约束
    1)不命名唯一约束
    create table tiger
    (
    tig_id int,
    tig_name char(10) unique
    );

    2)命名唯一约束
    create table tiger
    (
    tig_id int,
    tig_name char(10),
    constraint UN_001 unique(tig_name)
    );

    3)删除唯一约束
    alter table tiger drop constraint UN_001;
    4)改唯一约束
    alter table tiger add constraint UN_002 unique(tig_name);
    SQL语句
    创建数据库.
    语法:create database 数据库名字;
    数据库名字不能使用数字作为开头。可以使用字母或者_作为开头
    create database _51testing;

    开辟两块空间,一个是保存日志的,一个是保存表的
    删库 DROP DATABASE 库名;
    单行注释 ——
    多行注释 /* */
    切换数据库
    语法:use 数据库名;
    use _51testing;
    转换当前使用数据库
    备份数据库
    BACKUP DATABASE 库名 TO DISK=’文件名’;
    如BACKUP DATABASE xuanke TO DISK=’d:\test.bak’;
    恢复数据库
    RESTORE DATABASE 库名 FROM DISK=’文件名’;
    如RESTORE DATABASE xuanke FROM DISK=’D:\test.bak’;
    表、字段、类型
    建表
    语法:create table 表名字 ( 字段名 字段类型 字段约束 , 字段名 字段类型 字段约束 );
    create table tmp
    (
    NO int primarykey,
    NAME char(10)
    );
    字段约束可以没有
    删除表
    语法:drop table 表名
    注意:外键约束
    drop table grade
    建立外键
    Sid INT FOREIGN KEY (sid) REFERENCES student (sid)
    CREATE DATABASE xuanke1;
    USE xuanke1;
    –DROP database xuanke1;
    CREATE TABLE student1
    (sid INT PRImary KEY,
    sname CHAR(20) NOT NULL,
    sex CHAR(2) NOT NULL CHECK(sex IN (‘男’,‘女’)),
    card_id CHAR(20) NOT NULL unique CHECK(len(card_id) = 18),
    calss ChAR(20) NOT NULL,
    major CHAR(20) NOT NULL,
    birth_year INT CHECK(birth_year BETWEEN 1900 And 3000) NOT NULL,
    enrollment DATETIME NOT NULL,
    tel CHAR(20)
    )
    CREATE TABLE course1
    (
    cid INT PRIMARY KEY,
    cname CHAR NOT NULL,
    tname CHAR(20) NOT NULL,
    ctime DATETIME NOT NULL,
    cadress CHAR(20) NOT NULL,
    pre_course_id INT,
    )
    CREATE TABLE student_course1
    (
    sid INT FOREIGN KEY (sid) REFERENCES student1 (sid),
    cid INT FOREIGN KEY (cid) REFERENCES course1 (cid) PRIMARY KEY(sid,cid),
    grade INT
    )
    插入一条记录
    在表中增加数据(insert into)
    语法:
    1对表中所有的字段添加数据:insert into 表名 values (第一个字段的值,第二个字段的值,第三个字段的值…);
    注意1:values 值得个数一定要和表中字段的个数相等,一一对应。
    注意2:values 值得数据类型,必须要和表中字段的数据类型相匹配。int fload 值是数字,如果char 值得字符要用’值’。
    注意: 英文单引号 逗号 ,日期 要加单引号

    insert into student values (5,‘郑佳祺’,‘男’,‘003’,‘2011-08-23’,‘北京’,‘1979-01-01’);
    insert into student values (6,’’,‘男’,‘003’,‘2011-08-23’,‘北京’,‘1979-01-01’);
    insert into student values (7,‘庞鹏珏’,‘男’,3,‘2011-08-23’,‘北京’,‘1979-01-01’);

    2对表中指定字段添加数据:insert into 表名(字段1,字段2,字段3) values (值1,值2,值3);
    insert into student(stuid,stname,sex,family) values (8,‘齐倩’,‘女’,‘天津’);
    insert into student(stuid,stname,sex,family,birthday) values (9,‘例子1’,‘女’,‘天津’,’’);
    insert into student(stuid,stname,sex,family,class) values (10,‘例子2’,‘女’,‘天津’,’’);
    insert into student(stuid,stname,sex,family,class) values (11,‘例子3’,‘女’,‘天津’,NULL);
    insert into student(stuid,stname,sex,family,class) values (12,‘例子4’,‘女’,‘天津’,‘NULL’);
    删除数据
    语法:1.delete from 表名
    注意: 删除表中数据时要考虑约束。
    同样用法 truncate table 表名
    delete from grade
    truncate table grade;
    2.delete from 表名 [where 条件]
    delete from student where stname = ‘例子1’;
    1)单一条件
    DELETE FROM student WHERE sid=500;
    2)复合条件
    DELETE FROM student WHERE sex=‘男’ AND major=‘计算机’;
    DELETE FROM student WHERE major=‘计算机’ OR major=‘1’;
    DELETE FROM student WHERE sid BETWEEN 1 AND 500;
    DELETE FROM student WHERE sid>=1 AND sid<=500;
    3)模糊条件
    DELETE FROM student WHERE major LIKE ‘计算机%’; ———— % 通配符(计算机后面任意位任意字符)
    DELETE FROM student WHERE major LIKE ‘%务’;
    DELETE FROM student WHERE class LIKE ‘4%期’;
    DELETE FROM student WHERE class LIKE ‘4_期’; ———— _ 通配符(4后面一位的任意字符)
    DELETE FROM student WHERE sname LIKE ‘[xw]%’; ———— [] 从括号中取任意一个值
    Truncate table student 删除student表记录内容 但是后面不能加条件(不记录日志,速度快)
    更新表数据(修改一条记录)
    更新表数据
    –语法:update 表名 set 字段 = 值,字段2=值[where 条件]
    –注意1:字段类型 和 值得类型 要匹配,主键的值可以被修改
    –注意2:为了保证表数据的完整一直,最好在更新是增加WHERE 条件。
    select * from student
    update student set sex = ‘男’ ;
    update student set sex = ‘女’ where family = ‘天津’
    update student set family = ‘广州’ where family = ‘北京’
    将学号小于50的记录的入学时间更改为2011-5-18
    UPDATE student SET enrollment=‘2011-5-18’ WHERE sid<50;
    将‘计算机’专业的学生转成“计算机科学与技术”
    UPDATE student SET major=‘计算机科学与技术’ WHERE major LIKE ‘计算机’;
    将专业为“信管”开头和“汽车”开头的专业转成“自动化控制”
    UPDATE student SET major=‘自动化控制’ WHERE major LIKE ‘信管%’ OR major LIKE ‘汽车%’;
    将所有省为“北京”和“北京市”的记录统一成“北京”,并将这些记录的班级改成49期
    UPDATE student SET province=‘北京’,class=‘49期’ WHERE province LIKE ‘北京%’;
    将入学时间大于1985-1-1记录的出生时间改为比入学年月早18年
    UPDATE student SET borth_year=year(enrollment)-18 WHERE enrollment > ‘1985-1-1’;
    year() 是求DATE类型的年份
    查询
    简单的查询记录基础语法
    基础语法 select 查询的内容(也就是字段名1,字段名2) from 表名 where 条件
    (用*号可以显示所有字段)
    查询李进峰的所有基本信息
    SELECT * FROM student WHERE sname=‘李进峰’;
    查询李进峰和菲菲的所有基本信息
    SELECT * FROM student WHERE sname=‘李进峰’ OR sname=‘菲菲’;
    SELECT * FROM student WHERE sname IN (‘李进峰’, ‘菲菲’);
    查询所有姓张的同学的所有基本信息
    SELECT * FROM student WHERE sname LIKE ‘张%’;
    查询姓名中有“宇”的同学的所有信息
    SELECT * FROM student WHERE sname LIKE ‘%宇%’;
    查询姓名长度为3,第一个字为“李”最后一个字是“照”的记录
    SELECT * FROM student WHERE sname LIKE ‘李_照’;
    查询所有姓张和姓李的同学的学号和姓名以及出生年月
    SELECT sid,sname,borth_year FROM student WHERE sname LIKE ‘张%’ OR sname LIKE ‘李%’
    SELECT sid,sname,borth_year FROM student WHERE sname LIKE [张李]%’;
    查询姓“杨”并且所在省份为“河北省”的同学的学号
    SELECT sid FROM student WHERE sname LIKE ‘杨%’ AND province=‘河北省’;
    查询”北京”、”湖南”和”河南省”同学的所有信息
    SELECT * FROM student WHERE province=’北京’ OR province=‘湖南’ OR province=‘河南省’
    SELECT sid,sname,borth_year FROM student WHERE province IN (‘北京’, ‘湖南’, ‘河南省’);
    查询姓“”李但是所在省份没有“湖南”字样的同学的省份证号码
    SELECT card_id FROM student WHERE sname LIKE ‘李%’ AND province NOT LIKE ‘%湖南%’
    查询18岁(不含18)以前上学的同学的姓名和电话
    SELECT sname,tel FROM student WHERE year(enrollment)-borth_year<18;
    查询所有大于25岁的人的所有信息
    SELECT * FROM student WHERE year(getdate())-year(enrollment)>25;
    通配符
    使用环境,用于模糊查询,连接符号用Like 而非 =

    • 代表全部,所有,没有规定数量和内容。一般用于select 后面 不作为条件
      _ 代表一个字符,一个数据位。中文要用两个__ 。作为条件使用
      % 代表任意的,没有规定数量和内容。作为条件使用
      [值1,值2,值3] 从括号内的任意值。作为条件使用
      select * from student where stname like ‘张%’
      select * from student where stname like ‘[张李]%四’
      运算符。条件
      – = 两边内容相同。
      – > 左边大于右边。
      – < 右边大于左边。
      – >= 左边大于等于右边。
      – <= 右边大于等于左边。
      – <> 或者 != 不等于
      – between A and B 介于 A 和 B 之间,并且 >=a <=b .前面的数要小于后面的数,包含边界数的。
      select * from grade where coursegrade = 80
      select * from grade where coursegrade between 80 and 100
      条件关联
      – 条件1 and 条件2 :要同时满足条件1 和 条件2 是并且的关系。
      – 条件1 or 条件2 :只要满足一个条件就可以 是或的 关系。
      – () :先去执行括号中得条件
      – 字段 in (值1,值2,值3) :字段的内容要满足 值1 或 值2 或 值3
      select * from student where family = ‘北京’ and stname like ‘张%’
      select * from student where family in (‘上海’,‘天津’)
      排序
      –对查询到的结果按照升序或者降序的方式进行排列。
      –语法 order by 字段 排序方式 (desc,asc) asc 可以省略
      –注意:order by 一定要写在所有查询sql 的最后面
      select * from grade order by coursegrade
      聚合函数
      对查询的结果中某个字段进行函数运算
      sum(字段) :求和函数 字段需为数字类型
      avg(字段) :求平均值 字段需为数字类型.不会计算为空的条目。
      min(字段) :最小的值
      max(字段) :最大的值
      count(字段) :统计字段的个数,这里可以使用 * ,统计所有结果的行数。
      distinct(字段) :去重复
      注:聚合函数,不能放在WHERE中,需要放在HAVING里
      – 显示学生的总成绩
      select sum(coursegrade) from grade
      – 显示最高的成绩是多少
      select min(coursegrade) from grade
      select avg(coursegrade) from grade
      select * from grade
      select count(*) from student
      DISTINCT 去除重复行
      SELECT DISTINCT province FROM student;

    别名AS (Alias)
    as 临时别名
    列别名在WHERE中不能使用 GROUP BY 中不能使用 ORDER BY 中可以
    表别名可以在任何地方使用
    select class, sex, count(sex) as数量 FROM student GROUP BY class,sex ORDER BY class;
    select count(*) as 学生个数 from student
    select stname as 姓名,stuid as 学号from student
    SELECT和ORDER BY 不影响表中数据 所以可以使用别名
    分组里加WHERE和ORDER BY

    对真空的处理
    查询为空的字段 : 字段名 is null
    查询 不为空的字段 : 字段名 is not null
    select * from grade
    select * from grade where coursegrade is not null

    –统计每个课程的选修人数大于2个人的信息
    对课程分组,显示课程和对应的人数
    select couid,count(stuid) from grade group by couid having count(stuid) >2
    –统计<学生选修信息表>,统计每个同学的总成绩
    统计记录条数 count?
    查多少名学生
    SELECT count(*) FROM student
    查有多少种班级
    SELECT count (DISTINCT class) FROM student;
    查入学时间在1990-1-1到2000-12-31的学生个数
    SELECT count(enrollment) FROM student WHERE enrollment BETWEEN ‘1990-1-1’ AND ‘2000-12-31’ ;
    分组查询 GROUP BY
    先对查询内容进行分组、分类,然后在对一个组中的数据进行查询或者运算
    select 查询的内容 from 表名 group by 字段名
    select stuid,sum(coursegrade) from grade group by stuid
    select * from grade
    select stuid,sum(coursegrade) from grade where coursegrade is not null group by stuid
    分组前条件
    在group by 之前使用where 条件去选取数据
    分组后条件
    在 group by 分组后 + having 条件
    select stuid,sum(coursegrade) from grade where coursegrade is not null
    group by stuid having sum(coursegrade) >=200
    –统计<学生信息表>,统计每个班级中每种性别的学生人数
    select * from student
    –统计按照性别分得学生人数
    select sex,count(stuid) from student group by sex
    –统计<学生信息表>,统计每个班级中每种性别的学生人数,两个分组条件
    –统计<grade表>,统计每门课程的平均成绩,并按照成绩降序排序
    select couid,avg(coursegrade) from grade group by couid order by avg(coursegrade) desc

    RIGHT函数
    取身份证最后5位数
    SELECT right(card_id, 5) FROM student;
    将身份证补齐20位,前面用0补
    SELECT right(‘00’+card_id, 20) FROM student;
    SELECT ‘00’+card_id FROM student;
    LEFT函数
    取身份证的前10位
    SELECT left(card_id, 10) FROM student;
    将学号小于50的电话设置成NULL
    UPDATE student SET tel = NULL WHERE sid< 50;
    将电话为NULL的学生信息查询出来
    SELECT * FROM student WHERE tel IS NULL;
    判断条件是NULL的时候不能用= 要用IS IS NOT
    HAVING
    分组查询中,如果条件中有聚合函数,不能放在WHERE中,需要放在HAVING里
    查询平均出生年>1991的班级信息
    SELECT class,avg(borth_year) FROM student GROUP BY class HAVING avg(borth_year) > 1991 ;
    统计哪些班级男生平均年龄大于20
    SELECT calss FROM student WHERE sex=’男’ GROUP BY class HAVING avg(year(getdate())-borth_year)>20;
    优先级WHERE——GROUP BY——HAVING——PRDER BY
    查询学生的姓名和出生年,年龄最大的放前面
    SELECT sname, borth_year FROM student ORDER BY borth_year ;
    TOP 查询结果去前几个结果
    查询年龄最大的学生信息 年龄最大的前10名
    SELECT TOP 1 sname, borth_year FROM student ORDER BY borth_year ;
    SELECT TOP 10 sname, borth_year FROM student ORDER BY borth_year ;
    查询年龄最小的学生信息 年龄最大的前10名
    SELECT TOP 1 sname, borth_year FROM student ORDER BY borth_year DESC;
    SELECT TOP 10 sname, borth_year FROM student ORDER BY borth_year DESC;
    查询按照学号排序,前50个学生的信息
    SELECT TOP 50 * FROM student ORDER BY sid;
    查询最近入学的10名学生信息
    SELECT TOP 10 * FROM student ORDER BY enrollment DESC;
    关联查询(等值联接)
    内连接 等值连接
    外连接 1、左连 2、右连
    多表关联
    1内连接 等值连接 inner join
    语法 select 内容 from 表1 inner join 表2 on 等值条件
    select * from student inner join grade on grade.stuid = student.stuid
    select grade.couid,couname,coursegrade from grade inner join course on grade.couid = course.couid
    2外连接 outer join
    1)左连 select 内容 from 表1 left outer join 表2 on 条件
    2)右连 select 内容 from 表1 right outer join 表2 on 条件
    select * from grade outer join student on student.stuid = grade.stuid
    注:左连接 以 outer join 左边的表为准,要显示全部的左边表的数据,如果右边表没有关联的数据显示Null 与右边表比较 当右边表没有值时 只取左边表的记录
    select * from student right outer join grade on student.stuid = grade.stuid
    注:右连接 以 outer join 右边的表为准,要显示全部的右边表的数据,如果左边没有关联数据,显示Null
    多表关联
    语法:select 内容 from 表1,表2,表3 where 条件
    select stname,couname,coursegrade from student,course,grade
    where student.stuid = grade.stuid and course.couid= grade.couid and coursegrade >80
    select * from grade
    –所有男同学的平均成绩
    select avg(coursegrade) from student,grade where student.stuid = grade.stuid and sex = ‘男’ and coursegrade is not null
    select * from student
    3子查询,嵌套查询
    –当某一个查询的条件,是在另一个查询的结果集里面时,使用嵌套查询
    –查询李四的成绩。
    select * from grade where stuid = (select stuid from student where stname =‘李四’)
    –查询 张三和李四的成绩
    select * from grade where stuid in
    (select stuid from student where stname =‘张三’ or stname = ‘李四’)
    select * from grade where stuid = (select stuid from student where stname =‘张三’)
    or stuid = (select stuid from student where stname =‘李四’)
    –有考试成绩在 70~90的学生的姓名
    select stname from student where stuid in
    (select stuid from grade where coursegrade between 70 and 90)
    批量处理?
    DECLARE @sid INT 申请一个变量
    DECLARE @card_id CHAR(18)
    SET @sid=1 赋初值
    SET @card_id = ‘000000000000’+right(‘00000’+convert(VARCHAR(6),@sid),6) convert 强制转换数据类型
    PRINT @sid
    PRINT @card_id
    INSERT INTO student2 VALUES (@sid,‘蒋艳梅’,‘女’,‘49期’,‘计算机’,@card_id,1999,‘1999-9-9’,‘1111111’,‘1111’)

    CREATE PROCEDURE btInsert AS 或者 CREATE PROC btInsert AS --(保存一个过程)
    DECLARE @sid INT
    DECLARE @card_id CHAR(18)
    SET @sid = 1
    WHILE (@sid <= 100000)
    BEGIN
    SET @card_id=’1111111111111’+right(‘00000’+convert(VARCHAR(6),@sid),6)
    INSERT INTO student2 VALUES (@sid,‘蒋艳梅’,‘女’,‘49期’,‘计算机’,@card_id,1999,‘1999-9-9’,‘1111111’,‘1111’)
    SET @sid = @sid +1
    END
    EXECUTE btInsert 或者 EXEC btInsert (执行已经保存的过程)
    视图/虚表 view
    创建视图 create view 视图名 as (SQL)
    create view tiger as (
    select stname,couname,coursegrade from student,grade,course
    where student.stuid = grade.stuid and grade.couid = course.couid)
    –注意1.视图中不保存数据,不保存表结构,不保存表,没有约束。只保存sql 语句。
    –注意2.视图可以当表来使用,可以用视图去建视图。但是,不建议做 增 删 改的操作。
    –注意3.删除视图时,不会影响到原来的基础表。可以直接在视图中使用SELECT
    create view big_tiger as (select stname,coursegrade from tiger )
    delete from big_tiger where coursegrade is null
    删视图
    –语法:drop view 视图名
    drop view big_tiger
    drop view tiger

    视图的优点
    –1.对于使用sql 不熟练来说。视图是个很简单的查询途径
    –2.对于经常重复的复杂sql,使用视图可以提高查询效率
    –3.可以辅助用于权限管理,提高数据的安全性
    –4.帮助保护表和数据的完整一致性

    视图的缺点
    –1.降低了查询效率
    –2.增加保存空间
    –3.无法对数据和表进行操作。使得操作测试不方便进行。
    create view cll as (
    select class,count(stuid) as 人数 from student group by class
    )
    select * from cll where 人数 = 2

    –视图在企业中如何应用
    1.对于测试而言,降低测试的复杂度。例如:复杂 sql 写一次保存视图,以后都直接调用视图。
    2.对于测试而言,降低工作难度。例如:新来的新人可能对表结构不熟悉,对sql 也不熟悉,使用视图可以尽快上手开始工作。
    3.对于开发而言,降低服务器的工作。提高服务器运行效率。例如:如果不用视图,查询语句要在服务器端生成传给数据库。如果使用视图,服务器直接查询视图就可以满足查询功能。
    4.对产品设计和业务人员而言,降低了工作难度,不用学习和使用复杂sql 语言。例如:如果产品或者业务人员想去查询某些指定的数据时,只要让开发或测试人员建一个视图。直接去查询视图就可以了。
    索引
    –索引就像目录,表中对于一个字段的目录结构。如果想操作表中的数据,先在索引字段中找到那一行,然后再去操作那一条数据。
    –注意1:建议索引设置在主键、外键、唯一约束,效率高
    –注意2:索引是应用于查询条件的。经常使用的查询条件字段,应该被设置为索引字段。
    –注意3:一个表中可以有多个索引,索引之间可以交叉字段,一个索引可以有多个字段。

    索引的优点:
    –1.提高查询效率
    –2.提高了排序效率

    索引的缺点:
    –1.占用数据库资源
    –2.降低了对数据库增、删、改的效率

    索引在企业中如何应用:
    –1.页面上如果有很多的查询条件并且需要排序时,建议一一创建索引。
    –2.检查是否主键和外键、唯一约束建立了索引关系。
    –3.在测试过程中应该检查,运行查询条件时是否使用了索引。
    –4.测试中检查,平凡被更新的表,不要设置太多的索引。

    –建索引
    –语法:create index 索引名 on 表(字段,字段…);
    create index suoyin1 on student(stname,family);

    –删索引
    –语法:drop index 表名.索引名
    drop index student.suoyin
    存储过程 procedure
    –把一些sql 放在一起执行,并且有逻辑的执行。存储过程。
    –语法1:简单的无参数存储过程。
    – create procedure 存储过程名 as sql语句
    create procedure aa as
    update grade set couid = 6 where couid =1;
    update course set couid = 6 ,couname = ‘生物’ where couid =1;

    执行存储过程
    –语法 exec 存储过程名
    exec aa;

    select * from grade;
    select * from course;

    语法2:有参数的存储过程
    – create procedure 存储过程名 (@参数1 参数类型,@参数2 参数类型…) as sql语句
    create procedure BB (@id int,@NewId int,@name char(10))
    as
    update grade set couid = @NewId where couid = @id;
    update course set couid = @NewId ,couname = @name where couid =@id;

    执行存储过程
    –语法 exec 存储过程名(参数1,参数2,参数3)  
    SQL SERVER 参数不加括号
    –注意1.参数的个数和 数据类型必须与定义相一致
    –注意2.存储过程中的sql 是按照顺序执行的
    –注意3.当存储过程中一个sql失败时。不会影响其他sql的执行。
    exec BB 3,13,‘SQL SERVER’
    alter table grade add constraint CH check (couid < 20)
    update grade set couid = 21 where couid = 13;
    exec BB 13,21,‘ORACLE’
    create database bank;
    use bank;
    create table bj_bank
    (
    name char(10),
    money int check(money>=0)
    constraint pk_nanme_bj primary key (name)
    事务 transaction
    把所作的操作放在一组,如果有一个失败就全失败,都成功时候才成功
    事务是存储过程的一个部分,存储过程的一个写法
    – begin transacion 事务名
    – commit transacion 事务名
    – rollback transacion 事务名
    create procedure zz1
    (
    @a_name char(10),
    @b_name char(10),
    @a_money int,
    @b_money int,
    @c_name char(10)
    )
    as
    begin transaction c1 --开始事务c1
    if (select money from bj_bank where name = @a_name) > 0 --判断a账户
    begin
    update bj_bank --从a中减去
    set money=money-@a_money
    where name=@a_name;
    update zs_bank --向c中添加
    set money=money+@a_money
    where name=@c_name;
    commit transaction c1
    end
    else
    rollback transaction c1 --如果判断失败回滚所有操作

    begin transaction c2 --开始事务c2
    if (select money from bj_bank where name = @b_name) > 0 --判断b账户
    begin
    update bj_bank --从b中减去
    set money=money-@b_money
    where name=@b_name
    update zs_bank --向c中添加
    set money=money+@b_money
    where name=@c_name
    commit transaction c2
    end
    else
    rollback transaction c2 --如果判断失败回滚所有操作

    update bj_bank set money = 5000 where name = ‘a’;
    update bj_bank set money = 1000 where name = ‘b’;
    update zs_bank set money = 0 where name = ‘c’;

    exec zz1 a,b,250,600,c
    select * from bj_bank
    union
    select * from zs_bank
    exec zz2 a,b,500,700,c
    exec zz2 a,b,1000,500,c

    存储过程 zz2 开始
    create procedure zz2
    (
    @a_name char(10),
    @b_name char(10),
    @a_money int,
    @b_money int,
    @c_name char(10)
    )
    as
    begin transaction guopeng
    if (select money - @a_money from bj_bank where name = @a_name) >= 0
    begin
    update bj_bank set money = money - @a_money where name = @a_name;
    update zs_bank set money = money + @a_money where name =@c_name;
    commit transaction guopeng;
    end
    else
    begin
    rollback transaction guopeng;
    end
    begin transaction qiqian
    if (select money - @b_money from bj_bank where name = @b_name) < 0
    begin
    rollback transaction qiqian;
    end
    else
    begin
    update bj_bank set money = money - @b_money where name = @b_name;
    update zs_bank set money = money + @b_money where name =@c_name;
    commit transaction qiqian;
    end
    存储过程zz2 结束
    存储过程循环。大批量的制造测试数据。
    create procedure ww
    (
    @name char(10),
    @money int,
    @count int
    )
    as
    declare @num int;
    set @num = 0;
    while (@num < @count)
    begin
    insert into bj_bank values (@name,@money);
    set @num = @num +1;
    end;
    drop procedure ww
    alter table bj_bank drop constraint pk_nanme_bj
    exec ww ‘王沙’,900,1000000;
    select * from bj_bank where money = 30
    truncate table bj_bank

    –存储过程在企业中如何应用:
    –1.对于开发、对于测试而言,存储过程简化了工作难度。
    –2.对于开发、测试而言,存储过是可以实现逻辑的。
    –3.对于测试而言,帮助我们大批量的生成测试数据。
    –4.对于测试而言,帮助我们去检查对数据库的操作数据是否符合预期
    –5.对于开发而言,存储过程支持事务,可以做逻辑编程

    –删除存储过程
    –语法 drop procedure 存储过程名
    drop procedure ww
    触发器 trigger
    当满足触发条件时,执行后面的触发sql,支持事务
    语法: create trigger 触发器名 on 工作表表名 for 触发方式 as sql语句
    触发条件 delete,update 触发条件因数据库而异
    create trigger trigger_student_delete
    on student
    for delete
    as
    delete grade from grade,deleted where grade.stuid = deleted.stuid;
    select * from student
    select * from grade
    delete from student where stuid = 1
    alter table grade drop constraint FK__grade__stuid__7D78A4E7
    练习
    一、单表查询练习
    1、查询<学生信息表>,查询学生"张三"的全部基本信息
    Select * from student where stname=’张三’;
    2、查询<学生信息表>,查询学生"张三"和”李四”的基本信息
    select * from student where stname in (‘张三’,‘李四’)
    3、查询<学生信息表>,查询姓"张"学生的基本信息
    select * from student where stname like ‘张%’;
    4、查询<学生信息表>,查询姓名中含有"四"字的学生的基本信息
    select * from student where stname like ‘%四%’;
    5、查询<学生信息表>,查询姓名长度为三个字,姓“李”,且最后一个字是“强”的全部学生信息。
    select * from student where stname like ‘李_强’;
    6、查询<学生信息表>,查询姓"张"或者姓”李”的学生的基本信息。
    select * from student where stname like ‘张%’ or stname like ‘李%’;
    7、查询<学生信息表>,查询姓"张"并且"所属省份"是"北京"的学生信息
    select * from student where stname like ‘张%’ and family =‘北京’;
    8、查询<学生信息表>,查询"所属省份"是"北京"、”新疆”、”山东”或者"上海"的学生的信息
    select * from student where family in (‘北京’,‘新疆’,‘山东’,‘上海’);
    9、查询<学生信息表>,查询姓"张",但是"所属省份"不是"北京"的学生信息
    select * from student where family!=‘北京’ and stname like ‘张_’;
    10、查询<学生信息表>,查询全部学生信息,并按照“性别”排序,性别相同的情况下按照“所属省份”排序,所属省份相同的情况下再按照“班级”排序
    select * from student order by sex,family,class; (多个排序条件,用逗号以此分开,先排第一个、再排第二个。。。。)
    11、查询<学生信息表>,查询现有学生都来自于哪些不同的省份
    select distinct (family) from student;
    (注意distinct使用方法)
    12、查询<学生选修信息表>,查询没有填写成绩的学生的学号、课程号和成绩
    select couid,couid,coursegrade from grade where coursegrade is null;
    13、查询<学生选修信息表>,查询全部填写了成绩的学生的选修信息,并按照“成绩”从高到低进行排序
    select * from grade where coursegrade is not null order by coursegrade desc;
    二、聚合函数练习
    1、统计<学生信息表>,统计共有多少个学生
    select count(stname) from student;
    2、统计<学生信息表>,统计年龄大于20岁的学生有多少个
    select count(stname) from student where (year(getdate())-year(birthday))>20;
    3、统计<学生信息表>,统计入学时间在1998年至2000年的学生人数
    select count(stuid) from student where year(enrollment) between 1998 and 2000;
    4、统计<学生选修信息表>,统计学号为"S001"的学生的平均成绩
    select avg(coursegrade) from grade where stuid=‘1’;
    5、统计<学生选修信息表>,统计学号为"S001"的学生的总成绩
    select sum(coursegrade) from grade where stuid=‘1’;
    6、统计<学生选修信息表>,查询课程号为”C001”的课程的最高成绩
    select max(coursegrade) from grade where couid=‘1’;
    7、统计<学生信息表>,查询所有学生中的最大年龄是多少
    select max((year(getdate())-year(birthday))) from student;
    三、分组查询练习
    1、统计<学生选修信息表>,统计每个课程的选修人数
    select count(*) from grade group by couid;
    2、统计<学生选修信息表>,统计每个同学的总成绩
    select sum(coursegrade) from grade group by stuid;
    3、统计<学生信息表>,统计每个班级中每种性别的学生人数,并按照班级排序
    select class,sex,count(stuid) from student group by sex,class order by class;
    4、统计<学生选修信息表>,统计每门课程的平均成绩,并按照成绩降序排序
    select avg(coursegrade) from grade group by couid order by avg(coursegrade) desc;
    5、统计<学生选修信息表>,显示有两门以上课程不及格的学生的学号
    select stuid from grade where coursegrade<60 group by stuid having count(stuid)>2;
    6、统计<学生信息表>,统计每个班级中的最大年龄是多少
    select max(year(getdate())-year(birthday)) from student group by class ;
    四、嵌套查询练习
    1、用子查询实现,查询选修“高等数学”课的全部学生的总成绩
    select sum(coursegrade) from grade where couid=(select couid from course where couname=‘高等数学’);
    2、用子查询实现,统计<学生选修信息表>,显示学号为"S001"的学生在其各科成绩中,最高分成绩所对应的课程
    思考:如果该学号学生有两个课程分数都为最高的100分,查询会有什么结果(显示2个结果)
    select couname from course where couid=(select couid from grade where coursegrade in (select max(coursegrade) from grade where stuid=1)
    3、用子查询实现,查询2班选修"数据库技术"课的所有学生的成绩之和
    select sum(coursegrade) from grade where stuid in(select stuid from student where class=‘002’) and couid=(select couid from course where couname=‘数据库技术’);
    4、用子查询实现,查询3班"张三"同学的"测试管理"成绩
    select coursegrade from grade where stuid in (select stuid from student where class='003’and stname=‘张三’) and couid=(select couid from course where couname=‘测试管理’);
    五、联接查询练习
    1、查询"张三"的各科考试成绩,要求显示姓名、课程号和成绩
    select stname,couid,coursegrade from student inner join grade on student.stuid=grade.stuid and stname=‘张三’;

    select stname,couid,coursegrade from student,grade where student.stuid=grade.stuid and stname=‘张三’;
    2、查询"张三"的各科考试成绩中,哪科没有记录考试成绩,要求显示姓名、课程号和成绩
    select stname,couid,coursegrade from student inner join grade on student.stuid=grade.stuid and stname='张三’and coursegrade is null;

    select stname,couid,coursegrade from student,grade where student.stuid=grade.stuid and stname='张三’and coursegrade is null;
    3、查询"张三"的各门课程成绩,要求显示姓名、课程名称和成绩
    select stname,couname,coursegrade from student,course,grade where student.stuid=grade.stuid and course.couid=grade.couid and stname=‘张三’;
    4、查询3班"张三"的"测试管理"成绩,要求显示姓名、成绩
    select stname,coursegrade from student,course,grade where student.stuid=grade.stuid and course.couid=grade.couid and couname=‘测试管理’ and stname='张三’and class=003;
    5、查询所有2000年以前入学的,各班男生的各科考试平均成绩
    select class,avg(coursegrade) from grade,student where grade.stuid=student.stuid and sex='男’and year(enrollment)<2000 group by class ,couid;
    六、外联接查询
    查询”李坚强”所有课程的成绩,并显示学号、姓名、课程号和成绩,没有成绩记录的学号包括:(‘S009’,‘S010’,‘S011’)
    1、使用右联接
    select grade.stuid,stname,couid,coursegrade from grade right outer join student on student.stuid=grade.stuid and stname=‘李坚强’;
    2、使用左联接
    select grade.stuid,stname,couid,coursegrade from student left outer join grade on student.stuid=grade.stuid and stname=‘李坚强’;
    3、对比等值连接
    select grade.stuid,stname,couid,coursegrade from grade inner join student on student.stuid=grade.stuid and stname=‘李坚强’;
    七、补充提高
    1、查询“张三”比“王三”入学早几年
    select year(enrollment)-(select year(enrollment) from student where stname=‘王三’) from student where stname='张三;
    2、查询所在班级和该班内学生的年龄之和,其中每个人的年龄都大于20岁,每个班的年龄之和大于60岁
    select class,sum(year(getdate())-year(birthday)) from student group by class;

    第六章 C语言
    ATM机
    1.取钱 2.存钱 3.查询余额 4.转账

    作业1:存钱、查询、转账的流程图

    软件模块结构图
    需求:十进制一位数加法
    XX.C ——C语言的源文件
    编译 ——转换成二进制的机器语言
    XX.exe ——可执行文件

    不需要编译的脚本语言: PHP ASP JSP PYTHON VBS PEER TCL
    需要编译的高级语言: C VC C# Delphi Java .Net

    注释: // 和/* */

    作业3:绘制流程图中所有界面

    C语言 面向过程的语言 执行过程是自上向下 函数
    F10 单步执行 但不进入函数体
    F11 单步执行 进入函数体
    C语言中的存储
    数据类型
    基本数据类型
    1浮点型
    默认小数位是6位,若输出《6位,完成四舍五入
    若赋予的值小数位《5,后边会随机补数
    vc 对于定义的浮点型变量会默认为double类型,因此会产生警告。想没有警告,强制在赋值语句中的数据后加f. 比如,
    float x;
    x=0.33f;

    数组
    1)一维数据
    定义:数据类型 数组名[长度];
    初始化:1)先定义,后赋值;
    int a[3];
    a[0]=34;
    a[1]=45;
    a[2]=345;
    2) 边定义,边初始化
    float b[2]={4556.234,45.345};
    3) 只给第0个初始化,后边补0
    double c[3]={345.345345};
    访问:下标从0开始遍历数组

    2)二维数组
    定义:数据类型 数组组[行的长度][列的长度];
    初始化: 1)先定义,后赋值

         int a[2][4];
           a[0][0]=3;
           a[0][1]=4;
           a[0][2]=5;
           a[0][3]=6;
           a[1][0]=7;
           a[1][1]=8;
           a[1][2]=9;
           a[1][3]=10;
           2)边定义,变赋值;
             int b[2][4]={{3,4,5,6},{7,8,9,10}};
    

    访问: 行和列的下标从0开始遍历数组

    2字符串
    定义:char 字符串名[长度];
    初始化:先定义,后赋值
    边定义,边赋值
    1)
    char p1[5]=“abcd”; 对 (因为自动补\0占一位)
    char p2[5]=“abcde”; 错
    2)
    char p3[5]={‘a’,‘b’,‘c’,‘d’,’\0’}; 对
    char p4[5]={‘a’,‘b’,‘c’,‘d’,‘0’}; 错
    输出:prinf("%s",p1);
    printf("%c",p1[0]);
    输入:scanf("%s",&p1); /读整个字符串/
    scanf("%c",&p1[2]); /读单个字符/
    注意:赋值的长度小于定义长度时,会逐个补空

    结构体
    边定义,边初始化: struct 结构体名 {
    数据类型 数组名[长度];
    数据类型 变量名;
    …} 结构体变量名={初始化对应值}
    struct student {
    char name[10];
    int age;
    } s1={“zhangsan”,18};
    访问:结构体变量名.结构体内部定义的变量名。
    printf("%s",s1.name);
    printf("%d",s1.age);

    结构体数组
    边定义,变初始化:
    struct 结构体名 {
    数据类型 数组名[长度]; /表示长度/
    数据类型 变量名;
    …} 结构体数组变量名[长度]={{初始化对应值1},{初始化对应值2},} /这里的长度表示多少个,相当于表中的记录数/

      struct student {
                  char name[10]; /*name这个字符串能有效存储字符数9个,还有1位补空*/
              int age;
            } s[2]={{"zhangsan",18},{"lisi",20}};   /*这里的2表示有2条记录*/
    

    3运算符
    算数运算符: + - * /(求商) %(取余)
    关系运算符: > < >= <= ==(等于) != (不等于)
    真(非0) 假0
    逻辑运算符
    && (与) || (或) ! (非)

    常量
    const 数据类型 常量名=值;
    例如,圆周率 const int pi=3.14;

    变量

    1. 变量是什么?

    2. 先定义,后使用
      标准c
      int x;
      x=3;
      int j;
      j=89;

      vc
      int x;
      int j;
      x=3;
      j=89;

    3. 使用赋值语句修改变量值

    4. 变量赋予的值由数据类型决定

    5. 变量包含名字、值和地址

    6. 不同的数据类型在计算机中存储的字节大小不一样
      16位 32位
      int 2个字节 4
      long 4 4
      float 4 4
      double 8 8
      char 1 1

    7. 变量的名字
      首字母:字母,下划线
      包含:字母,下划线,%, 数字
      不能使用c语言的保留字

    8. 全局变量和局部变量
      1)局部变量如何定义:在函数中定义的变量都成为局部变量。离开函数(调用完函数),该变量消失。
      2)全局变量如何定义:在main函数上边定义的变量成为全局变量。对所有的函数都生效。
      3)当局部变量和全局变量同名时,实际上修改的是局部变量。
      常量
      常量的定义

    define 常量名 常量值

    常量名要全用大写字母
    常量没有数据类型
    1.数组
    定义数组
    数据类型 数组名[N] ;
    N 代表数组的个数

    结构体
    定义
    Struct 结构体名
    {
    变量1
    变量2



    }
    例:
    Struct
    {
    Char name[8];
    Char sex[2];
    Int age;
    Int grade;
    } Stu_49[2]={{“张三”,”男”,18,60},{“李四”,”女”,19,100}};
    结构体的显示
    Printf(“ %s”,Stu_49[0].name);
    条件/分支逻辑
    Switch
    switch (表达式)
    {
    case 值1: 处理语句1;
    break;
    case 值2:处理语句2;
    break;
    case 值3:处理语句3;
    break;
    。。。。

    }
    注意1:值的数据类型要和表达式返回值的类型要匹配;
    注意2:不要忘了使用break退出后续执行;
    注意3:表达式要使用括号括起来;
    

    条件 只能输入固定的值
    If
    分支结构
    条件
    情况1: 对应流程图,假分支直接指向后续语句,不含有处理语句
    if (条件)
    处理语句1;
    处理语句2;

    情况2: 对应流程图,处理语句1和处理语句对应真分支,处理3和4对应假分支
    if (条件)
    处理语句1;
    处理语句2;

    else
    处理语句3;
    处理语句4;

    情况3
    If (表达式1)
    if(表达式2)
    语句2;
    Else
    语句3;
    Else
    语句1;
    循环
    For
    循环结构
    定义循环控制变量
    for (控制变量赋初始值;控制变量终止条件;累加(减)器)
    {
    循环体语句;
    }

    while (条件)
    {

    循环体语句;
    }

    do
    {
    循环体语句;
    } while (条件)

    for(表达式1;满足循环条件;表达式2)
    {
    循环体;
    }
    while
    while (表达式)
    {
    循环体
    }
    do…while
    do
    {
    循环体
    }
    While(表达式)
    申请卡号时,多申请一位 并用\0座位数据的结束
    Do while 在条件不满足时,会执行一次do中的循环体
    有参数有返回值的函数
    错误跟踪 写日志
    函数
    函数:

    1. 输出函数
      1.1 printf(控制格式,变量);
      控制格式 %d - 整形
      %c - 字符型
      %ld -长整形
      %f -浮点型 (想控制小数位 %.3f)
      %lf --双精度 (想控制小数位 %.5lf)
      1.2 printf(“你想输出的内容”);
    2. 输入函数
      scanf(“控制格式”,&变量1,&变量2,…);
      %c
      %d
      %ld
      %f
      %lf
      int x,y;
      x=234;
      y=234;
      scanf("%d%d", &x,&y);
    3. 存在的必要性
      1) 职能单一
      2)定位问题
      3)便于使用
      4) 减少代码冗余
    4. 项目中只能有一个main函数。否则移除。
    5. 函数使用
      先定义(在main函数外定义)-》调用-》申明(在main函数外边申明)
      注意1:定义的函数名、调用的函数名、申明的函数名必须同名
      5.无返回且无形参的函数
      定义:
      void 函数名()
      {
      语句1;
      语句2;
      。。。。。
      }
      调用:
      函数名();
      申明:
      void 函数名();
    6. 无返回有形参的函数
      定义:
      void 函数名(形参列表)
      {
      语句1;
      语句2;
      。。。。。
      }
      在这里,形参列表实际上就是变量定义列表,只是因为放在函数定义中就叫叫它形参。
      比如:int x,int y,int z,char m
      调用:
      函数名(实参列表);
      在这里,实参列表的个数和形参列表个数一致;类型一致;实参列表可以相同数据类型的值,也可以是相同数据类型的变量
      申明:
      void 函数名(形参列表);
    7. 有返回有形参的函数
      定义
      数据类型 函数名(形参列表)
      {
      语句1;
      语句2;
      。。。。。
      return 值
      }
      在这里,return的值必须和函数名之前的数据类型匹配。
      调用:
      定义一个和函数返回数据类型相同的变量
      变量=函数名(实参列表);
      申明
      数据类型 函数名(形参列表);
    8. 函数名在一个程序中不能同名
      程序结构
      #include 语句
      #include <stdio.h>
      函数申明
      main函数
      自己定义的函数
      其他
    9. 字节
    10. ascii
      char m;
      m=“k”;
      /* 输出k字符的ascii码*/
      printf(“m=%d\n”,m);
    11. 转义
      \ - 转义符
      \n -控制换行
      \t -控制tab键的距离
      \0 -空
    12. 注释
      单行
      跨行 /* 语句 */
      Void 函数名()
      Void 函数名(参数1,参数2…)
      Int 函数名()
      Int 函数名(参数1,参数2…)
      注:1调用函数时 传入的参数类型 要和函数参数的类型一致,返回值也是一样
      2对有参数没有返回值的函数测试
      3对没有参数 没有返回值的函数测试
      4 测试main()函数 复制了一个定义为test_main()

    补充知识:
    1个字节=8位 编码是ASCII码 又叫ANSI(美国标准码)
    用两个字节表示中文 编码是GB2312
    当16位全部占用时 编码是GB18030
    后来同意标准 全部用16位表示 编码用UTF-8 只翻译16位中的低8位
    注: C语言中main()函数默认是返回int 类型的值

    第七章 Windows环境搭建
    一、名词注解与定义:
    环境:分为{1、生产环境;2、测试环境;3、开发环境。
    生产环境(也称为:上线环境)是运维人员的工作环境,有时候测试人员也会参与运维的部署工作)。
    测试环境:测试人员为了测试软件在生产环境中的功能和性能的表现,而尽量可能的模拟了一个生产环境来进行软件测试。
    开发环境:就是开发人员编写软件代码的工作环境。
    一个软件要从开发环境——>测试环境——>生产环境这个环境流程。
    问:为什么不在开发环境中进行软件测试,要测试环境中进行?
    答:因为开发环境它具有可变性,其影响测试的准确性,所以为了保证数据独立性和其测试的准确性,软件测试最好在测试环境中进行。
    测试环境-分为:1、硬件环境;2、OS(操作系统)环境;3、软件环境。
    其硬件环境和OS(操作系统)环境都是要根据被测软件的需求来搭建;软件环境包括:被测试软件和一些用户常用软件,主要测试被测软件和常用软件之间的兼容性、冲突。
    搭建:先要根据需求架设硬件环境,再根据需求架设OS系统环境,要保证架设的OS系统是无毒的,最后架设软件环境,卸载无用的软件,确认软件的证件号来保证一些不必要的错误和冲突。
    为什么要保证架设的OS操作系统环境是无毒的?因为病毒可能产生一些和被测软件无关的BUG。解决方法:可安装杀毒软件,重装系统来防止和保证。
    被测软件-分为:1、单机版;2、C/S(client/server);3、B/S(browser/server)。三种运行模式。
    C/S(client/server):是分为客户端、服务端和数据库端(如:PPS、QQ需要用户先安装客户端)。其架设的软件会用客户端来分担一部分工作;
    优点:运行速度快、部分数据存放在本地;
    缺点:兼容性差,要根据不同的系统来开发不同的系统版本,成本高和测试成本高。
    B/S(browser/server):是可以用IE浏览器直接访问和运行的一种模式,不用预先安装客户端(如:网页游戏、网上订票系统等)。
    优点:兼容性好,数据保密性好;
    缺点:运行速度较慢。
    软件要根据不同的运行环境、性能的要求来选择使用C/S架设,还是用B/S架设。
    扩展内容:
    app:应用软件,是安装在OS(操作系统)上面的。
    光的三原色:红。绿。蓝。
    服务器(软件):
    1、应用服务器:IIS,Weblogic,JBoss;
    2、Web服务器软件:Apache,Nginx,LightHttpd;
    3、数据库服务器:SQL Server,MySQL(Strucrure Query Language),Oracle;
    4、邮件服务器:QMail,Exchange,Lotus;
    5、FTP服务器

    C/S
    海达票务系统
    1.硬件环境和操作系统(略)
    2.安装SqlServer,安装客户端和服务器端软件
    3.测试数据准备,环境初始化
    DROP DATABASE hdpw1;
    CREATE DATABASE hdpw1;
    RESTORE DATABASE hdpw1 FROM DISK=‘D:\training\doc\win\student\海达票务\hdpw1’;
    4.参数配置
    4.1 Client端配置,和Server端的链接
    4.2 服务器端配置,和数据库的链接
    4.3 数据库端配置(略)
    5.启动整个海达票务系统
    系统更新时,需要更新C和S端,而B/S架构只用更新S端
    B/S
    安装虚拟机:
    1.打开VMWare
    2.新建一个虚拟机
    3.更改光驱加载的ISO
    4.开启电源
    5.将鼠标焦点放到GuestOS里(如何到HostOS?热键)
    6.格式化磁盘
    7.后续安装的步骤
    8.配置网络
    9.安装VMWareTools

    配置共享文件夹:

    进销存系统
    1.安装IIS (通过本机IP访问和netstat查看进程是否成功启动)

    2.把jxc文件夹拷贝到guest
    3.在IIS里新建一个虚拟目录->指向jxc文件夹

    4.设置jxc文件夹的权限

    5.启动相关扩展

    6.启用虚拟目录的父路径

    7.设置默认首页

    OA系统
    1.安装JDK

    2.将Jdk的bin目录追加到环境变量的Path变量里(如:C:\jdk6\bin)
    注:放在path里可以在cmd中输入命令运行,系统将自动定位jdk所在目录

    3.新建一个系统变量 JAVA_HOME,值为jdk的安装目录(如:C:\jdk6)

    4.新建一个系统变量 CLASSPATH,(如:.;C:\jdk6\lib\tools.jar;C:\jdk6\lib\dt.jar) . 指当前目录 注:CLASSPATH (java的类库)

    注: . 表示当前目录 允许jdk查找当前目录
    5.安装Tomcat,解压后运行startup.bat
    通过127.0.0.1:8080 访问,可以看到Tomcat的欢迎页面
    也可以修改conf/server.xml文件,更改Tomcat启动的端口号

    注:在conf下server.xml中查找8080(默认端口)修改端口号 更改配置后要重启服务器
    用startup.bat启动tomcat
    6.安装MySql

    7.部署OA的程序,将程序拷贝到tomcat的webapps目录

    1)先到Mysql里把测试数据导入
    登录mysql mysql -uroot -p
    进入mysql控制命令行,显示如下
    mysql>

    从硬盘上导入一个sql文件

    mysql> source c:~~~~~\oa\setup\redmoon.sql (本质是执行Sql文件里的每一个SQL语句)
    2)修改和数据库的连接、日志和缓存参数
    oa/WEB-INF/proxool.xml 改里面的数据库密码即可

    oa/WEB-INF/log4j.properties 改日志的路径,并创建相应的路径

    oa/WEB-INF/classes/cache.ccf 改缓存的路径

    8.重新启动Tomcat
    访问 127.0.0.1/oa
    admin
    111111
    注:参考 windows环境搭建
    补充知识:
    查看服务:

    MySql相关命令
    show databases 查看有哪些库
    show tables 查看库有哪些表

    desc 表名 查看表结构

    浏览器引擎: 1.渲染页面 2.执行客户端脚本JavaScript 3.执行自由控件
    w3school.com.cn学习前端语言的网站

    Trident IE
    Gecko FireFox
    Presto Opera
    Webkit Safari

    Cookies 保存用户名 密码 根据浏览器、域名保存 一个浏览器、域名一个Cookies 唯一标识一个客户
    Session 和cookies类似 是服务器端保存的
    Session 和cookies是一一对应的
    TCP/IP分四层 OSI七层

    应用层传输协议:http pop3 smtp
    传输层协议:tcp udp
    网络层协议:ip arp
    物理层协议:mac pppo
    三次握手
    目的:建立连接
    1.给服务器发送一个包[SYN] 处于send状态
    2.服务器回复一个包[SYN,ACK] 处于receive状态
    3.给服务器返回一个包[ACK] 两边同步待命 准备发送

    注:参考 网络模型和协议.vsd
    Session/Cookie:Session是存在服务器的,关闭浏览器则删除Cookie存在客户端,可以设置生命周期

    JAVA环境搭建
    1.安装jdk程序
    2.配置环境变量
    1)增加JAVA_HOME,值为jdk的根目录
    2)修改Path,在后面追加 ;%JAVA_HOME%/bin
    3)增加CLASS_PATH,值为 .;%JAVA_HOME%/lib/tools.jar
    3.运行cmd,检查java版本
    java -version
    4.运行一个java程序
    1)编辑文件 HelloWorld.java,内容为
    public class HelloWorld {
    public static void main(String[] args) {
    System.out.println(“Hello, world!”);
    }
    }
    2) 编译java文件,生成字节码文件
    javac HelloWorld.java
    3) 运行字节码文件
    java HelloWorld

    安装Tomcat

    1. 解压至某目录
    2. 运行 bin/startup.bat
    3. 使用浏览器访问
      http://127.0.0.1:8080

    安装MySQL

    1. 运行MySQL安装程序,next
    2. MySQL的配置
      1. 编码
      2. 密码
      3. 服务
    3. mysql -uroot -p

    部署oa

    1. 将程序文件拷贝到tomcat的webapps目录
    2. 初始化测试数据,登录到mysql控制台,运行source命令
      mysql> source c:…\redmoonoa.sql
    3. 程序配置
      1. oa程序和数据库的连接配置
        WEB-INF\proxool.xml
        修改用户名和密码
      2. oa的日志配置文件
        log4j.properties
        log4j.appender.R.File=C:/apache-tomcat-7.0.6/webapps/oa/log/oa.log
      3. oa的缓存配置文件
        WEB-INF\classes\cache.ccf
        jcs.auxiliary.DC.attributes.DiskPath=C:/apache-tomcat-7.0.6/webapps/oa/CacheTemp
    4. 重启一下tomcat
    5. 访问
      http://127.0.0.1:8080/oa
      admin
      11111

    第八章 需求管理
    1.什么是需求
    明确要什么做什么
    2. 需求工程在做什么
    ㈠需求开发:需求获取 需求分析 需求格式化 需求验证
    ㈡需求管理:需求分配 需求评审 需求基线 需求变更 需求跟踪
    3. ★需求变更
    a)为什么要变更:外因:市场,客户。内因:技术不足 缺陷 人员资源
    b)变更影响了什么:SRS HLD LLD SP UI ZI CODE
    c)怎么做变更的控制(需求变更控制目标):控制项目成本,控制项目风险
    d)需求变更的越早,影响范围越小,变更越晚,影响范围越大
        原则 方法
    变更控制的目标 降低变更引起的成本 防止随意的变更 通过评审和会议让用户或者企业负责人在变更上签字来确认变更
    尽量早的发生变更 多设计一些产品原形,由用户确认,
    尽量控制变更影响的范围 尽量不变更,如果变更尽量发生在后续版本
    尽量减少变更所引起的反工 当变更的需求稳定后在介入开发和测试
    降低变更引起的风险 高内聚,低耦合 代码内部干的是一件事,函数与函数之间关联尽量小,尽量使变更只影响到局部,而不影响到整个系统
    4.★需求的跟踪
    a)目的(为什么要需求变更跟踪)将和SRS有关的文档统一管理和关联起来,从而可以从任何一点找到其他文档的相关内容
    b)★★★★★输入、输出(RTM)
    ①开发的需求跟踪:SRS HLD LLD
    ②系统的需求跟踪:SRS ST计划 ST方案 ST用例
    ③集成的需求跟踪:HLD IT计划 ITf方案 IT用例
    ④单元的需求跟踪:LLD UT计划 UT方案 UT用例
    输出(RTM)Requirement Tvace Matrix需求跟踪矩阵
    c)每个阶段,跟踪的内容和变更的跟踪.
    SRS编号 SRS名称 系统测试项ID ST描述 ST子项ID ST子项描述 系统测试用例ID 系统测试用例描述
    HLD编号 HLD名称 集成测试项ID IT描述 IT子项ID IT子项描述 集成测试用例ID 集成测试用例描述
    LLD编号 LLD名称 单元测试项ID UT描述 UT子项ID UT子项描述 单元测试用例代码声明 单元测试用例代码描述
    需求跟踪矩阵的作用:
    开发RTM: 保证所有的需求都被设计实现了
    测试RTM: 保证所有的需求都被测试了
    保证可以通过需求,确定需求变更影响的范围,找到所有的成果物(HLD、LLD、系统测试计划…)
    需求的特点:
    只关心想要什么,不关心怎么去做
    需求工程

    不同阶段的需求变更的影响范围      
           
    需求阶段需求变更影响      
    需求规格说明书 系统测试计划    
    开发RTM 系统测试RTM    
           
    概要设计需求变更影响      
    需求规格说明书 概要设计    
    系统测试计划 系统测试方案 系统测试用例  
    集成测试计划      
    开发RTM 系统测试RTM 集成测试RTM  
           
    详细设计需求变更影响      
    需求规格说明书 概要设计 详细设计  
    系统测试计划 系统测试方案 系统测试用例  
    集成测试计划 集成测试方案 集成测试用例  
    单元测试计划      
    开发RTM 系统测试RTM 集成测试RTM 单元测试RTM
           
    编码以及后期测试阶段需求变更      
    需求规格说明书 概要设计 详细设计 编码
    系统测试计划 系统测试方案 系统测试用例  
    集成测试计划 集成测试方案 集成测试用例  
    单元测试计划 单元测试方案 单元测试用例  
    开发RTM 系统测试RTM 集成测试RTM 单元测试RTM
    补充知识:
    1代码编写原则:
    1).高内聚,低耦合
    2).可续性高
    3).查阅代码编写规范
    2在公司中出现以下问题如何解决
    (1)业务背景不同,导致项目延期
    明确需求文档的格式和标准,尽可能细化需求文档
    (2)需求变化频繁
    建立变更控制
    (3)需求相关的代码,用例找不到,找不全
    建立雪球跟踪

    CR(Changes requirement)需求变更
    CCB(Changes control board)变更控制委员会
    CMO(Configuration management officer)配置管理员
    PM 项目经理
    SWE 软件开发工程师
    STE 软件测试工程师
    QA 质量保证人员
    CI 基线

    —基线变更流程
    1)项目成员提交CR
    2)CMO将CR状态标识为已提交,并将CR提交给CCB进行签发
    3)CCB召开会议对CR进行评估
    4)未通过CMO将CR状态标识为已拒绝并返回提交人
    5)通过,CMO将CR状态标识为已接受,将CR与要修改的配置项发给项目组成员并开放CI的配置库权限
    6)项目组成员执行更改并进行验证
    7)CCB召开会议对修改进行审核,如果通过将CR状态标识为已验证,发给CMO,否则返回修改人
    8)CMO检查验证CR记录,收回配置权限,将CR状态标识为已关闭,返回提交人
    变更控制流程图

    第九章 缺陷管理
    缺陷相关概念
    1什么是缺陷:被测得产品部符合需求和用户使用的实际结果,不符合法律法规
    软件:满足某个功能的逻辑体
    系统:硬件、支撑软件、人员、数据等,综合起来满足某个业务需求的集合体
    2什么可以被定义为缺陷:(缺陷的分类)
    ①缺陷(defect)产品设计与需求设计部符合
    ②错误(error)没有定义的或者未知的错误信息
    ③故障(fault)由于一些原因导致产品失效,重新启动调整后可以恢复用户使用
    ④失效(failure)由于一些原因产品失效,无法自行恢复
    3缺陷提出的目的和意义
    对开发:更好发现缺陷现象,重现和定位缺陷,查找原因,保证所有的缺陷都被修复
    对测试:记录和保证BUG完整一致,回归保证所有的 BUG都验证
    提出问题,把问题交给开发去改
    跟踪缺陷,看是否已经修改
    测试报告,统计数据
    缺陷管理相关概念
    1.BUG管理的目的:
    ①.保证每个缺陷都被修改
    ②.保证每个缺陷都被回归
    ③.缺陷的完整性和一致性
    ④.避免纠纷,降低沟通成本
    2缺陷管理的意义:
    ①提高工作效率(BUG分类,状态负责人)
    ②记录唯一的缺陷信息,保证BUG完整一致(通过设置权限实现)
    ③记录中间环节,是BUG可追溯
    ④统计为测试报告提供数据
    3.参与缺陷管理的角色:
    测试工程师:发现和回归BUG
    测试经理:判断BUG的有效性
    开发经理:分配BUG
    开发工程师:修改BUG
    评审:解决矛盾
    4.缺陷的分类(属性)
    ①按模块分类:例如:登录模块,查询模块
    ②按严重级别分类:blocker阻碍的(不修改该BUG之后的开发测试无法执行)
    Critical崩溃(系统部能用)
    major严重的(严重影响功能使用流程)
    anormal一般的(不会影响主要的功能流程)
    minor轻微的(不会2影响业务流程也不影响使用)
    trvival 界面的
    suggestion建议(可用性,易用性,侧重用户体验)
    ③按优先级别分类:P1----P5(同意 BUG可能会变)
    BUG管理基本流程:

    BUG管理基本流程及相关角色
    1缺陷管理常见流程
    1)BUG回归时没有修改好:测试工程师REOPEN——开发工程师
    2)测试经理认为BUG无效,原因:不是BGU,对需求的理解误差,描述不清楚。BUG不全,重复
    测试工程师NEW----测试经理CAN OPEN-----REJECTED-----测试工程师CLOSED
    3)开发工程师拒绝修改BUG,原因:修复提高项目风险,理解分歧,技术难度大,修复成本高,修改范围广,优先级低
    测试工程师NEW----测试经理OPEN-----开发经理ASSIGNED-----开发工程师CANFIX------开发经理
    4)开发经理拒绝修改或分配BUG,原因:开发与测试已经不同意,偶发,项目风险高,关系进度成败,技术难度大,无法实现,修改成本高,难度大,影响大,影响进度优先级别低
    测试工程师NEW----测试经理OPEN----开发经理ASSIGNED----评审委员会CAN LATER----Y(LATER)-----N开发经理
    5)一般BUG生命周期
    测试工程师NEW----测试经理OPEN—开发经理ASSIGNED----开发工程师fixed----测试工程师CLOSED
    2缺陷状态:
    New新BUG单 Open确认 Reject拒绝 Assigned已分配 Fixed已修复 Reopen回归时未修改正确重新开放 Closed关闭 Later稍后再改 Postpone延迟 Abandon放弃 duplicate重复 verify验证
    测试人员: 无 → New Fixed → Reopen Fixed → Close
    测试组长: New → Open New → Abandon
    开发经理: Open → Reject Open → Postpone Open → Assign
    开发人员: Assign → Fixed
    项目经理: Reject → Passed Reject → Faild Faild → Abandon
    BUG单
    1.BUG单写作准则(5C):
    correct(准确)每个组成部分的描述准确,不会引起误解
    clear(清晰)每个组成部分的描述清晰,易于理解
    concise(简洁)只包含必不可少的信息,不包括任何多余的内容
    complete(完整) 包含复现改缺陷的完整步骤和其他本质信息
    consistent(一致)按照一致的格式书写全部缺陷报告

    2.BUG单模板

    注意:
    1一定可以重现的BUG可以不写“重复几次操作,出现几次,我认为,标题里不能写步骤,不能用主观的话描述,我在 。。。。的,不确定语句:某些好像,禁止使用”之后”,然后之类的语句”之类的话
    2需求规格说明书以外的错误可以当建议报告,不当BUG报告,开发可以改,也可以不改
    3若是随机出现的BUG,要写出操作几次,出现几次
    4若被测软件是跨平台软件,要写上在其他平台下无误
    5禁止写冗余的操作的步骤。常识性的步骤不用写进缺陷操作步骤
    6写明环境数据,如何选择数据,数据如何被破坏
    7一定要交代清楚测试书记,明确处理对那些数据进行操作

    第十章 测试需求分析
    概念:
    1.什么是需求分析:明确做什么,明确测什么,怎么测
    2.需求分析的目的(针对测试而言):
    1)对需求进行细化和分解,从而找到所有的测试点
    2)使从测试覆盖所有的需求(方法:先覆盖业务流,然后模块,关联 非功能)
    3)更细致的需求分析有利于提高测试质量(非软件质量)
    3.测试需求分析的特征
    1)所有的需求项要通过需求分析被核实
    2)测试需求分析应明确指出满足需求的前置条件和不满足需求的前置条件
    3)测试需求分析不涉及具体的测试数据,测试数据是在测试用例中产生
    ★如何做测试需求分析
    1.明确系统框架,有多少个业务流程
    2.明确业务流中有多少个功能测试点,细化分解业务流:
    a)明确每个功能模块的输入、输出、逻辑
    b)满足功能需求的条件和不满足功能需求的条件
    3.明确每个功能的独立处理流程关系
    4.明确功能之间的处理、联系
    5.明确非功能需求和隐性需求 如:安全性、性能、界面美观、易用性等…
    6.系统运行环境包括代码 硬件、软件、外设、数据库、网络
    罗老师的答案
    1考虑非功能性需求
    2挖掘规范需求形成规范需求流程
    3仔细阅读需求规格说明书(找出问题所在)形成问题列表
    4明确该系统的子系统,模块,子模块,功能,子功能(可以借助用例图的方法)
    5明确功能,子功能的流程和逻辑(可以使用活动图,状态图或流程图)
    6挖掘隐性功能,形成隐性功能规范需求
    7找出模块与模块,功能与与功能之间的 关系,确定组合测试需求
    ★UML统一建模语言(Unified Modeling Language)
    1.用例图:被称为参与者的外部用户所能观察到的系统功能模型图
    关系:
    1.关联
    2.泛化 指向父用例 如:
    3.依赖
    a)扩展 指向被扩展者
    b)包含 大的指向小的
    2.活动图:描述了一组顺序的或并发的活动
    状态 活动 开始 结束
    状态转移 循环 集 判断
    泳道
    3包含3个因素:参与者(Actor执行者),系统(Use Case用例),关系(Association关联,Dependency依赖,Generalize继承)

    第十一章 配置管理
    1.什么是配置管理
    a)对所有配置项进行标识,解决了在不同时间周期内,这些文档贯穿整个项目的生命周期并且对配置项进行权限的控制
    b)配置管理的目的(配置管理在解决生命事情,为什么要进行配置管理):解决了保证了软件产品的完整性,一致性,共享性、权限,变更可控、可追溯性
    c)配置管理管理了什么(配置项都包括哪些):配置项 版本 状态
    配置项:项目过程中每个阶段文档产物(SRS,HLD,LLD)代码,开发工具,测试工具,环境(应用服务器,数据库服务器)第三方软件、用户手册,方案、用例等等,
    对象的版本:XX.YY.ZZ.PP
    XX 主版本号——内核程序,核心代号
    YY 子版本号——主要功能、添加功能
    ZZ 维护版本号——增加次要功能,功能改进
    PP 补丁号——SP
    对象的状态状态: 未检查 入基线 冲突 锁
    注:BUG单也算配置项,但是一般单独管理 常用管理工具:QC、Jira、Bugfree、Bugzilla
    2.配置管理流程

    角色:
    项目经理(Project Manger PM)配置管理员(Configuration Mange Officer CMO)开发经理(Development Manger)测试经理 开发工程师 测试工程师
    质量保证人员(Quality Assurance)变更控制委员会(Change Control Board CCB)
    3.SVN实战
    开发可以生成branch 测试经理可以合并branch入trunk 评审可以使trunk入基线并打tag
    启动数据库服务器subversion: 用start svnserve.exe –d –r

    为每个文档建立trunk 然后再为单独的文档建立branch 测试后合并入trunk生成新版本的该文档
    优点:节省空间 缺点:tag版本不配套

    为所有文档建立trunk 每次建立branch都包括所有文档
    优点:所有文档版本相同 缺点:浪费空间
    配置管理工具SVN操作过程手册
    一、如何创建“project”项目版本库
    第一步:在D盘根目录下创建文件夹:“SVNROOT”如图所示:

    第二步:在“SVNROOT”文件夹内创建两个文件夹分别为:“project”,“project1”两个项目。

    第三步:对“project”创建版本库;
    A.选中“project”文件夹,点击右键;

    B.点击“在此创建版本库”。

    C.版本库创建成功后,“project”文件内自动生产以下文件;

    D.打开“conf”文件夹

    备注:对上述三个文件进行解释
    “authz”文件设置“project”操作人员的权限“read、write”;
    “passwd”文件设置操作的用户名和密码;
    “svnserve.conf”文件是“系统配置文件”
    E.对上述3个文件分别进行修改;
    修改“svnserve.conf”

    未修改
    修改处:

    修改方法:将“#”及空格,去掉即可。

    修改后
    修改后对文件另存,存储过程中格式选择“UTF-无BOM”然后点击“保存”。

    点击“保存后”在原文件夹内生产如图文件,这个是ultra edit 的备份文件

    修改完成。
    

    修改“authz”文件

    未修改前
    添加内容:
    p1_group_a=p1_a1
    p1_group_d=p1_d1,p1_d2
    p1_group_t=p1_t1

    [/]
    *=r
    root=rw

    [project:/]
    @p1_group_a=rw
    @p1_group_d=rw
    @p1_group_t=rw

    修改后
    修改后对文件另存,存储过程中格式选择“UTF-无BOM”然后点击“保存”。
    点击“保存后”在原文件夹内生产如图文件

    修改完成。
    修改“passwd”文件

    未修改前
    添加内容为:
    p1_a1=p1_a1
    p1_d1=p1_d1
    p1_d2=p1_d2
    p1_t1=p1_t1

    修改后对文件另存,存储过程中格式选择“UTF-无BOM”然后点击“保存”。
    点击“保存后”在原文件夹内生产如图文件

    修改完成。
    二、如何查看创建的“project”项目版本库
    通过“版本库浏览器”进行查看
    任意空白处点击“鼠标右键”

    点击:“版本库浏览器”

    输入:“svn://localhost/project”

    备注:“localhost”本机的IP地址;
    Project为项目工程名;
    点击“确定”

    问题:为何会出现“无法连接主机“localhost”:由于目前机器积极拒绝,无法连接”
    分析:“Subversion”服务器端“svnserve”服务未启动;

    如何启动“svnserve”服务,操作步骤:
    A.桌面“开始”----“运行”----输入“cmd”;

    B.用“DOS”命令打开此服务
    首先找到该文件:cd c:\Program Files\subversion 进入该目录

    查看该目录里面有哪些文件,用到命令“dir”

    3启动该项服务
    输入:start svnserve.exe –d –r D:/SVNROOT
    备注:D:/SVNROOT为客户端路径。

    运行后弹出一个窗口

    表示此服务已经运行。

    备注:在SVN运行过程中,此窗口 必须一直打开运行。

    服务运行后再次查看创建的版本库

    如图所示,创建版本库成功。
    三、在版本浏览器里面,创建文件,并进行检出
    如何在版本库里面创建文件
    A.在根目录下点击“右键”点击“创建文件夹”

    C.点击“创建文件夹”

    输入文件夹名“trunck”
    D.填写“创建日志”

    E.认证:输入用户名称和密码:
    用户名:p1_a1
    密码:p1_a1

    F.创建成功
    按照此方法在“trunck”根目录下创建“需求分析、RTM文档、测试报告、代码文档、概设文档、详设文档、用例文档”7个文件夹。

    在代码文档里面添加5个代码程序

    A.将5个代码程序 选中后直接拖入“代码文档”中;
    B.点击“复制项到此”

    添加成功。
    C.同样的方法,添加另外4个文件:min.c;mul.c;div.c;main.c;

    添加完成。
    检出添加的文件夹及文件
    A.A.在D盘根目录下建立文件夹“worksp”

    备注:此文件夹为空文件夹
    B.空白处点击“点击右键”

    C.点击“SVN检出(K)….”

    D.检出完成

    备注:检出成功
    E.打开“wroksp”文件夹,出现“trunck”文件夹

    F.打开“trunckt”文件

    G.打开“代码文档”

    备注:版本库浏览器里面的文件全部被检出,检出成功(如图所示)。

    四、如何对该项目入基线
    第一步:打开D盘文件夹“worksp”找到“trunck”文件夹。
    第二步:选中“trunck”文件夹点击“右键”
    选择“分支/标记”

    点击“确定”

    第三步:在D盘根目录中选择“worksp”文件夹,点击“右键”选择“提交更新”

    第四步:查看入基线是否成功,打开文件“worksp”

    第五步:通过“版本浏览器”进行查看;

    入基线成功。

    五、分支文件进行合并
    第一步:在D盘根目录下创建两个文件夹“group1”、“group2”

    第二步:用入基线的操作的方式,创建两个分支“branch1”、“branch2”

    “group1”文件夹

    “group2”文件夹

    第二步:
    “group1”–“branch1”中“代码文档”内添加文件:chengfang.c
    “group2”—“branch2”中“代码文档”内添加文件:kaifang.c
    以添加“chengfang.c”为例:
    A.添加文件“chengfang.c”

    B.选中“chengfang.c”文件点击“增加”

    C.点击“增加”后

    D.选中后“右键”点击“SVN提交”:

    备注:未提交前“代码文档”显示为:

    E.点击确定、添加成功

    添加“kaifang.c”
    同样的方法进行添加
    添加成功后:

    校验是否添加成功
    方法1:点击“worksp”进行更新,检查“branch1”、“branch2”中是否存在添加的两个文件;
    方法2: 版本浏览器查看

    将“chengfang.c”和“kaifang.c”进行合并;
    A.“合并”前“trunck”里面如图

    B.“合并”步骤:
    a.点击 “worksp”中的“trunck”点击“右键”

    将“kaifang.c”合并

    同样的方法对“branch1”中的“changfang.c”进行合并
    C.对“trunck”文件右键进行“提交”

    D.查看合并是否成功
    方法1:“worksp”----“trunck”----“代码文档”查看

    方法二:版本浏览器进行查看

    六、分支冲突的解决
    在D盘根目录下创建2个文件夹分别为“dev1”、“dev2”

    分别点击该文件夹的“右键”—“检出”—“trunck”

    针对“dev1”里面的“代码文档”

    进行修改添加“除数不能为0”;

    修改后“保存”

    “右键”点击“提交”

    针对“dev2”—“代码文档”里面

    进行修改添加“除数不能为0”;

    修改后“保存”

    “右键”进行提交“提交”

    提交失败,起冲突。
    解决冲突
    A.对“dev2”中的“div.c”文件进行更新

    B.更新完成后打开该文件:

    C.对该程序进行修改,完成后对该文件进行“提交”

    D.然后对“代码文档” —“右键”进行“更新”

    E.查看冲突是否解决
    方法1:“worksp”—“trunck”—“代码文档”—“dev.c”
    方法2: 版本浏览器进行查看。

    第十二章 系统测试
    概念:
    1什么事系统测试
    在对软件,硬件,外设,第三方软件,数据,人员,环境,代码,数据库考虑了的情况下,尽可能的模拟用户的使用环境下所做的测试
    另一答案:将已集成的软件系统,作为整个基于计算机系统的一个元素,与计算机硬件、外设、数据、支撑软件、人员等其他元素结合在一起,在实际运行环境下,对计算机系统进行的一系列测试活动
    注:代码称之为数据流,数据称之为信息流
    2系统测试在做什么工作
    验证系统是否满足需求规格说明书SRS
    验证是都满足隐性需求
    验证系统可以支付用户使用
    3系统测试的工作过程
    系统测试个阶段(计划,设计,实现,执行)的输入,输出,入口准则,出口准则
    4系统测试的对象:对整个产品(整个软件系统)
    5系统测试依据什么:SRS
    6系统:整体性、独立性、功能性、环境适应性
    7软件系统:包括支撑软件、数据、硬件、外设、人员及目标软件
    分类:
    功能测试:(Function testing中国 Feature testing国际)
    根据产品的功能是否满足用户需求
    工作内容 1业务,业务流是否正确实现
    2功能点是否正确实现
    3输入是否正确实现
    4输出是否正确实现
    测试工具:QTP(使用 B/S和C/S软件),LR(C/S软件),RIA TEST(针对Flash,Flex软件,用Flash编写),Selenium(firefox的插件), IBM Rational Robot
    工作原理:模拟用户操作
    性能测试:(Sercarity testing)
    定义:验证产品是否满足性能需求
    常用测试点:响应时间和资源性能(CPU,I/O,内存,信道,传输速度)
    例:1.先确认记事本的性能需求(加入50万行内正常)
    2.先输入50万行文本(造场景)
    3.监控所占用的系统资源
    4.报告,提出改进意见
    注:1内存和硬盘的区别
    内存临时存储,电脑关机,信息就消失,内存容量速度快
    硬盘是靠执行存储,断电后也能保存数据,硬盘存储量大,速度慢
    2信道与宽带有关,与访问人数有关
    3吞吐量,每个时间单元的处理数据
    4测试方法:压力测试(stres stesting)短时间
    负载测试(load testing)正常负荷下,长时间运行
    容量测试(volume testing)最大访问量
    5高级录制能找到每个按钮的控件名,低级录制只能找到鼠标移动到的位置
    6没有需求的性能测试无法进行
    测试工具:LR,Webload , Jmeter(开源软件,JAVA编写),Silk Performance
    工作原理:(B或者C端通过传送协议与S进行通信)
    录制协议,模拟多个永和传送协议(可以几句到以点,同时发给S也可以迭代发送给S)通过IP骑在变成不通的IP地址,突显多线程并发操作
    安全性测试:(Security Testing)

    定义:验证产品在系统的内部的安全保护机制和系统外对入侵的防护能力
    考虑内容:1内部包括身份验证,权限,数据的完整一致,数据的保密性(DB中有些数据加密保存)
    2外部,病毒木马,未授权攻击,传输数据安全(传输过程中密码加密,通过HTTP协议传输)
    注:病毒与木马的区别:病毒主要是针对计算机程序的破坏,木马主要用于盗取计算机内的相关机密
    测试工具:MBSA(Microsoft Basehne Sercurity Analyzer),IBM Rational Appscan,X-scan(某黑客组织开发)
    工作原理:监控服务器或客户端那些端口应该被禁止
    安装测试
    定义:产品的安装过程和结果进行测试
    工作内容:根据软件的测试特性列表,软件安装,配置文档,设计安装过程的测试用例
    包括安装前
    安装后
    安装过程中
    安装时异常终止包括:进程终止(操作系统未关闭)断电模式,断网
    测试对象:安装文件、安装系统、安装文档、配置项
    安装后检查:1件是否产生,是否正确
    2否按人工修改的方式安装
    3装日志是否记录正确
    4否有临时目录,垃圾文件,是否清理掉冷补丁(需要关闭系统的补丁) 热补丁(多数网站补丁属于热补丁)
    GUI测试(Graphical user interface)
    又称为用户界面测试,接口测试
    注:GUI测试无法脱离功能测试而独立存在,否则只能测试外观,布局,无法测试行为
    测试内容:对界面元素进行测试,
    验证产品界面是否与SRS设计一致(包括布局,外观,配色,元素行为,元素功能)
    验证界面元素的交互是否正确
    界面元素包括:整体界面,独立元素组合,独立元素
    思路:先找整体界面,再测独立元素组合和独立元素(据对用户的影响严重程度不同)
    测试步骤:对完整的界面进行测试 (包括布局,功能组合,页面展现的外观)
    组合元素(包括拆分,组合行为,独立行为,展现,外观)
    独立元素(包括外观,为)
    工具:所有做功能测试的工具都可以做GUI的测试
    可用性测试(Usability testing)
    又称易用性测试

    定义:验证用户管理和使用软件的容易程度
    工作内容:是否符合用户的实际习惯
    是否符合用户的特殊要求
    操作是否合理,简单,容易理解
    是否有明确的提示可以指导下一步操作
    异常性测试
    系统允许的操作
    健壮性测试,系统不允许的操作
    文档测试
    说明书,与易用性一起做
    备份测试
    主要测试备份策略
    目的:为了解决风险
    备份策略包括:本地备份,异地热备份(实时备份),本地异步备份,异地异步备份
    还原策略:(又称恢复策略)
    配置测试
    又称兼容性测试
    包括软件配置和硬件配置,验证系统都可以使用那些软硬件配置
    网络测试

    主要测试网络中的协议和数据
    工作内容:1网络协议测试(协议是否正确)
    2网络传输是否正确(不同网络是否正常)
    3网络结果测试

    第十三章 用例设计
    等价类
    定义:1、等价:如果多个输入在程序中处理方式(路径)相同,则认为这些输入是等价的,测试一个即可。(前提:测试不能穷举)
    2、输入:分为两类,有效输入(可以保存)、无效输入(不可保存)
    3、结合:有效等价类、无效等价类
    满足规则:只需寻找一个全部满足规则的(有效等价类)
    不满足规则:需分开,每条不满足条件的举出一个,方便定位错误(无效等价类)
    规则的几种情况:
    1 若规则是布尔式的
    有效、无效分别取一个 一个真,一个假
    2. 若规则是区
    有效的取一个 无效的,在小于区间的取一个,大于区间的取一个,空或零取一个
    3. 规则是集合:
    有效取一个 无效的在集合外取一个
    4. 规则是必须满足的一个条件:
    对无效,要细分无效(先试试其他有效等价类,再试试全部等价类全都不满足)
    例:邮箱注册,注册名只能以小写字母开头,设计无效等价类时,先试试其他有效等价类,如大写字母、数字等,再试试其他有效全部都不满足的情况
    有效等价类:程序规格说明有意义,合理的输入数据
    无效等价类:程序规格说明无意义,不合理的输入数据

    ASCII码
    7位 表示27=128个字符 每个字符存储占用1个字节
    分类:不可见字符
    控制字符:LF(换行)、CR(回车)
    通信字符
    NULL(空)
    可见字符
    空格
    数字
    字母(大写、小写)
    符号
    练习
    1.1年龄注册
    注册页面要求输入年龄,限制:1到150期间的正整数
    1、需求分析—找出全部的输入条件
    1)、正整数
    2)、[1,150]
    注意:分解的过程中,条件之间不用组合(完全独立),在设计有效用例输入的时候再考虑组合(一对多)
    2、绘制等价类表格
    条件 有效等价类 有效编号 无效等价类 无效编号
    正整数 >1 A01 数字 小数 B01
    负数 B02
    0 B03
    非数字 空(NULL) B04
    空格 B05
    字母 B06
    符号 B07
    [1,150] [1,150] A02 <1 B08
    >150 B09

    3、设计测试用例输入
    原则:有效输入—尽可能多去覆盖有效等价类 (一对多)
    无效输入—只能覆盖一个无效等价类 (一对一)
    目的:A、出现问题可以更好的去定位
    B、有可能第一个无效处理后,后续无效值被漏测
    输入序号 有效输入值 覆盖有效等价类 输入序号 无效输入值 覆盖无效等价类
    1 100 A01、A02 2 1.8 B01
    3 -9 B02
    4 0 B03、B08
    5 空(NULL) B04
    6 空格 B05
    7 W B06
    8 @ B07
    9 200 B09
    1.2.年龄注册
    某保险公司注册页面要求输入年龄,限制:1到150期间的正整数,其中:
    [1,10] 适用的费率 10%
    [11,50] 适用的费率 30%
    [51,150] 适用的费率 80%

    条件 有效等价类 有效编号 无效等价类 无效编号
    正整数 >1 A01 数字 小数 B01
    负数 B02
    0 B03
    非数字 空(NULL) B04
    空格 B05
    字母 B06
    符号 B07
    [1,150] [1,10] A02 <1 B08
    [11,50] A03 >150
    [51,150] A04

    输入序号 有效输入值 覆盖有效等价类 输入序号 无效输入值 覆盖无效等价类
    1 5 A01、A02 4 1.8 B01
    2 30 A01、A03 5 -9 B02
    3 100 A01、A04 6 0 B03、B08
    7 空(NULL) B04
    8 空格 B05
    9 W B06
    10 @ B07
    11 200 B09
    1.3.扩充
    考虑全角和半角问题(GB231)
    其中,半角占1个字节
    全角占2个字节

    字符集
    1、ASCII
    2、GB2312 -> GB18030 一个汉字占2个字节
    BIG5(繁体)
    3、ISO Unicode (UTF-16) -> UTF-8(变长存储) 一个汉字可能占3个字节
    条件 有效等价类 有效编号 无效等价类 无效编号
    正整数 >1(半角) A01

    			0	B03
    >1(全角)	A02	半角字符	空(NULL)	B04
    			空格	B05
    			字母	B06
    			符号	B07
    		全角字符	空格	B08
    			字母	B09
    			符号	B10
    		汉字
    

    (字符集) GB-2312 B11
    BIG-5 B12
    [1,150] [1,150] A03 <1 B13
    >150 B14

    分解粒度:根据功能的重要性决定(用例图----执行者角色,考虑用户的关注功能)
    根据成本、时间决定
    取消负数、小数的原因:
    1、前提:在编辑框内输入年龄,程序在接收编辑框的内容时,一般以字符(或字符串)的形式接收,再根据需要进行类型转换,如年龄,需要转换为整型
    2、小数中的“小数点”和负数中的“负号”都认为是符号,不允许保存
    边界值
    取值(5个):上点、内点、离点
    1、上点、内点取值与区间的开闭无关
    2、离点和上点互为有效
    作用:(有序、有范围)等价类的补充
    补充:考虑数据类型的边界
    如 int 类型 占4个字节 即 32bit 取值范围[-231, 231-1]
    上点:边界上的点
    内点:区间内的点
    离点:离边界值最近且与上点不属于同一等价类的点(对于小数,没有离点,不用取)
    规则的几种情况:

    1. 规则是区间:
      按上点、内点、离点取
    2. 规则是取值的个数:
      取最小、最大、中间个数
      3.规则是a~z序列:
      取a、z
      4.规则是集合:
      取一个集合内的,取一个集合外的
      (0,20)上点是0,20 离点是1,19 [0,20]上点是0,20 离点是-1,21
      若规则是下拉菜单:建议每个下拉值都选择一个
      2.1.年龄
      条件 有效等价类 有效边界值 有效编号 无效等价类 无效边界值 无效编号
      正整数 >=1 上点:1 A01 数字 小数 B01
      内点:100 A02 负数 B02
      离点:0 0 B03
      非数字 空(NULL) B04
      空格 B05
      大写字母 上点:A B06
      上点:Z
      内点:M
      符号 离点:@ B07
      离点:[
      [1,150] [1,150] 上点:1 A03 离点: 0 B08
      上点:150 A04 离点: 151 B09
      内点:100 A05
      有效用例输入:1 、100、150
      2.2.用户名注册

    1、测试需求分析
    1、内容:字母、数字、
    2、长度:[6,18]
    3、约束:字母开头
    字母或数字结尾
    不允许重名
    不允许使用保留字
    自动去除首尾空格
    2、等价类划分
    条件 有效等价类 有效编号 无效等价类 无效编号
    内容 字母 大写字母 A01 符号(除下划线) B01
    小写字母 A02 NULL(空) B02
    数字 [0,9] A03 空格 B03
    下划线 下划线 A04 GB2312 B04
    BIG5 B05
    长度 [6,18] [6,18] A05 <6 B06
    >18 B07
    约束 字母开头 大写字母开头 A06 数字开头 B08
    小写字母开头 A07 下划线开头 B09
    符号(非下划线)开头 B10
    汉字开头 B11
    字母或数字结尾 大写字母结尾 A08 下划线结尾 B12
    小写字母结尾 A09 符号(非下划线)结尾 B13
    数字结尾 A10 汉字结尾 B13
    不允许重名 不重名 A11 重名 B15
    去除首尾空格 A12
    不允许使用保留字 不使用 A13 使用保留字 B16
    3、用例设计
    序号 有效输入 有效边界值 序号 无效输入 无效边界值
    1 (当前系统无AAb_4)
    AAbb_4 A01、A02、A04、A03
    A05、A06、A10
    A11、A13 5 abc@a B01
    2 (当前系统无AAb_4)
    aAbcvb_A A01、A02、A04、A03
    A05、A07、A08
    A11、A13 …………
    3 (当前系统无AAb_4)
    aA555b_b A01、A02、A04、A03
    A05、A07、A09
    A11、A13 (当前系统存在aaf5bc)
    (空格)aaf5bc(空格) B15
    4 (当前系统无aaf5bc)
    (空格)aaf5bc(空格) A12 Administrator B16
    2.3.变量命名
    在某一版的编程语言中对变量名称规格作如下规定:变量名称是由字母开头的,后跟字母或者数字的任意组合构成,并且区分字母大小写。编译器能够区分的有效字符数为8个,并且变量名称必须先声明,后使用,在同一声明语句中,变量名称至少必须有一个。
    1、测试需求分析
    内容:字母、数字
    长度:[1,8]
    约束:字母开头
    大小写敏感
    先声明后使用
    变量的个数[1,?] (需要和需求人员沟通)
    在同一作用域中不允许重名
    不能使用关键字
    2、等价类划分
    3、确定边界值
    4、设计用例输入
    2.4.进销存价格

    代码实现:
    if (isNumberString(document.form2.tichen.value,“1234567890.”)!=1)
    {
    alert(“价格只能为数字!”);
    return false;
    }

    根据代码的实现,价格编辑框只接受键盘的“数字键”和“小数点”共11个字符

    等价类
    条件 有效等价类 无效等价类
    内容 数字 字母
    字母、符号处理方式(路径)相同,认为都是等价的
    小数点
    约束 小数点最多有1个 >1个小数点

    2.5.Windows文件命名
    目录 目录长度 文件最大长度 目录+文件最大长度
    C:\ 3 255个 = 258
    C:\ABCD 7 251个 =258
    C:\ABCD\AAAA 12 246个 =258

    根目录: 255
    非根目录: 254 (文件夹最小长度为1)

    1、测试需求分析
    内容:非“/😗?"<>|”的字符
    长度: 根目录: 255
    非根目录: 254 (文件夹最小长度为1)
    约束:同目录下不能重名(去除首尾空格)
    不同扩展名使用不同的图标
    不能为空
    总结
    1、适用范围:针对程序的输入
    2、使用步骤
    1)、测试需求分析,找出全部条件(显示、隐士)(条件之间不能交叉或者组合,到设计用例的时候再考虑多条件的组合)
    2)、划分等价类
    划分依据:相同的处理方式(路径)
    分类:有效等价类、无效等价类
    3)、使用边界值补充(有序、有范围的)等价类
    内容:上点、离点、内点
    要求:上点、内点与区间开闭无关,离点和上点互为有效
    分类:将边界值分为有效边界和无效边界,填入等价类表格
    4)、对每个等价类进行唯一的编号
    5)、设计用例的输入
    原则:有效等价类,一对多
    无效等价类,一对一
    3、优点:对输入的考虑充分
    4、缺点:如果不清楚系统的实现方式,造成大量的冗余用例(黑盒方法共有)
    对于多输入的组合不太适宜
    规则的几种情况:
    1 .若规则是布尔式的
    有效、无效分别取一个 一个真,一个假
    2. 若规则是区间
    有效的取一个 无效的,在小于区间的取一个,大于区间的取一个,空或零取一个
    3. 规则是集合:
    有效取一个 无效的在集合外取一个
    4. 规则是必须满足的一个条件:
    对无效,要细分无效(先试试其他有效等价类,再试试全部等价类全都不满足)
    例:邮箱注册,注册名只能以小写字母开头,设计无效等价类时,先试试其他有效等价类,如大写字母、数字等,再试试其他有效全部都不满足的情况
    有效等价类:程序规格说明有意义,合理的输入数据
    无效等价类:程序规格说明无意义,不合理的输入数据
    边界值
    上点:边界上的点
    内点:区间内的点
    离点:离边界值最近且与上点不属于同一等价类的点(对于小数,没有离点,不用取)
    规则的几种情况:

    1. 规则是区间:
      按上点、内点、离点取
    2. 规则是取值的个数:
      取最小、最大、中间个数
      3.规则是a~z序列:
      取a、z
      4.规则是集合:
      取一个集合内的,取一个集合外的
      (0,20)上点是0,20 离点是1,19 [0,20]上点是0,20 离点是-1,21
      若规则是下拉菜单:建议每个下拉值都选择一个

    第十四章 系统测试执行
    测试用例
    编号组成: 项目名—测试阶段—需求—用例
    测试环境搭建文档:
    1.为日后回归测试等搭建环境做指导
    2.刚开始写完后,评审,防止日后因环境搭建而引起测试时的问题
    3.可为日后上线的文档做参考
    4.可以做自动化步骤的参考
    5.项目留存
    用例执行:
    1.严格按照用例步骤,执行用例
    2.发现结果与预期结果不一致:
    a)重复步骤,重现发现的问题
    b)找同类型的数据,重新执行用例
    c)定位发现问题的步骤 (每执行一步都要做记录)
    3.提交BUG (先确认不是重复BUG)
    填BUG报告:
    (一)若开发返回”不可重现的BUG”,可能原因:
    1.开发步骤不对
    2.测试环境和开发环境不同
    3.出现时有概率的,或者其他原因导致,不是每次重现 (环境、概率)
    (二)合并BUG的原则
    1)同样的原因产生的BUG可以合并
    2)与开发沟通后如果修改一处就可以修改一些BUG可以合并
    3)当BUG之间有制约关系,BUG可以合并
    (三)补充 为什么.TXT文件中报春”联通”重新打开后会有乱码
    文件保存格式默认为ASCII码,但恰好”联通”是以EF开头的,保存时误认为UFT-8格式,导致重新打开时歘先乱码
    (四)书写测试用例应该注意一下几点
    1)测试输入数据必须是唯一的,并且明确的
    2)步骤要完整,按照步骤可以测试测试点和预期结果,建议每个步骤后面都有预期结果,步骤要编号
    3)结果要和需求规格说明书完全一致,如果SRS没有明确结果,需要产品人员补充和定义
    4)无效等价类不能合并

    第十五章 QC(Quality Center)
    QC (Quality Control)质量控制
    QC(前身TD Test Director与QTP、LoadRunner同是MI公司开发)
    QC是B/S架构的
    QC 9.0支持JBoss、IIS两款服务器支持SQL Server和Oracle两个数据库
    QC默认端口是8080 数据库连接端口是1433
    Quality Center QC前台
    Site Administration QC后台
    Add-Ins Page QC插件
    Qcsiteadmin_db 是保存后台数据的 而前台中项目的数据,是单独保存在另一个数据库中
    QC预置5个用户组,只能查看,不能修改权限
    Developer Project Manger QATester TDAdmin Viewer
    外部测试人员,只有Defects Module权限,只执行测试,报BUG
    组内测试人员,有QC所有模块权限
    QC后台:
    Site Project 设置项目
    Create Domain 建立区域
    Create Project 建立项目
    Create an empty Project 建立一个空项目
    Create project by copying data from an existing project
    从已有项目拷贝创建一个新项目

    Create a project by importing data from an exported Quality Center project file
    从一个已有项目的导出文件创建一个新项目

    QC前台:
    TOOLS下的Customize…选项
    Customize Project Entities 设置输入项 可添加新选项
    Customize Project Lists 项目列表 可设置下拉列表中的项
    Requirements 需求模块
    菜单栏下Requirements菜单项
    Covert to Tests 将需求转换成测试用例 用于比较系统的测试
    Generate Test…将需求生成测试执行 用于临时的测试
    菜单栏中View菜单项
    Expend 扩展,全部展开
    Collapse 收起
    Numeration 排序
    Full Coverage 完全覆盖(考虑测试覆盖率)
    Requirements Tree 需求树结构 用于项目经理等管理者
    Requirements Grid 需求表结构 用于浏览者(可用Favorites)
    Requirements Coverage 需求覆盖率 用于测试人员
    Coverage Analysis 需求分析视图 用于测试管理者
    菜单栏中Analysis菜单项
    Reports选项下
    Standard Requirements Report 标准需求表 用于评审
    Tabular Requirements 概要表 用于产品经理
    Requirements with Coverage Tests 带覆盖率的表 用于测试经理
    Requirements with Coverage Tests and Steps 带步骤的 用于测试用例评审
    Requirements with Linked Defects 带缺陷的 用于生成测试报告
    Graphs选项下
    Progress 给项目经理
    Requirements Coverage 给测试经理
    Trend 给BOSS
    工具条中Set filter/Sort 过滤器 用*做通配符
    View Order 结果排序 同级才能排序
    双击一条需求 Requirement Details 需求详细信息
    Coverage 覆盖率
    Linked Defects 连接缺陷
    需求的状态:not covered not run not complete passed failed
    Test Plan 测试用例模块
    点击一条测试用例,右侧在Design Steps标签页下
    Call to Test 调用测试用例 只能调用到一个参数 可用于回归测试
    Generate Script 生成脚本
    Insert Parameter 添加参数
    Renumber Steps 重新写序号 (不影响步骤的内容)
    菜单栏中Tests菜单项
    Flag for Follow up 标记一个执行
    Mark as Template Test 标记成为模板用例
    菜单栏下View菜单项
    Trace Changes 变更跟踪警告
    Analysis中所有报表均是测试工程师用
    Test Lab 测试执行模块
    需要添加一个集,用例才能被添加到集
    菜单栏下Test Sets菜单项
    Reset Test Set 重置测试集 用于测试回归,所有状态均为No Run
    Purge Runs 清理执行的状态 清理执行的状态,上个是全部,此为条件式
    状态优先级:
    Failed > Not Complete或N/A或No Run > Passed
    Trace Changes
    在Test Plan中,跟踪的是需求Requirements
    在Test Lab中,跟踪的是缺陷Defects状态时”Fixed”时
    在Defects中,跟踪的是Test Lab成功运行时
    其中红色! 表示未看过 灰色表示已看过
    QC中,Defects只能连接自己,不能连接其他而 Requirements、Test Plan、Test Lab可以连接其他任何一个,不能连接自己,所以,Defects单独划出一个模块
    权限设置:
    需求人员:写需求
    测试人员:用例、BUG、执行用例
    开发人员:改BUG
    项目经理:所有权限
    当产生纠纷时,在设置中增加一个状态,在Defects中的Description选项中的Comments下添加分歧的描述、原因等等,分配给解决纠纷的人,只有被分配的人可以修改状态,其他人不能更改

    变更控制:
    能否变更 用Reviewed(评审)字段,将之设置成必填项,默认值是Not Reviewed
    设置权限中,不给需求人员修改Reviewed的权限

    第十六章 PYTHON
    Python 是面向过程、面向对象,跨平台的高级脚本语言。 是解释性语言,执行源代码,提供丰富的库,支持多线,多进程,网络编程,可以支持C/C++的代码,可以测试C/C++和Python开发的单元产品,Python越来越多的成为开源测试自动化工具的首选语言
    Python的安装
    1.安装之前,先退出杀毒软件
    2.安装 缺省安装
    3.完毕后,配置系统环境变量 path (把解释器pathon.exe所在目录加入path)
    Python的集成环境:
    1.Python文件夹下IDLE
    2.新建文件 New Window
    3.保存(用Save As…后缀.py)
    4.运行方式:
    a)Run Module (若提示”Socket Error”是因为杀毒软件引起的错误)
    b)命令行方式 在DOS下,进入文件目录,输入命令python hello.py
    python可以用#来做注释
    输入中文时在前面加#coding:gbk
    在GUI界面下 若不加保存时会报警告

    补充知识:
    DOS命令:
    切换盘符 盘符名:
    进入文件夹 cd 文件夹名\文件夹名…
    退到根目录 cd
    退出一层目录 cd…
    数据类型:
    Str字符型字符串(可以用单引号、双引号) Int 整形 long长整形 float浮点型 bool(布尔型):True真/False假(首字母大写,不用加引号)
    运算符:
    算术运算符: + — * /
    逻辑运算符: and or not
    比较运算符: 相等 = = 不等于 != < > <= >=
    缩进:
    在行首用空格或者Tab 缩进相当于C语言中的{ } 表示对应一段完整的逻辑
    一般用TAB 因为一段代码中不能同时使用两种缩进方式,默认是TAB
    控制语句:
    IF条件
    1 if (条件1):
    处理语句1
    处理语句2
    处理语句3

    语法解释:满足条件1执行语句1,2,3,不满足条件1执行语句
    2 if (条件1):
    处理语句1
    处理语句2
    elif
    处理语句3

    语法解释:满足条件1执行语句1,2不满足条件1,执行语句3
    3 (1) if (条件1): (2)if (条件1):
    处理语句1 处理语句1
    处理语句2 处理语句2
    elif (条件2): else:
    处理语句3 if(条件2):
    处理语句4 处理语句3
    … 处理语句4
    else: else:
    处理语句5 处理语句5
    处理语句6 处理语句6
    … 。。。。
    处理语句7 处理语句7

    语法解释:满足条件1执行语句1,2,7,不满足条件1,满足条件2,执行语句3,4,7,不满足条件1,2,执行语句5,6,7
    注:条件中可以用逻辑运算符 比较运算符
    条件后面要加“;”
    WHILE循环
    while (条件):
    处理语句1
    处理语句2

    处理语句3

    语法解释:条件为真(满足条件)执行语句1,2,在判断条件,还满足条件的话,还执行1,2直到不满足条件,执行语句3

    while (条件1):
    if (条件2):
    处理语句1
    处理语句2

    elif (条件3):
    处理语句3
    处理语句4

    else:
    处理语句5
    处理语句6

    处理语句7

    语法解释:条件1为真,判断条件2,条件2为真,执行语句1,2,在判断条件1,当条件1 为假,执行7
    条件1为真,判断条件2,条件2为假,,在判断条件3,当条件3为真,执行语句3,4,判断条件1,当条件1为假,执行语句7
    条件1为真,判断条件2,条件2为假,,在判断条件3,当条件3为假,执行语句5,6,判断条件1,当条件1为假,执行语句7
    FOR循环
    for 变量 in 集合函数: 注:range是一个集合函数
    处理语句1
    处理语句2

    处理语句3

    语法解释:变量值在这个集合函数之内执行语句1,2,循环执行1,2,直到变量值不在集合函数范围内,执行语句3
    例题:for I in range(1,7); #循环了6次
    print”sjsddsj”
    BREAK \ CONTINUE
    break \ continue 只能用在循环语句中(for ,while)。
    break:跳出循环,执行循环体外的第一条语句,无论循环体内还有多少语句
    continue:跳过循环体内的语句去执行循环条件的条件判断

    	while (条件1):
    		  处理语句1
            处理语句2
    			if (条件2):
        			处理语句3
       			break
    		 	else:
        			处理语句4
        			continue
    

    处理语句5
    处理语句6

    语法解释:条件1为真,条件2为真,执行1,2,3,6
    条件1为真,条件2为假,执行语句1,2,4,1,2,4执行到条件1为假后,执行语句6
    条件1为假,执行语句6
    这个程序没有执行过语句5

    程序举例:
    a=1
    i=1
    while (a<=10):
    a=a+1
    if (a==10):
    break
    else:
    continue
    i=i+1
    print a
    print i
    运行结果:a=10 i=1
    函数
    1 type () 查看变量的数据类型

    2 Raw-input (“提示信息”) 输入函数(从界面输入的均是字符型)

    3 isdigit() 判断变量a是否是纯数字

    4 int() 强制转换类型

    5 Print输出函数
    6 range 集合函数
    Range(起始值,结束值,步长)求从开始值到小于结束值,并且以步长为某一个值的等差数列

    定义:
    Def 函数名(形参列表):
    函数体语句
    Return 返回值 #若没有返回值,不加这句
    程序举例:Def fun(a,b,c):
    Print a,b,c
    Return a+b+c
    调用:
    有返回值
    变量名=函数名(实参列表)
    例:M=fun(‘3’,’4’,’asd’)
    无返回值
    函数名(实参列表)

    第十七章 单元测试
    单元测试概念:
    1什么是单元测试:对软件的基本组成单元所作的测试(面向过程(一组函数或一个函数)面向对象(类中得方法))
    2语句:真正的处理语句才算是语句(判断框中的语句不算)。
    判定:流程图中的菱形框;
    判定数:流程图中菱形框的个数;
    分支:判定的真或假;
    分支数:判定数2;
    条件:判定中关系(比较)运算符;
    条件数:判定中关系(比较)运算符的个数;
    条件结果数:条件数
    2(每个条件有真、假两个取值)。
    3单元测试的目的:与LLD是否符合;与SRS是否符合;编程是否存在问题。
    4关注重点包括:单元接口(类型,顺序,长度,参数个数);局部数据结构;独立路径;边界值;出错处理。
    5单元测试环境包括:被测单元、驱动单元(把被测单元驱动起来,完成被测单元的调用)、桩单元(被测单元的调用的替代品,替代输入与输出),测试用例(测试数据)。
    6驱动单元的四个职责1)接收侧四数据包含测试用例输入好预期输出
    2)吧测试用例输入传送给要测试的单元
    3)将被测单元的实际输出和预期输出进行比较,得到测试结果
    4)将测试结果输出到指定位置
    7辅助技术:评估,框架应用
    8桩单元:通过一组输入和输出模拟被替代单元的行为
    单元测试静态测试:
    1.从详设的评审开始介入
    2.详设评审、编码完成后,作代码的静态检查
    (可以用专门的检查工具 如:PC_lint)
    3.代码的交叉走读 (要制定标准,标准越清晰,任务被分配者越有目标,工作越细)
    单元测试动态测试:
    1写用例之前,先确定覆盖率标准
    2写用例
    3搭建测试环境
    4执行
    5测试报告
    测试评价准则:
    系统测试评价准则:需求覆盖率(=至少被用例覆盖一次的需求项数/测试需求分析列表中的需求项数)。
    单元测试评价准则:逻辑覆盖率(=item至少被评估一次的次数/item总数)【这是一个总公式】。
    逻辑覆盖率
    逻辑覆盖率包括以下几种:语句覆盖率、分支覆盖率、条件覆盖率、分支条件覆盖率、路径覆盖率。【掌握计算公式、每种覆盖率的问题】
    1语句覆盖率=语句至少被执行一次的次数/语句总数。
    问题:有可能语句覆盖率为100%,有可能发现逻辑运算符的问题
    2判定覆盖率=每个分支取值至少被执行一次的次数/分支总数(判定数2)
    问题:当分支覆盖率为100%时,可能不能发现关系运算符的缺陷
    3条件覆盖率=每个条件取值至少被执行一次的次数/条件结果总数(条件数
    2)
    问题:条件覆盖率为100%时,有可能某个分支没有执行到,若该分支有缺陷,可能会遗漏。
    4分支覆盖率=(每个分支至少被执行一次的次数+每个条件取值至少被执行一次的次数)/(分支总数+条件结果数)
    问题:分支条件覆盖率为100%时,有可能漏路径,若该路径上有缺陷,可能遗漏
    5路径覆盖率=每个路径至少被执行一次的次数/路径总数
    问题:路径覆盖率为100%时,条件覆盖率可能不为100%
    注:独立路径覆盖,若路径覆盖率100%则条件、语句、分支覆盖率均100%
    路径一定要从始点到终点
    可以用软件来计算路径覆盖率 如BullseyeCoverage

    单元测试策略
    孤立、自顶向下、自底向下。
    ⑴ 孤立测试
    缺点:需要大量的桩和驱动
    优点:改方法是最简单最容易操作的 ,可以达到高的结构覆盖率,该方法是纯粹的单元测试
    方法:不考虑每个模块与其他模块之间的关系,为每个模块设计桩模块和驱动模块,每个模块进行独立的单元测试
    【例1】
    对象 驱动 桩
    A Driver-A Stub-B
    B Driver-B Stub-C
    C Driver-C X

    【例2】
    对象 驱动 桩
    A Driver-A Stub-B,C
    B Driver-B Stub-D
    C Driver-C Stub-E
    D Driver-D X
    E Driver-E X

    ⑵自顶向下的单元测试策略
    方法:先对最顶层的单元进行测试,把顶层所调用的单元做成桩模块。其次,对第二层进行测试,使用上面已测试的单元做驱动模块。如此类推直到测试完所有模块。
    优点:可以节省驱动函数的开发工作量,测试效率高。
    缺点:随着被测单元一个一个被加入,测试过程将变得越来越复杂,并且开发和维护的成本将增加。
    【例1】
    对象 驱动 桩
    A Driver-A Stub-B
    B Driver-A Stub-C
    C Driver-A Stub-D
    D Driver-A X

    【例2】
    对象 驱动 桩
    A Driver-A Stub-B,C,D
    B Driver-A Stub-E,C,D
    C Driver-A Stub-E,D
    D Driver-A Stub-F,E
    E Driver-A Stub-F
    F Driver-A X

    【例3】
    对象 驱动 桩
    A Driver-A Stub-B,C
    B Driver-A Stub-C,D
    C Driver-A Stub-E,D
    D Driver-A Stub-F,E
    E Driver-A Stub-F
    F Driver-A Stub-G,H
    G Driver-A Stub-H
    H Driver-A X

    a. 看被测函数下边有没有调用,如果被调用则打一个桩
    b. 假如曾经被测过的函数的时候,这些函数下边是否有调用,而那些调用是否也被测过,如果没有被测过,也需要打桩
    c. 只要驱动单元驱动的时候,所依赖的函数要run的时候,相关的调用都需要加入
    ⑶自底向上的单元测试方法
    方法:先对模块调用层次图上最低层的模块进行单元测试,模拟调用该模块的模块做驱动模块。然后再对上面一层做单元测试,用下面已被测试过的模块做桩模块。以此类推,直到测试完所有的模块
    优点:可以节省桩函数的开发工作量,测试效率较高。
    缺点:不是纯粹的单元测试,底层函数的测试质量对上层函数的测试将产生很大的影响。
    对象 驱动 桩
    E Driver-E X
    C Driver-C X
    F Driver-F X
    B Driver-B X
    D Driver-D X
    A Driver-A X

    如果桩难写,采用自底向上;如果驱动难写,采用自顶向下。
    打桩原则:1被测函数有没有调动,若有调动则打桩
    2加入曾经被测过的函数,这些函数是否有调动,这些调动是否被测过,都没有则打桩
    【例子】
    自底向下
    对象 驱动 桩
    F Driver-F X
    I Driver-I X
    J Driver-J X
    G Driver-G X
    H Driver-H X
    E Driver-E X
    C Driver-C X
    D Driver-D X
    B Driver-B X
    A Driver-A X
    自顶向下
    对象 驱动 桩
    A Driver-A Stub-B
    B Driver-A Stub-C,D
    C Driver-A Stub-E,D
    D Driver-A Stub-E
    E Driver-A Stub-F,G,H
    F Driver-A Stub-G,H
    G Driver-A Stub-I,H
    H Driver-A Stub-I,J
    I Driver-A Stub-J
    J Driver-A

    单元测试用例设计(基本路径覆盖法)★ (面试)
    步骤:(所有的循环仅执行一次)
    1)画流程图
    2)根据流程图画出流图
    3)根据流图找出独立路径
    4)根据独立路径设计用例
    结点:表示一个或者多个无分支的语句
    边:表示处理流向
    分支:判定真假的语句
    区域:由结点和边构成的,域的个数等于独立路径的条数
    注:路径一定要从始点到终点
    复杂性计算 :V(G)=E-N+2
    V ( G ) =P+1
    E:边,
    N:结点数量,
    P:条件结点的数量,
    V最好不要大于10
    程序控制流图
    1顺序结构
    2if结构
    3while循环结构
    4until重复结构
    5Case分支结构(swith结构)

    单元测试执行
    单元测试执行的过程:
    单元计划—单元设计—单元测试用例脚本----单元测试执行
    1.搭建环境 引入googletest
    a)将googletest的include和lib两个文件夹复制到VS的项目文件夹下
    b)在Visual Studio中选择项目,属性,将include和lib导入
    c)引入googletest头文件
    #include “gtest/gtest.h”
    d)在main函数中初始化GTEST
    testing::InitGoogleTest(&argc,argv); 初始化google test
    RUN_ALL_TESTS(); 运行当前工程所有test
    2.加入被测代码
    将被测代码头文件引入
    3.新建测试代码
    新建一个文件,编写测试代码
    调用googletest宏
    TEST(测试套名字,测试用例名字)
    {
    ………
    }
    用断言函数(ASSERT、EXPECT)来判断预期结果与实际结果
    ASSERT:致命断言,若前一个执行失败,则不继续执行
    EXPECT:一般断言,前一个执行失败,继续执行
    系统测试中发现不了在单元测试中比较容易发现的问题,如内存泄露、算法缺陷
    补充:
    C语言中,用malloc申请内存,free释放内存
    C++中,用new申请内存,delete释放内存
    TDD(测试驱动开发)
    内存中的堆、栈
    编译器自动分配的内存是在栈中,栈会自动维护、清理
    手工分配的内存是在堆中,堆不会自动清理,需要手工释放,容易忽略造成内存泄露

    单元测试框架

    1. 框架是一组为重用而设计的方法,单元测试框架包括junit\cppunit\phpunit\c#unit\pyunit
    2. pyunit是 d:\py26\unittest.py
    3. 单元测试框架概念
      测试固件(test fixture)
      一组测试函数执行前或执行后要执行的语句。
      例如,void insertDB(chr sql[30])
      {
      //执行insert语句
      }
      若现在测试该函数,实际上每次测试之前都要建立数据库连接,测试完成之后都要断开断开数据库连接。
      建立连接和断开连接称之为测试固件。测试固件在框架如何体现:
      setUP(…) —表示测试函数执行之前要执行的语句,比如建立连接
      teardown(…) —表示测试函数执行之后要执行的语句,比如断开连接
      有没有测试固件,取决于被测函数在测试执行需要否

    测试用例
    主要以测试函数为主,什么是测试函数?就是对被测单元完成测试的函数,类似于原始的驱动单元。
    在框架中一般来说要继承单元测试框架的TestCase。

    测试套件
    装配测试用例的车间。
    在框架中一般来说使用TestSuite的addTest方法来进行装配,装配的就是测试用例,实际上装配的用例中的测试函数。

    测试运行器
    加载测试套件,对套件中装配的测试函数的运行。
    在框架中一般来说使用TestRunner中的run方法来进行运行TestSuite。
    4. pyunit框架中有:
    unittest.py
    测试固件 -----》TestCase类中有setUp和tearDown有这两个方法
    测试用例 -----》TestCase类,在该类中写测试函数的方法 ????
    测试套件 -----》通过TestSuite类中的addTest方法将测试用例中的测试函数加载
    测试运行器-----》通过TextTestRunner中的run方法将测试套件运行起来。
    5. 实例:
    见例子
    6. 使用pyunit单元测试步骤:
    1)import unittest
    2)继承unittest.TestCase构建自己的测试类
    3)在自己构建的测试类中,若对于测试函数有测试固件,则在setUp和tearDown中进行代码编写。否则跳到1)

    第十八章 集成测试
    1什么是集成测试
    依据概要设计说明书,对软件组成的所有函数模块和子模块进行的测试
    2目的:检查软件是否符合概要设计说明书,是否符合需求
    3关注重点
    全局变量 组合功能(集成后的问题)单元接口(1穿越模块的数据是否会丢失,即做输入输出的的形参定义是否正确2全局数据结构是否被异常修改)
    4集成测试环境
    集成后的功能:单个功能都没有问题,组合在一起时否有问题,单个功能之间是否相互影响
    进程:是一个程序在计算机上的一次执行活动,当运行了一个程序就启动了一个进程,进程是操作系统进行资源分配的单位
    线程:是进行中的一个片段
    集成测试没有联系,不存在集成,联系时全局变量,全局变量可能是内存的一片区域,也可能是同一份文件,数组,堆栈,字段,记录,变量,队列,集成也需要考虑到性能匹配问题,网络集成
    5集成测试的依据:LLD
    6集成测试的对象:接口
    7集成测试策略:大爆炸集成测试,自顶向下,自底向上,三明治集成,基于集成,分层集成,基于功能集成,基于消息集成,基于进度的集成,基于风险的集成
    1)大爆炸集成测试
    方法:对每个模块分别进行单元测试,在吧所有单元组装在一起测试
    优点:从未投入模块间,子系统间角度节省时间,减少桩和驱动的开发,节省成本,方法简单

    使用范围:小型项目,大型项目已经稳定,只有少量修改
    2)自顶向下集成测试

    深度优先:在系树的根优先,根—左—右
    广度优先:从上到下,从左到右,一层一层的
    注:调用没有加入过的都需要打桩
    优点:减少驱动的开发,测试充分(一个模块一个模块的加入)定位问题容易,效率有所下降,最容易让高层人物建立信心
    缺点:打桩开发成本较高,效率有所下降,下层容易测试不充分
    适用范围:上层稳定,下层相对不稳定的系统
    3)自底向上集成测试
    优点:桩少,定位问题容易,小测试成分,下层测试充分,问题容易暴漏
    缺点:驱动开发成本高,顶层测试不充分
    适用范围:下层稳定,上层变化较多
    4)三明治集成测试方法

    优点:桩和驱动都减少,测试灵活,问题好隔离,好定位
    缺点:测试中间层可能不充分
    适用范围:大项目
    注:采用哪种方法根据系统本身确定
    8集成测试用例可借用单元测试和系统测试用例设计技术进行用例设计
    9接口覆盖率:每个接口至少被覆盖一次的次数/系统中所有的接口
    10集成测试的层次:子系统间集成测试,模块内集成测试,子系统内集成测试,还要考虑模块间集成测试

    11集成测试过程:计划(输入HLD测试计划,输出IT计划)——设计(输出IT方案)——实施(输出IT用例,脚本——执行(输出集成测试报告,缺陷报告)

    第一阶段总结
    基础课程:测试基础、测试过程、测试方法、软件质量
    开发基础:SQL、C语言
    需求分析、需求管理(变更控制、需求跟踪)
    环境搭建
    配置管理、QC
    缺陷管理
    系统测试、集成测试、单元测试、python
    要做好测试还应该学习DB 网络知识 数据结构 算法 语言(C JAVA PYTHON)OS
    所有课程全部基于测试目的:尽量多、尽量的发现BUG,并预防BUG
    在做测试之前,先考虑可以套用哪些软件质量特性,再根据特性考虑测试点

    Test platform
    管理工具:QC ,RQM
    执行工具:QTP,LR ,WinRunner,BRAT
    数据库:SQL,Oracle,MySQL
    配置管理工具:SVN,CVS,VSS,CC,DB2
    服务器:Tomcat,IIS,JBOSS
    2. 系统测试流程:
    测试流程:需求阶段(产出测试计划):1)来自客户(用户需求)特点:用户自身根据自己的需要提出来,不考虑技术的实现能力,功能和具体细节的实现不2)来自研发(开发文档):特点,进一步明确客户的需求3)来自标准协议(手机网络协议:CSM,CDMA,WCDMA,TD_WCDMA,EDEE)
    需求分析(产出测试方案):分析测试项。罗列功能模块和功能点,产出测试项1)从质量特性的角度对测试项进行分析,包括6大特性,27个子特性2)从功能交互的角度对测试项进行分析,功能是否存在交合,交互过程中有什么影响3)按用户场景进行测试分析
    设计阶段(产出测试用例)设计方法:等价类、边界值、状态迁移图、因果图等
    执行阶段(产出测试报告)提交缺陷报告: 测试结束,研发人员根据提交的bug进行修改,之后release 1.1(新版本),进行回归测试(REG)。
    补充:
    敏捷开发模式 scrum 短小、快。 没有需求、不便于管理。
    User story 功能点。
    3. 与bug相关
    Bug的其他说法
    Defect、CR(change Request)(Moto使用这种说法、submit a CR)、issue。
    缺陷流程:
    缺陷管理工具:QC、Mantis(开源工具)、bugfree(开源)、bugzilla、DDTS(Moto)、DMS(索爱)。
    缺陷状态:New、open、fixed、reopen、assigned、reject、later(postpone)、closed、abandon、duplicate(很常见的一个状态,重复bug,原有的有效,引用时引用原来的bug)。摩托罗拉的缺陷管理系统DDTS
    Postpone原因:有争议的、项目进度紧,优先级低的bug、以后版本就没有这个功能了。
    Retest时可能是不同的测试人员做回归测试:testerA(原)、TesterB(新),测试完后需要测试经理verify
    验证完成后bug没问题tester是否可以直接closed?如果tester没有对bug进行测试,直接closed后可能导致bug遗漏。

    第二阶段项目笔记
    JXC项目
    扩展名为ASP的文件是微软开发的
    扩展名为JSP的文件是JAVA编写的
    一.建立项目JXC
    1把JXC项目方在d:
    2点JXC右键—共享和安全----WEB共享----编辑别名----执行包括脚本等权限都选上
    3删掉JXC布置:控制面板—管理工具—Internet—网站----默认网站----JXC—右键删除
    4给权限:d:\点上边的文件夹—JXC—data—右键属性----安全与共享----添加—everyone----检查名称----确定----点中everyone—权限都选上—确定
    5登录:控制面板—管理工具—Internet信息服务-----JXC—右边栏里找到index.asp右键—浏览
    6找WEB共享或者安全:工具—文件夹—查看—使用简单文件共享前面的对号去掉
    7客户端需要建TSVN,服务器需要建SVN(必须)/TSVN(可建,可不建)
    二.布置JXC
    1建立仓库:新建文件夹SVN—新建文件夹jxc52-niuxiaoqing和文件夹JXC—在文件夹jxc52-niuxiaoqing里分别新建文件夹trunk branches tags—在文件夹trunk里分别新建文件夹doc code tools—在文件夹doc里分别建立文件夹croup person—在文件person里分别新建文件夹niu wang guo
    2点SVN文件夹—jxc52-niuxiaoqing右键----create repository here
    3创建的文件结构放到仓库:SVN文件夹------tortoisvn----import
    4binary文件时二进制文件(可执行的文件)
    5.Dll动态链接库文件有什么作用
    6 1)开始—运行—CMD—目录—cd空格c:\program files\svbversion\bin(svnserve的路径)—回车
    2)svnserve.exe空格–(2个下划线)help-------回车
    3)svnserve.exe空格-d空格-r空格d:\svn\jxc52-niuxiaoqing----回车
    4)停掉服务ctrl+c
    5)上光标,又出现3)回车,启动服务

    三.配置SVN
    1、svn搭建的服务环境:svnserve方式

    2、修改文件:

    svnserve.conf //修改服务器配置文件
    anon-access = none //去除注释 匿名访问 不允许
    auth-access = write //去除注释 指定用户访问 允许

     password-db = passwd //去除注释 启用密码文件passwd
     authz-db = authz     //去除注释 启用用户文件authz
    

    passwd //增加用户并设置密码
    zhangsan = 1111 // 用户名 = 密码

    authz //控制仓库的访问权限
    [groups]
    组名 = 用户名(逗号分隔)

     [/] 代表仓库的根目录
     如果使用组:@组名 = rw
    
     [/trunk/doc/person/lisi] 控制仓库里具体目录的权限
    
     如果没有特别规定某个目录的权限,则使用根目录[\]权限
    

    3、启动服务
    1、进入svn安装目录:cd C:\Program Files\Subversion\bin

    四.访问SVN
    1 svn checkout
    输入要访问的电脑IP
    File:///仓库建立在之间的计算机上
    SVN://仓库建立在其他机器上,要通过网络方式访问
    Apache为HTTP协议请求提供服务
    Tomcat为JAVA的容器,为JAVA编译的文件解释
    IIS为ASP提供服务
    QC的前身叫testdirector
    9.0之前是mercury公司
    10.0之后是惠普公司
    支持的数据库SQL ,Oracle
    支持的应用服务器JBoss
    支持的WEB服务器JBoss,IIS

    进销存项目
    上传图片:数据库中保存的是图片名称,实体保存在jxc/upload下。access中没有存储图片的类型。

    【SqlServer数据库中binary(二进制)类型可以用来保存图片。】

    为什么改名? ------有可能重名,同一个windows目录下不能重名

    为什么是日期时间?------重名的可能性小

    在哪见过类似的实现方式?----QC生成日志,日志保存名称,年月日秒毫秒(1秒=1000毫秒)
    系统生成的文件,为了解决文件重名的问题,使用日期时间命名,做好精确到毫秒级。

    日志功能怎么测?
    保存4个内容:日志级别、日志的最大保存行数(边界值)、保存天数(边界值)、日志保存路径。

    QC非活动状态(选择域后项目下拉列表中没有这个项目了)才能导出(export)----没有人操作

    离点(10001):预期结果(有三种实现方式):保存一个新的日志、覆盖第一条、整个全删了从第一条开始写。

    保存天数(-1):不限制。
    保存路径:目录空间是否够,如果不限制保存时间,硬盘保存空间不够时怎么办?tester是否对硬盘空间

    可操作?

    安装文件后会有一个bin目录,存有大量二进制文件(编译后),扩展名为.exe,为可运行程序。

    路径写到上一级,不包括本级
    文字相同

    001 用户名 用户名和编号
    002 密码

    新密码 确认新密码合为一个需求 新密码处验证特殊符号等,确认新密码只验证是否与新密码相同。

    排序 翻页 打印 详细信息

    所在仓库 有读取数据库的代码

    测试需求:

    测试需求的目的:保证测试的完整性。
    将需求导入QC中,能得出什么东西??-------not covered 应该被用例覆盖但是还没被覆盖。
    细化需求
    每个需求项下边的description:描述该项需求具体的 测试内容,提供测试思路。
    例如:
    产品图片:
    1.浏览功能
    支持的图片格式 jpg、gif(打开文件窗口对该文件类型进行过滤,默认显示过滤类型为jpg、gif)(不区分扩展名的大小写)。
    浏览本地路径、网络路径。
    2.上传功能
    被选文件路径是否合法(选择U盘中的文件,上传前拔掉U盘;存在本地,上传前删除);
    文件的大小限制(0,1M】
    被上传文件是否被独占打开(需要编写程序来独占打开该图片)。
    图片被上传到服务器的upload目录中,而且被修改图片名称(防止该目录下出现重名文件),名称格式:YYYYMMDDHHMMSS。
    上传成功后相关图片信息(图片的新名称)被保存到buy(photo字段)和produit(photo字段)。
    3.预览功能
    预览模式:平铺、拉伸、居中【一般预览功能就这几种模式】
    使用“居中”的预览模式。

    Buy表中 shulian(数量)保存时采用四舍五入。(有bug)。如何提bug???
    输多个小数点出错 编辑框输入的内容按字符(字符串)处理,放到数据库前需要进行字符转换,多个小数点时不能转换成float型,导致语法错误。
    Bug:
    页面允许输入浮点类型,数据库中为整形。
    页面控制的类型和数据库的类型不一致。
    进销存项目总结
    测试需求分析
    工作思路参考QC需求工作流:

    1、定义测试范围

        依据:ISO9126质量模型
        确定测试范围:被测质量特性
    

    2、建立需求项
    参考需求(SRS),明确具体要测试的需求项(测试点、需求点、功能点。。。)
    树形结构,考虑分析的“粒度”(参考QC)

        (1)、粗:文件夹级(只分解到模块或者页面层次)
        (2)、中:用例级  (只分解到页面中的具体控件,如“产品名称”、“入库数量”,意味这最底层需求可以直接转换为测试用例)
        (3)、细:步骤级  (分解到用例的操作步骤 Step)
        (4)、默认使用级别:用例级
    
         考虑后期用例执行,分解出一些特殊的需求(以后会专门对应某些测试类型)
    
        (1)、页面同名:(作为模板),只考虑该页面中所有控件都输入最正确的值
                       (控件同名的需求,目的从有效、无效两方面反复验证该控件输入的合法性)
        (2)、页面同名 Page: 以后作为“界面测试”使用
        (3)、模块同名:(调用该模块内所有页面同名的用例)作为预测试使用,证明版本的基本功能是否正确
    

    3、细化需求项
    描述每个需求项的详细内容

    详细内容:
    1、页面:(星号)是否允许为空
    2、数据库: 输入类型、长度(边界值)
    3、设计:跑到(所有)的路径----精简测试用例的个数

    如:入库管理模块–新产品入库页面–入库数量控件:
    1、页面:入库输入量不能为空

    2、数据库:
    数字类型(整型)
    边界:-231 ~ 231-1
    31:整型类型占用4个字节(4B),每个字节有8位(8bit),每位有两个取值(0、1),考虑符号(正、负)占用1位
    2:代表每位(bit)有两个取值(0、1)
    31:除去符号位,还剩31位

    3、代码实现:
    1)、当键盘抬起的时候,重置输入
    测试思路:(1)、抬起按键,非法字符的输入被重置
    (2)、绕过该事件,按下字母键不抬起进行鼠标焦点切换
    2)、当粘贴之前,重置输入
    考虑粘贴功能的实现(鼠标右键进行粘贴,快捷键粘贴 Crtl+V)
    3)、小数点个数最多1个(考虑边界值0,1,2个)

     具体分析思路:
    

    4.需要进行评审
    借助Rose的活动图统一思路

    4、需求覆盖率分析
    借助工具----QC
    将写好的需求导入到QC中的需求模块
    切换到需求的第四种视图方式:Coverage Analysis (需求覆盖率分析),看需求的状态(Not Covered 未覆盖状态),得知应该被覆盖到的需求的数量(只统计最底层需求的个数)(被测对象的测试规模),进而预测试出用例的数量

    当粘贴之前
    课前复习:
    需求名称为必填项,有空行不能导到QC中。
    把相同需求分类
    从需求详细描述从哪几方面去找?(1)页面角度 获取需求(星号—是否允许为空);(2)数据库 (长度----边界值、类型);(3)代码 实现方式----【目的:精简用例的个数,路径全部覆盖】
    代码实现讲解:
    onKeyUp="value=value.replace(/[^\d.]/g,’’)
    Onkeyup:当键盘键抬起的时候
    Replace:重新设置
    (/[^\d.]/g,’’):正则表达式 d----代表数字(0~9) .------代表小数点
    整个句子解释:
    当键盘抬起的时候,判断按键是否属于数组或者小数点,如果不属于这些键,则重置该输入(取消该输入)。

    这样只要设计2个用例就可(1)输入字母;(2)输入1.5。
    如果没有的代码,只按照等价类边界值的思想设计用例,需要考虑很多情况(此处略)。
    此处总结如下:

    onbeforepaste=“clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\d.]/g,’’))” value=“1”>
    onbeforepaste:当粘贴之前
    入库数量测试(设计):
    1.输出-------入库输入量不能为空;
    2.代码实现:
    (1)当键盘抬起的时候,重置输入;
    测试思路:绕过该事件,按下字母键不抬起鼠标焦点切换
    (2)当粘贴之前,重置输入
    考虑粘贴功能的实现(鼠标右键进行粘贴,快捷键粘贴Ctrl+V)
    3.小数点个数最多1个
    测试需求分析:
    如何做??1.工作思路参考QC需求工作流
    2.定义测试范围; 依据:ISO9126质量特性确定测试范围:被测质量特性
    3.建立需求项------参考需求(SRS)明确具体要测试的需求项(又叫测试点、需求点、功能点….)树型结构,考虑分析的“粒度”
    【粗—文件夹级(只分解到模块或者页面层次);
    中------用例级(只分解到页面中的具体控件,如“产品名称”、“入库数量”)
    细-----步骤级】
    考虑后期用例执行,分解出一些特殊的需求(以后)
    1.页面同名
    2.页面同名page
    3.模块同名
    4.细化需求项
    5.需求覆盖率分析

    判定表
    解决:多个输入的组合问题

    方法1:利用数据库的查询命令—笛卡尔积(全排列组合)
    计算最终的组合数量:每个表记录数的乘积
    如:A(a1,a2)
    B(b1,b2,b3,b4)
    C(c1,c2,c3)
    Select * from A、B、C

    方法2:判定表多个条件,每个条件2个取值

    3.1.读书选择
    1、如果觉得不疲倦,并且对书中的内容不感兴趣,同时书中的内容让你不糊涂,跳到下一章去读
    2、如果觉得不疲倦,并且对书中的内容不感兴趣,同时书中的内容让你糊涂, 跳到下一章去读
    3、如果觉得不疲倦,并且对书中的内容感兴趣, 同时书中的内容让你不糊涂,继续读下去
    4、如果觉得不疲倦,并且对中书的内容感兴趣, 同时书中的内容让你糊涂, 回到本章重读
    5、如果觉得疲倦, 并且对书中的内容不感兴趣,同时书中的内容让你不糊涂,停止阅读,请休息
    6、如果觉得疲倦, 并且对书中的内容不感兴趣,同时书中的内容让你糊涂, 请停止阅读,休息
    7、如果觉得疲倦, 并且对书中的内容感兴趣, 同时书中的内容让你不糊涂,继续读下去
    8、如果觉得疲倦, 并且对书中的内容感兴趣, 同时书中的内容让你糊涂, 回到本章重读

    提炼需求:多个条件的组合生成不同的结果
    1、需求分析:
    条件:是否疲倦(是、否)、是否感兴趣(是、否)、是否糊涂(是、否)
    结果:跳到下一章、继续读、本章重读
    2、绘制判定表
    1、分别填入条件和结果,确定表格的“行数”
    2、计算条件组合的数量,规划表格的“列数”
    3、将条件进行排列组合(全排列–笛卡尔积),利用二进制原理(0代表否,1代表是)。
    4、根据每种组合方式(每列),推出其对应的结果

    	1	2	3	4	5	6	7	8
    

    条件 是否疲倦 0 0 0 0 1 1 1 1
    是否感兴趣 0 0 1 1 0 0 1 1
    是否糊涂 0 1 0 1 0 1 0 1
    结果 跳到下一章 X X
    继续读 X X
    本章重读 X X
    休息 X X
    注:虽然1、2的结果是一样的,但是不能使用等价类的思想将其合并,原因:处理路径不同。(时间紧张可以合并)。

    3、编写测试用例
    原则1:判定表中的每一列生成一个测试用例,多个测试用例
    原则2:判定表中的每一列生成一个测试用例的步骤,一个测试用例

    我们项目使用:原则2
    两个原则测试的充分程度(覆盖率)相同;
    第二种进行需求跟踪更简单易操作;

    原则1:----8个测试用例
    用例编号 用例标题 用例输入 操作步骤 预期结果
    ST-001 不疲倦+没兴趣+不糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 否 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    ST-002 不疲倦+没兴趣+糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 是 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    ST-008 。。。。。。。。 。。。。。。。。 。。。。。。。。 。。。。。。。。

    原则2:-----1个测试用例,8个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果
    ST-001 读书选择 启动系统 。。。。。。。。 。。。。。。。。 。。。。。。。。
    准备测试 。。。。。。。。 。。。。。。。。 。。。。。。。。
    Step 1不疲倦+没兴趣+不糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 否 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    Step 2不疲倦+没兴趣+糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 是 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    Step 8 。。。。。。。。 。。。。。。。。
    3.2.Counter

    1、测试需求分析
    条件:是否统计代码行、是否统计空行、是否统计注释行、是否统计总行
    结果:统计代码行、统计空行、统计注释行、统计总行

    2、绘制判定表
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    条件 是否统计代码行 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
    是否统计空行 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
    是否统计注释行 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
    是否统计总行 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
    结果 统计代码行 X X X X X X X X
    统计空行 X X X X X X X X
    统计注释行 X X X X X X X X
    统计总行 X X X X X X X X

    3、生成测试用例
    每一列生成一个测试用例,一共16种组合方式
    原则1:----16个用例
    用例编号 用例编号 用例输入 操作步骤 预期结果 编写人 编写时间
    ST-001 1.
    ST-002 1.
    ST-003
    ST-004
    ST-005

    原则2:----1个测试用例,16个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果 编写人 编写时间
    ST-001 Counter统计

    3.3:word中的判定表举例
    Word中的判定表举例:
    (1)字体和字号

    输入太多,选择有代表的处理(等价类—减少输入个数)。
    字体:中文、英文(英文又可以分为2类:带横线(serif—如times new roman)、不带横线(sens serif----arial Unicode ))
    (2)

    组合:4个条件,每个条件2个值。
    (3)

    这个不能用判定表,因为不能同时选中“左对齐”和“右对齐”,不满足判定表使用条件。

    3.4.合并判定表
    前提:时间紧张,成本过高;
    原则:结果相同,条件有一个不同(貌似该条件没有作用);
    风险:被合并的条件可能走不同的“路径”,合并可能会造成漏测。
    对练习1(读书选择)中的判定表进行合并:

    	1	2	3	4	5	6	7	8
    

    条件 是否疲倦 0 0 0 0 1 1 1 1
    是否感兴趣 0 0 1 1 0 0 1 1
    是否糊涂 0 1 0 1 0 1 0 1
    结果 跳到下一章 X X
    继续读 X X
    本章重读 X X
    休息 X X

    判定表合并:

    	1	2	3	4
    	1、2	3、7	4、8	5、6
    

    条件 是否疲倦 0 ---- ---- 1
    是否感兴趣 0 1 1 0
    是否糊涂 — 0 1 —
    结果 跳到下一章 X
    继续读 X
    本章重读 X
    休息 X

    前提:
    合并:时间紧张,成本过高
    原则:结果相同,条件有一个不同(“貌似”该条件没有作用)
    风险:被合并的条件可能走不同的“路径”,合并会造出漏测

    3.4.密码修改
    若需修改密码,系统验证旧密码正确,两个新密码相同,则更新密码,旧密码即失效,其他修改项也生效,并提示“用户信息修改成功”; 若旧密码不正确,则提示“用户密码错”,系统将不修改个人信息;若两个新密码不同,则提示“新密码与验证新密码不同”,系统将不修改个人信息。
    若只修改密码外其他信息,则不需输入两个新密码,系统只验证旧密码正确,就成功更改个人信息,并提示“用户信息修改成功”;如果系统验证旧密码输入不正确,则提示“用户密码错”。
    流程图如下

    答案:
    (1)需求分析
    条件:只修改密码外其他信息(是/否)、旧密码(正确/错误)、新密码和验证新密码(相同/不相同)
    结果:提示“用户信息修改成功”、提示“用户密码错”、提示“新密码与验证新密码不同”
    (2)绘制判定表
    1 2 3 4 5 6 7 8
    条件 只修改密码外其他信息 0 0 0 0 1 1 1 1
    旧密码 0 0 1 1 0 0 1 1
    新密码和验证新密码 0 1 0 1 0 1 0 1
    结果 提示“用户信息修改成功” x x x
    提示“用户密码错” x x x
    提示“新密码与验证新密码不同” x
    说明 无效 无效
    (3)编写测试用例
    原则2:----1个测试用例,8个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果
    ST-001 修改信息 启动系统 无 双击 系统正常启动
    Step1:修改密码+旧密码错误+新密码与验证密码一致 是否只修改密码外其他信息:否;
    旧密码是否正确:否;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    无任何提示信息
    Step2:修改密码+旧密码错误+新密码与验证密码一致 是否只修改密码外其他信息:否;
    旧密码是否正确:否;
    新密码和验证新密码是否一致:是 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户密码错”,个人信息未修改
    Step3:修改密码+旧密码正确+新密码与验证密码不一致 是否只修改密码外其他信息:否;
    旧密码是否正确:是;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“新密码与验证新密码不同”,个人信息未修改
    Step4:修改密码+旧密码正确+新密码与验证密码一致 是否只修改密码外其他信息:否;
    旧密码是否正确:是;
    新密码和验证新密码是否一致:是 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户信息修改成功”,旧密码生效,其他个人信息修改
    Step5:只修改密码外信息+旧密码错误 是否只修改密码外其他信息:是;
    旧密码是否正确:否;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户密码错”,个人信息未修改
    Step7:只修改密码外信息+旧密码正确 是否只修改密码外其他信息:是;
    旧密码是否正确:是;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户信息修改成功”,其他个人信息修改

    3.5.进销存
    进销存中的下列选框可以使用判定表的方法设计用例:

    (1)需求分析
    条件: 仓库(所有仓库/具体仓库)(0表示所有仓库,1表示具体仓库)【“具体”只测试一个即可,处理方式相同,等价类思想】
    类别(包括大类和小类,有三种组合,分别为所有/所有、具体/所有、具体/具体)(分别使用0、1、2表示)
    关键字(填/不填)(0表示不填,1表示填)
    结果:所有仓库所有库存、所有仓库具体库存、具体仓库所有库存、具体仓库具体库存
    (2)绘制判定表
    1 2 3 4 5 6 7 8 9 10 11 12
    条件 仓库 0 0 0 0 0 0 1 1 1 1 1 1
    类别 0 0 1 1 2 2 0 0 1 1 2 2
    关键字 0 1 0 1 0 1 0 1 0 1 0 1
    结果 所有仓库所有库存 X X
    所有仓库具体库存 X X X X
    具体仓库所有库存 X X
    具体仓库具体库存 X X X X
    (3)编写测试用例
    使用原则2:----1个测试用例,12个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果
    ST-001 库存查询信息组合查询 进入库存查询页面。 无 1.打开进销存网页;2.点击库存管理模块中的库存查询页面。 界面显示库存查询页面。
    Step1:所有仓库+所有大类+所有小类+不填关键字 仓库:所有仓库;
    类别:所有大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的所有库存信息
    Step2:所有仓库+所有大类+所有小类+填关键字 仓库:所有仓库;
    类别:所有大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的所有库存信息
    Step3:所有仓库+具体大类+所有小类+不填关键字 仓库:所有仓库;
    类别:具体大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step4:所有仓库+具体大类+所有小类+填关键字 仓库:所有仓库;
    类别:具体大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step5:所有仓库+具体大类+具体小类+不填关键字 仓库:所有仓库;
    类别:具体大类和具体小类;
    关键字:不填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step6:所有仓库+具体大类+具体小类+填关键字 仓库:所有仓库;
    类别:具体大类和具体小类;
    关键字:填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step7:具体仓库+所有大类+所有小类+不填关键字 仓库:具体仓库;
    类别:所有大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的所有库存信息
    Step8:具体仓库+所有大类+所有小类+填关键字 仓库:具体仓库;
    类别:所有大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的所有库存信息
    Step9:具体仓库+具体大类+所有小类+不填关键字 仓库:具体仓库;
    类别:具体大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息
    Step10:具体仓库+具体大类+所有小类+填关键字 仓库:具体仓库;
    类别:具体大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息
    Step11:具体仓库+具体大类+具体小类+不填关键字 仓库:具体仓库;
    类别:具体大类和具体小类;
    关键字:不填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息
    Step12:具体仓库+具体大类+具体小类+填关键字 仓库:具体仓库;
    类别:具体大类和具体小类;
    关键字:填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息

    3.6.总结
    1、什么时候用:
    (1)有多个输入需要组合的时候,采用判定表进行用例设计;
    2、怎么用:
    (2)使用判定表进行用例设计,有以下3个步骤:
    a)需求分析,通过对多个条件的组合生成不同的结果进行分析,得出条件和结果。
    b)绘制判定表。
    i.分别填入条件和结果,确定表格的“行数”;
    ii.计算条件的组合数量,规划表格的“列数”;
    iii.将条件进行排列组合,利用二进制原理;
    iv.根据每种组合方式,推出其对应的结果。
    c)编写测试用例
    有2种方式:判定表中的每一列生成一个测试用例,最终生成多个测试用例;
    判定表中的每一列生成一个测试用例的步骤,最终生成一个测试用例。
    3判定表的优点和缺点
    (3)优点:组合充分,没有遗漏;
    缺点:当条件数多的时候(超过5个)用例数量多,成本高。

    因果图

    条件和结果之间的关系:

    恒等:条件成立,结果成立
    非: 条件成立,结果不成立
    或: 只要有一个条件成立,结果成立
    与: 必须所有条件都成立,结果成立

    条件之间的关系:

    E:不能同时为“真”
    I:不能同时为“假”
    O:有且仅有一个为“真”

    第一个举例:呼吸,用鼻子和嘴(异)
    因果图作用:1、条件和结果之间的关系:分析业务逻辑(类似流程图的作用)
    2、条件之间的关系:去除判定表中的无效的列
    使用viso绘制因果图:
    操作步骤:
    (1):在viso中选择基本流程图,
    (2)按照等价(处理方式相同)条件,添加中间节点(临时结果)。如:A、B处理相同(等价类),做中间节点(如A or B)。
    (3)分析条件和结果之间的关系(恒等、与、或、非)。
    如果不是一次退出结果,或者连线较多,可以增加中间节点。
    (4)分析条件之间的关系(E、I、O)。

    4.1.字母判定
    第一列字符必须是A或B,第二列字符必须是一个数字(每列只能输入一个字符),在此情况下(只有这个时候才)进行文件的修改,但如果第一列字符不正确,则给出信息L;如果第二列字符不是数字,则给出信息M。
    不能使用等价类【不是单一条件】

    1、测试需求分析
    条件:第一列是A、第一列是B、第二列是数字
    结果:修改文件、 提示L、 提示M

    2、绘制因果图

    3、绘制判定表
    假设:当判断完是否是AorB条件后,如果取“否”,则退出系统运行。【实际工作中不允许假设】。

    利用因果图条件之间的关系,去除无效列

    	1	2	3	4	5	6	7	8
    

    条件 第一列是A 0 0 0 0 1 1 1 1
    第一列是B 0 0 1 1 0 0 1 1
    第二列是数字 0 1 0 1 0 1 0 1
    结果 修改文件 X X
    提示L X X
    提示M X X
    实际输入 CC T7 B@ B5 AF A6 无效 无效

    第一种条件是提示L还是提示L、M,在公司中要找开发人员询问,不能主观猜测。

    编写测试用例
    原则2:----1个测试用例,8个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果 编写人 编写时间
    ST-001 修改文件

    4.2.自动售货机
    有一个处理单价为5角钱的饮料的自动售货机软件测试用例的设计。其规格说明如下:若投入5角钱或1元钱的硬币,押下〖橙汁〗或〖啤酒〗的按钮,则相应的饮料就送出来。若售货机没有零钱找,则一个显示〖零钱找完〗的红灯亮,这时在投入1元硬币并押下按钮后,饮料不送出来而且1元硬币也退出来;若有零钱找,则显示〖零钱找完〗的红灯灭,在送出饮料的同时退还5角硬币。

    1、测试需求分析:
    条件:有零钱、投1元、投5角、选啤酒、选橙汁
    结果:红灯亮、退1元、找5角、出啤酒、出橙汁

    2、绘制因果图

    3、绘制判定表

    红色列为无效组合

    4、生成测试用例
    经办人不能为空,程序中通过管理员ID判断当前登陆用户是否为管理员,如果在数据库login表中删除再添加一个管理员后id变了,导致查询错误。
    Admin登陆,下拉列表中显示所有系统的用户,非admin登陆,下拉列表中只显示当前登陆用户。
    关于下拉列表的问题:

    关于单元测试逻辑覆盖率:
    在只有一个条件时,路径覆盖率为100%时,条件覆盖率也是100%。
    如果条件不是布尔类型,可以使用数据库中的多表关联,计算笛卡尔积。
    如:
    A表(a1、a2)
    B表(b1、b2、b3、b4)
    C表(c1、c2、c3)
    使用select * from A、B、C;查询。

    状态迁移
    5.1.飞机售票系统
    1、客户向航空公司打电话预定机票—>此时机票信息处于“完成”状态
    2、顾客支付了机票费用后—>机票信息就变为“已支付”状态
    3、旅行当天到达机场后,拿到机票后—>机票信息就变为“已出票”状态
    4、登机检票后—>机票信息就变为“已使用”状态
    5、在登上飞机之前任何时间可以取消自己的订票信息,如果已经支付了机票的费用,则还可以得到退款,取消后—>订票信息处于“已被取消”状态

    1、测试需求分析
    状态:完成、已支付、已出票、已使用、已被取消

    2、绘制状态迁移图
    使用rose画状态图:
    方法:
    (1)右键,新建状态图New -----stagechar Diagram。

    特点:每个状态只出现一次

    3、生成用例 — 路径覆盖
    绘制状态迁移树-----每个树枝生成一个测试用例

    特点:
    1、每个状态可以出现多次
    2、箭头方向统一向“右”延伸
    3、状态转换如果出现循环,该路径只遍历一次
    4、编写测试用例
    用例编号 标题 步骤名称 步骤描述 预期结果
    STC-001 售票流程 启动系统
    准备测试数据和环境
    Step 1 完成-取消 1、客户向航空公司打电话预定机票
    2、取消该机票 1、此时机票信息处于“完成”状态
    2、订票信息处于“已被取消”状态
    Step 2 完成-支付-取消 。。。。。 。。。。。
    Step 3 。。。。。 。。。。。
    Step 4 。。。。。 。。。。。
    那些软件适合状态迁移
    淘宝买东西、QC需求、配置管理状态(normal、modified.;……)、缺陷状态(new、open……)
    QC需求状态(只考虑一次运行)

    5.2.缺陷跟踪

    流程分析
    使用ROSE中的活动图进行分析

    状态:吃饭中(有一个延续,使用ing)。
    活动:吃饭
    方法:
    (1)右键,新建状态图New -----activity Diagram。

    6.1.处理流程
    在某嵌入式系统中,将待发送的数据打包成符合CAN协议的帧格式后,便可写入发送缓站区,并自动发送。该发送子程序的流程为:
    1、首先进入发送子程序
    2、系统判断是否有空闲发送缓冲区,如果没有则返回启动发送失败消息。
    3、如果有空闲缓冲区,将数据包写入空闲发送缓冲区
    4、系统判断是否写入成功,如果不成功则返回启动发送失败消息
    5、如果写入成功,则启动发送命令
    6、返回启动发送成功消息
    1、绘制流程(活动)图

    2、生成测试用例------ 路径覆盖
    1、(A) (B) (C) (D) (E) (F) 基本流
    2、(A) (B) (G) 备选流
    3、(A) (B) (C) (D) (G) 备选流

    6.2.系统登录

    C/S程序。
    先判断是否为空,再去判断是否合法,涉及到效率问题。
    把判断时间短的放前边。

    用例:6个(路径)
    单个测试用户名考虑:空、最大值、特殊符号、匹配、不匹配
    6.3.字母判断

    用例:
    基本流: (1) (2) (3)
    (1A) (2) (3) 6列
    (1B) (2) (3) 4列
    备选流1:(1) (4) 1、2列
    备选流2:(1) (2) (5)
    (1A) (2) (5) 5列
    (1B) (2) (5) 3列

    	1	2	3	4	5	6	7	8
    

    条件 第一列是A 0 0 0 0 1 1 1 1
    第一列是B 0 0 1 1 0 0 1 1
    第二列是数字 0 1 0 1 0 1 0 1
    结果 修改文件 X X
    提示L X X
    提示M X X
    实际输入 CC T7 B@ B5 AF A6 无效 无效

    合并判定表
    1 2 3 4 5
    1、2 3 4 5 6
    条件 第一列是A 0 0 0 1 1
    第一列是B 0 1 1 0 0
    第二列是数字 ---- 0 1 0 1
    结果 修改文件 X X
    提示L X
    提示M X X
    实际输入 CC B@ B5 AF A6
    使用流程图和判定表
    都可以,使用流程图前提要保证流程图正确
    6.4.组合查询
    库存管理–库存查询代码实现:
    //
    nowkeyword–输入在查询关键字
    nowku-------输入在仓库名对应在id
    nowbigclass—输入的大类对应的id
    nowsmallclass—输入的小类对应的id
    &—+加在之前sql语句后边

    语句覆盖100%:1个用例;
    分支覆盖100%:2个用例;
    路径覆盖100%:16个用例。
    可以使用独立路径(每个路径只走一次):A-----、AB------、AC------、AD------、AE------(非法用例,不选大类不能选小类,使用ADE)。可以再找一个全选ABCDE。共6个用例。

    用例:
    1、 (A) (F) 独立路径
    2、 (A) (B) (F) 独立路径
    3、 (A) (C) (F) 独立路径
    4、 (A) (D) (F) 独立路径
    5、 (A) (D) (E) (F) 独立路径
    6、 (A) (B) (C) (D) (E) (F) 补充
    补充1关于where中1=1的问题:

    两个查询是一样的。为什么要加上1=1呢??因为每个select查询语句调教中只能加一个where,可以加多个and;如果在这句中不加1=1,之后的sql语句需要判断之前是否加过where,再做处理,影响查询效率。
    查询效率高。
    如果查询的是两个表,两个表查询就不用写了,两个表关联时就把where语句占用了。
    补充2取仓库ID,而不是仓库名称:
    代码实现:

    下拉列表由2部分组成,分别为value和库名。

    课前复习
    单个输入------- 等价类边界值
    多条件组合------判定表(全排列组合,组合个数2n(n代表条件个数)条件是布尔类型,如果不是布尔类型,参考数据库笛卡尔积,条件取值个数相乘)。
    ------因果图(帮助描述中间处理过程,去除判定表中的无效组合,3种约束:E、I、O)
    ------正交试验(两两组合)
    每个条件取值不规范,使用allpairs工具。
    处理流程问题------状态迁移(强调状态属性,用状态来描述流程)
    -----流程分析(活动图活流程图)
    生成用例原则:单元测试的路径覆盖。路径太多可以取独立路径或分支覆盖。

    正交试验
    7.1.环境搭建
    假设一个WEB站点,该站点有大量的服务器和操作系统,并且有许多具有各种插件的浏览器浏览:

    环境配置 Web浏览器 浏览器插件 操作系统 服务器
    配置选项 Netscape RealPlayer WinXP IIS
    IE Flash Win2000 Tomcat
    Firefox PDF Reader Win2003 Weblogic

    1、测试需求分析,找出条件和取值
    条件:4个
    取值:每个条件有3个取值

    2、选择正交表: L9_3_4
    9:代表最终生成用例的个数
    4:因素(因数),代表条件的个数
    3:水平,代表每个条件取值的个数

    3、生成用例
    因素 Web浏览器 浏览器插件 操作系统 服务器
    实验1 Netscape RealPlayer WinXP IIS
    实验2 Netscape Flash Win2000 Tomcat
    实验3 Netscape PDF Reader Win2003 Weblogic
    实验4 IE RealPlayer Win2000 Weblogic
    实验5 IE Flash Win2003 IIS
    实验6 IE PDF Reader WinXP Tomcat
    实验7 Firefox RealPlayer Win2003 Tomcat
    实验8 Firefox Flash WinXP Weblogic
    实验9 Firefox PDF Reader Win2000 IIS

    正交表的特点:
        1、任意一列,每个取值出现的次数一致(均匀)(本题出现3次)
        2、任意两列,任何两个值得组合出现的次数一致(均匀)(本题出现1次)
        3、任意一列,该列的每个值都和其他列的所有值成对组合过(均匀)(本题组合1次)
           正交表:充分的两两组合(9个用例)
           判定表:全排列组合(两两组合、三三、四四。。。。组合)(81个用例)
           
    使用原则:根据经验,如果充分的两两组合不出问题,那么多次组合出问题的可能性很小,基于成本、时间等因素,可以考虑正交试验方法生成测试用例
    

    7.2.Counter

    因素 代码行 注释行 空行 总行
    实验1 选 选 选 选
    实验2 选 选 选 不选
    实验3 选 不选 不选 选
    实验4 选 不选 不选 不选
    实验5 不选 选 不选 选
    实验6 不选 选 不选 不选
    实验7 不选 不选 选 选
    实验8 不选 不选 选 不选
    补充 不选 不选 不选 选
    补充 不选 不选 不选 不选

    7.3.组合
    题目:
    土壤的酸碱度:酸性、碱性、中性
    土壤的潮湿度:潮湿、干燥
    土壤的温度: 高温、低温
    提供标准正交表:
    因素 A B C
    实验1 0 0 0
    实验2 0 1 1
    实验3 1 0 1
    实验4 1 1 0

    答案:
    方法1)、正交试验方法:
    正交表:L4_2_3
    土壤的酸碱度:0(酸性)、1(碱性+中性)
    土壤的潮湿度:0(潮湿)、1(干燥)
    土壤的温度: 0(高温)、1(低温)

    因素 酸碱度 潮湿度 温度
    实验1 酸性 潮湿 高温
    实验2 酸性 干燥 低温
    实验3 碱性+中性 潮湿 低温
    实验4 碱性+中性 干燥 高温

    拆分正交表
    

    因素 酸碱度 潮湿度 温度
    实验1 酸性 潮湿 高温
    实验2 酸性 干燥 低温
    实验3 碱性 潮湿 低温
    实验4 中性 潮湿 低温
    实验5 碱性 干燥 高温
    实验6 中性 干燥 高温

    方法2)、判定表方法:
    绘制判定表
    1 2 3 4 5 6 7 8
    条件 酸碱度 0 0 0 0 1 1 1 1
    潮湿度 0 0 1 1 0 0 1 1
    温度 0 1 0 1 0 1 0 1

    带入判定表
    1 2 3 4 5 6 7 8
    条件 酸碱度 酸性 酸性 酸性 酸性 碱性+中性 碱性+中性 碱性+中性 碱性+中性
    潮湿度 潮湿 潮湿 干燥 干燥 潮湿 潮湿 干燥 干燥
    温度 高温 低温 高温 低温 高温 低温 高温 低温

    拆分判定表
    1 2 3 4 5 6 7 8 9 10 11 12
    条件 酸碱度 酸性 酸性 酸性 酸性 碱性 中性 碱性 中性 碱性 中性 碱性 中性
    潮湿度 潮湿 潮湿 干燥 干燥 潮湿 潮湿 潮湿 潮湿 干燥 干燥 干燥 干燥
    温度 高温 低温 高温 低温 高温 高温 低温 低温 高温 高温 低温 低温

    时间不充裕选择正交实验法,否则选择判定法(充分)

    方法3)、Allpairs工具实现
    使用allpairs工具:
    Cmd-----进入allpairs目录下 cd C:\Documents and Settings\51testing\桌面\allpairs----
    新建空白excl文件,将条件复制到excl中,

    TEST CASES
    case 土壤酸碱度 土壤潮湿度 土壤温度
    1 酸性 潮湿 高温
    2 酸性 干燥 低温
    3 碱性 潮湿 低温
    4 碱性 干燥 高温
    5 中性 潮湿 高温
    6 中性 干燥 低温

    7.4.环境搭建
    假设一个WEB站点,该站点有大量的服务器和操作系统,并且有许多具有各种插件的浏览器浏览:

    环境配置 Web浏览器 浏览器插件 操作系统 服务器
    配置选项 IE RealPlayer WinXP IIS
    Firefox Flash Win2000 Tomcat
    PDF Reader Win2003 Weblogic
    Baidu Win 7
    XunLei

    练习:进销存

    条件:
    仓库:所有、具体
    大类和小类:所有-所有、具体-所有、具体-具体
    关键词:不填、货号(模糊)、产品名称(模糊)

    其他
    输入域
    寻找输入中的特殊值
    如:注册页面输入用户名:hujintao、admin、administrator…….
    如:结构化输入(相互制约)要考虑组合:日期(年月日)、国家—城市,进销存权限复选控制(库存查询、删除)
    【上下级关系的选项】
    员工权限
    Bug:不选库存查询可以选择修改和删除。
    输出域
    找输出或者设计的等价类和边界值----根据结果的边界反推出输入
    如:数据库涉及时间的查询(首尾时间参考数据的第一条和最后一条记录的时间)
    如:QC需求,同级最大的需求数量:263
    最多的子集层数:255/3 = 85
    如:QC中的日志最大行数、
    如:QC中添加字段最多可以添加24个字段(可以通过查看数据库中的表得到)。

    异常分析
    可靠特性:容错、恢复
    如:SQL Server的导入功能(在源数据上构造错误数据,看异常处理功能的代码是否正确)
    如:进销存的数据还原

    错误猜测
    凭经验
    如:数字输入框(非数字字符的输入控制,小数点的个数)
    数据库的设计(约束关系)

    作业:
    三角形问题:

    成立条件:两边之和大于第三边。
    1、等价类
    2、判定表
    3、流程分析。
    密码修改问题:
    1、判定表
    流程分析

    展开全文
  • 软件测试面试题汇总

    万次阅读 多人点赞 2018-09-27 12:31:09
    27、软件的安全性应从哪个方面去测试? ................................................................................................. 9 28、软件配置管理工作开展的情况和认识? ......................
    转载自: https://blog.csdn.net/koudaidai/article/details/7394126

    软件测试面试题汇总

    测试技术面试题

    ........................................................................................................................................................................ 5

    1、什么是兼容性测试?兼容性测试侧重哪些方面?.................................................................................... 5

    2、我现在有个程序,发现在Windows上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?       5

    3、测试的策略有哪些?................................................................................................................................. 5

    4、正交表测试用例设计方法的特点是什么?............................................................................................... 5

    5、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程?.......................................... 5

    6、你觉得bugzilla在使用的过程中,有什么问题?................................................................................. 5

    7、描述测试用例设计的完整过程?.............................................................................................................. 6

    8、单元测试的策略有哪些?......................................................................................................................... 6

    9、LoadRunner分哪三部分?....................................................................................................................... 6

    10、LoadRunner进行测试的流程?................................................................................................................ 6

    什么是并发?在lordrunner中,如何进行并发的测试?集合点失败了会怎么样?.................................. 6

    12、使用QTP做功能测试,录制脚本的时候,要验证多个用户的登录情况/查询情况,如何操作?......... 6

    13、QTP中的Action有什么作用?有几种?................................................................................................. 6

    14、TestDirector有些什么功能,如何对软件测试过程进行管理?............................................................... 7

    15、你所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)?........................................................................................................................................... 7

    16、条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?...... 8

    17、Beta测试与Alpha测试有什么区别?...................................................................................................... 8

    18、软件的评审一般由哪些人参加?其目的是什么?.................................................................................. 8

    19、测试活动中,如果发现需求文档不完善或者不准确,怎么处理?........................................................ 8

    20、阶段评审与项目评审有什么区别?......................................................................................................... 8

    21、阐述工作版本的定义?............................................................................................................................ 8

    22、什么是桩模块?什么是驱动模块?......................................................................................................... 8

    23、什么是扇入?什么是扇出?.................................................................................................................... 8

    24、你认为做好测试计划工作的关键是什么?............................................................................................. 8

    25、你认为做好测试用例工作的关键是什么?............................................................................................. 9

    26、简述一下缺陷的生命周期?.................................................................................................................... 9

    27、软件的安全性应从哪几个方面去测试?................................................................................................. 9

    28、软件配置管理工作开展的情况和认识?................................................................................................. 9

    29、你觉得软件测试通过的标准应该是什么样的?.................................................................................... 10

    30、引入测试管理的含义?......................................................................................................................... 10

    31、一套完整的测试应该由哪些阶段组成?............................................................................................... 10

    32、单元测试的主要内容?......................................................................................................................... 10

    33、集成测试也叫组装测试或者联合测试,请简述集成测试的主要内容?.............................................. 10

    34、简述集成测试与系统测试关系?.......................................................................................................... 10

    35、软件测试的文档测试应当贯穿于软件生命周期的全过程,其中用户文档是文档测试的重点。那么软件系统的用户文档包括哪些?.............................................................................................................................. 10

    36、软件系统中除用户文档之外,文档测试还应该关注哪些文档?.......................................................... 10

    37、简述软件系统中用户文档的测试要点?............................................................................................... 11

    38、单元测试主要内容是什么?.................................................................................................................. 11

    39、如何理解强度测试?............................................................................................................................. 13

    40、如何理解压力、负载、性能测试测试?............................................................................................... 13

    41、什么是系统瓶颈?................................................................................................................................. 13

    42、文档测试主要包含什么内容?.............................................................................................................. 13

    43、功能测试用例需要详细到什么程度才是合格的?................................................................................ 14

    44、配置和兼容性测试的区别是什么?....................................................................................................... 14

    45、软件文档测试主要包含什么?.............................................................................................................. 15

    46、没有产品说明书和需求文档地情况下能够进行黑盒测试吗?............................................................. 15

    47、测试中的“杀虫剂怪事”是指什么?................................................................................................... 15

    48、在配置测试中,如何判断发现的缺陷是普通问题还是特定的配置问题?........................................... 15

    49、为什么尽量不要让时间有富裕的员工去做一些测试?......................................................................... 16

    50、完全测试程序是可能的吗?.................................................................................................................. 16

    51、软件测试的风险主要体现在哪里?....................................................................................................... 16

    52、发现的缺陷越多,说明软件缺陷越多吗?........................................................................................... 16

    53、所有的软件缺陷都能修复吗?所有的软件缺陷都要修复吗?............................................................. 17

    54、软件测试人员就是QA吗?.................................................................................................................... 17

    55、如何减少测试人员跳槽带来的损失?................................................................................................... 17

    56、测试产品与测试项目的区别是什么?................................................................................................... 17

    57、和用户共同测试(UAT测试)的注意点有哪些?................................................................................. 18

    58、如何编写提交给用户的测试报告?....................................................................................................... 18

    59、测试工具在测试工作中是什么地位?................................................................................................... 18

    60、什么是软件测试,软件测试的目的?................................................................................................... 18

    61、简述负载测试与压力测试的区别。....................................................................................................... 19

    62、写出bug报告流转的步骤,每步的责任人及主要完成的工作。.......................................................... 19

    63、写出bug报告当中一些必备的内容。................................................................................................... 19

    64、开发人员老是犯一些低级错误怎么解决?........................................................................................... 20

    65、画出软件测试的V模型图。.................................................................................................................. 20

    66、为什么要在一个团队中开展软件测试工作?........................................................................................ 20

    67、您在以往的测试工作中都曾经具体从事过哪些工作?其中最擅长哪部分工作?............................... 20

    68、您所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)............................................................................................................................................. 20

    69、您认为做好测试用例设计工作的关键是什么?.................................................................................... 21

    70、请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系。21

    71、测试计划工作的目的是什么?测试计划工作的内容都包括什么?其中哪些是最重要的?................. 22

    72、您所熟悉的测试用例设计方法都有哪些?请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。................................................................................................................................................................ 22

    73、请以您以往的实际工作为例,详细的描述一次测试用例设计的完整的过程。.................................... 23

    74、您以往是否曾经从事过性能测试工作?如果有,请尽可能的详细描述您以往的性能测试工作的完整过程。................................................................................................................................................................ 23

    75、你对测试最大的兴趣在哪里?为什么?................................................................................................ 23

    76、你以前工作时的测试流程是什么?....................................................................................................... 24

    77、当开发人员说不是BUG时,你如何应付?.......................................................................................... 24

    78、软件的构造号与版本号之间的区别?BVT(BuildVerificationTest)............................................... 24

    79、您以往的工作中,一条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?.................................................................................................................................................... 25

    80、您以往所从事的软件测试工作中,是否使用了一些工具来进行软件缺陷(Bug)的管理?如果有,请结合该工具描述软件缺陷(Bug)跟踪管理的流程。.......................................................................................... 25

    81、您认为性能测试工作的目的是什么?做好性能测试工作的关键是什么?........................................... 25

    82、单元测试、集成测试、系统测试的侧重点是什么?............................................................................. 25

    83、集成测试通常都有那些策略?............................................................................................................... 25

    84、一个缺陷测试报告的组成...................................................................................................................... 25

    85、基于WEB信息管理系统测试时应考虑的因素有哪些?......................................................................... 25

    86、软件测试项目从什么时候开始,?为什么?........................................................................................ 26

    87、需求测试注意事项有哪些?.................................................................................................................. 26

    88、简述一下缺陷的生命周期...................................................................................................................... 26

    89、你在你所在的公司是怎么开展测试工作的?是如何组织的?............................................................. 26

    90、你认为理想的测试流程是什么样子?................................................................................................... 26

    91、您在从事性能测试工作时,是否使用过一些测试工具?如果有,请试述该工具的工作原理,并以一个具体的工作中的例子描述该工具是如何在实际工作中应用的。...................................................................... 26

    92、软件测试活动的生命周期是什么?....................................................................................................... 26

    93、请画出软件测试活动的流程图?.......................................................................................................... 26

    94、针对缺陷采取怎样管理措施?.............................................................................................................. 26

    95、什么是测试评估?测试评估的范围是什么?........................................................................................ 26

    96、如果能够执行完美的黑盒测试,还需要进行白盒测试吗?为什么?.................................................. 26

    97、测试结束的标准是什么?...................................................................................................................... 26

    98、软件验收测试除了alpha ,beta测试以外,还有哪一种?.................................................................... 26

    99、做测试多久了?以前做过哪些项目?你们以前测试的流程是怎样的?用过哪些测试工具?............. 27

    100、请就如何在开发中进行软件质量控制说说你的看法.......................................................................... 27

    101、一套完整的测试应该由哪些阶段组成?分别阐述一下各个阶段。.................................................... 27

    102、软件测试的类型有那些?分别比较这些不同的测试类型的区别与联系。......................................... 27

    103、测试用例通常包括那些内容?着重阐述编制测试用例的具体做法.................................................... 27

    104、在分别测试winform的C/S结构与测试WEB结构的软件是,应该采取什么样的方法分别测试?他们存在什么样的区别与联系?.................................................................................................................................. 27

    105、在测试winform的C/S结构软件时,发现这个软件的运行速度很慢,您会认为是什么原因?您会采取哪些方法去检查这个原因?.............................................................................................................................. 27

    106、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程........................................ 27

    107、你都用什么测试方法 针对不同的产品或者系统或者模块,有不同的测试方法。总体而言有白盒测试和黑盒测试。.................................................................................................................................................... 27

    108、怎么编写案例 案例的编写与测试阶段的定义有很大的关系。系统测试和unit测试的案例可能不同。总体而言测试案例根据系统的需求而定。....................................................................................................... 27

    109、怎么才能够全面的测试到每一个点 测试的全面性主要需要在设计测试计划的时候考虑,从测试策略,产品需求等等多个角度考虑从而定义全部的测试点。................................................................................. 27

    110、谈谈软件测试技术,以及如何提高..................................................................................................... 27

    111、谈谈软件测试职业发展,以及个人的打算......................................................................................... 27

    112、谈谈软件测试在企业的地位,也可以结合软件生命周期来谈........................................................... 27

    113、一般公司里实际的软件测试流程是什么样的?你们公司又是怎样的?............................................ 27

    114、软件工程师要具有那些素质?............................................................................................................ 27

    115、你会哪些测试工具?怎么操作?........................................................................................................ 27

    116、你能不能说下你的3到5年的职业计划(规划)............................................................................... 27

    117、你觉得你来应聘有那些优势?............................................................................................................ 27

    其他问题:(有可能清晰的思路比确切的答案更重要)............................................................................. 27

     

    开发及环境搭建类面试题

    ....................................................................................................................................................................... 28

    1、描述软件产生内存泄露的原因以及检查方式。(可以结合一种开发语言进行描述)............................ 28

    2、简述什么是值传递,什么是地址传递,两者区别是什么?................................................................... 28

    3、结构化程序设计和面向对象程序设计各自的特点及优缺点是什么?.................................................... 28

    4、简述什么是存储过程和触发器?............................................................................................................. 28

    5、使用C语言编写一个函数,用于交换两个变量的值(地址传递)。...................................................... 29

    6、请简述DNS、活动目录、域的概念。..................................................................................................... 29

    7、描述TCP/IP协议的层次结构,以及每一层中重要协议。...................................................................... 29

    8、简述子网掩码的用途。............................................................................................................................ 29

    9、说出4种以上常用的操作系统及其主要的应用范围(微软的操作系统除外)。.................................... 29

    10、在Linux系统中,一个文件的访问权限是755,其含义是什么?......................................................... 29

    11、Windows操作系统中PATH环境变量的作用是什么?.......................................................................... 30

    12、Ghost的主要用途和常用方法?........................................................................................................... 30

    13、在RedHat中,从root用户切到userl用户,一般用什么命令?..................................................... 30

    14、Linux中,一般怎么隐藏文件?........................................................................................................... 30

    15、如何将自己的本地磁盘(D)做成FTP供远端主机使用?................................................................... 30

    16、对RUP.CMM,CMMI,XP,PSP.TSP的认识?............................................................................................. 30

    17、DNS是什么,它是如何工作的?............................................................................................................... 31

    18、防火墙如何保证安全的?主要有哪些?............................................................................................... 31

    19、目前流行的操作的系统有哪些?请举例说明安装操作系统的注意事项?........................................... 33

    20、简述一下c/s模式或者b/s模式?....................................................................................................... 33

    21、TCP/UDP有哪些区别?.......................................................................................................................... 34

    22、ISO模型?HUB、tch、Router是ISO的第几层设备?....................................................................... 34

    23、内存有哪几种存储组织结构.请分别加以说明?.................................................................................. 34

     

    人力资源面试题

    ....................................................................................................................................................................... 34

    1、你的测试职业发展是什么?你自认为做测试的优势在哪里?................................................................ 34

    2、你为什么想离开目前的职务?................................................................................................................ 34

    3、你对我们公司了解有多少?.................................................................................................................... 34

    4、你找工作时,最重要的考虑因素为何?................................................................................................. 34

    5、为什么我们应该录取你?........................................................................................................................ 34

    6、请谈谈你个人的最大特色。.................................................................................................................... 34

    7、一个测试工程师应具备那些素质和技能?.............................................................................................. 35

    8、您认为在测试人员同开发人员的沟通过程中,如何提高沟通的效率和改善沟通的效果?维持测试人员同开发团队中其他成员良好的人际关系的关键是什么?..................................................................................... 35

    9、在您以往的测试工作中,最让您感到不满意或者不堪回首的事情是什么?您是如何来对待这些事情的?35

    10、在即将完成这次笔试前,您是否愿意谈一些自己在以往的学习和工作中获得的工作经验和心得体会?(可以包括软件测试、过程改进、软件开发或者与此无关的其他方面)....................................................... 35

    11、为什么选择测试这行?......................................................................................................................... 35

    12、你的工作通常能在时限内完成吗.(我想问一下就是她问这个问题的动机是什么).......................... 35

    13、通常你对于别人批评你会有什么样的反应........................................................................................... 35

    14、如果明知这样做不对,你还会依主管的指过去做吗?......................................................................... 35

    15、如果你接到一个客户抱怨的电话,你确知无法解决他的问题,你会怎么处理?............................... 35

    16、请就软件测试人员应该具备什么样的基本素质说说你的看法。.......................................................... 36

    17、你在五年内的个人目标和职业目标分别是什么?................................................................................ 36

    18、你怎样做出自己的职业选择?.............................................................................................................. 36

     

     

     

     

     

     

     

     

    测试技术面试题

    1、什么是兼容性测试?兼容性测试侧重哪些方面?

    参考答案:

    兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行,即是通常说的软件的可移植性。

    兼容的类型,如果细分的话,有平台的兼容,网络兼容,数据库兼容,以及数据格式的兼容。

    兼容测试的重点是,对兼容环境的分析。通常,是在运行软件的环境不是很确定的情况下,才需要做兼容。根据软件运行的需要,或者根据需求文档,一般都能够得出用户会在什么环境下使用该软件,把这些环境整理成表单,就得出做兼容测试的兼容环境了。

    兼容和配置测试的区别在于,做配置测试通常不是Clean OS下做测试,而兼容测试多是在Clean OS的环境下做的。

    2、我现在有个程序,发现在Windows上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?

    参考答案:

    1、检查系统是否有中毒的特征;

    2、检查软件/硬件的配置是否符合软件的推荐标准;

    3、确认当前的系统是否是独立,即没有对外提供什么消耗CPU资源的服务;

    4、如果是C/S或者B/S结构的软件,需要检查是不是因为与服务器的连接有问题,或者访问有问题造成的;

    5、在系统没有任何负载的情况下,查看性能监视器,确认应用程序对CPU/内存的访问情况。

    3、测试的策略有哪些?

    参考答案:

    黑盒/白盒,静态/动态,手工/自动,冒烟测试,回归测试,公测(Beta测试的策略)

    4、正交表测试用例设计方法的特点是什么?

    参考答案:

    用最少的实验覆盖最多的操作,测试用例设计很少,效率高,但是很复杂;

    对于基本的验证功能,以及二次集成引起的缺陷,一般都能找出来;但是更深的缺陷,更复杂的缺陷,还是无能为力的;

    具体的环境下,正交表一般都很难做的。大多数,只在系统测试的时候使用此方法。

    5、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程?

    参考答案:

    就是Bugzilla的状态转换图。

    6、你觉得bugzilla在使用的过程中,有什么问题?

    参考答案:

    界面不稳定;

    根据需要配置它的不同的部分,过程很烦琐。

    流程控制上,安全性不好界定,很容易对他人的Bug进行误操作;

    没有综合的评分指标,不好确认修复的优先级别。

    7、描述测试用例设计的完整过程?

    参考答案:

    需求分析 + 需求变更的维护工作;

    根据需求 得出测试需求;

    设计测试方案,评审测试方案;

    方案评审通过后,设计测试用例,再对测试用例进行评审;

    8、单元测试的策略有哪些?

    参考答案:

    逻辑覆盖、循环覆盖、同行评审、桌前检查、代码走查、代码评审、景泰数据流分析

    9、LoadRunner分哪三部分?

    参考答案:

    用户动作设计;

    场景设计;

    测试数据分析;

    10、LoadRunner进行测试的流程?

    参考答案:

    1、 测试测试

    2、 创建虚拟用户脚本

    3、 创建运行场景

    4、 运行测试脚本

    5、 监视场景

    6、 分析测试的结果

    以上,最好是结合一个案例,根据以上流程来介绍。

    什么是并发?在lordrunner中,如何进行并发的测试?集合点失败了会怎么样?

    参考答案:

    在同一时间点,支持多个不同的操作。

    LoadRunner中提供IP伪装,集合点,配合虚拟用户的设计,以及在多台电脑上设置,可以比较好的模拟真实的并发。

    集合点,即是多个用户在某个时刻,某个特定的环境下同时进行虚拟用户的操作的。集合点失败,则集合点的才操作就会取消,测试就不能进行。

    12、使用QTP做功能测试,录制脚本的时候,要验证多个用户的登录情况/查询情况,如何操作?

    参考答案:

    分析用户登录的基本情况,得出一组数据,通过性测试/失败性测试的都有(根据TC来设计这些数据),然后录制登录的脚本,将关键的数据参数化,修改脚本,对代码进行加强,调试脚本。

    13、QTP中的Action有什么作用?有几种?

    参考答案:

    Action的作用

    用Action可以对步骤集进行分组

    步骤重组,然后被整体调用

    拥有自己的sheet

    组合有相同需求的步骤,整体操作

    具有独立的对象仓库

    Action的种类

    可复用Action

    不可复用Action

    外部Action

    14、TestDirector有些什么功能,如何对软件测试过程进行管理?

    参考答案:

     需求管理

    定义测试范围

    定义需求树

    描述需求树的功能点

    测试计划

    定义测试目标和测试策略。

    分解应用程序,建立测试计划树。

    确定每个功能点的测试方法。

    将每个功能点连接到需求上,使测试计划覆盖全部的测试需求。

    描述手工测试的测试步骤

    指明需要进行自动测试的功能点

    测试执行

    定义测试集合。

    为每个测试人员制定测试任务和测试日程安排。

    运行自动测试。

    缺陷跟踪

    记录缺陷

    查看新增缺陷,并确定哪些是需要修正的

    相关技术人员修改缺陷

    回归测试

    分析缺陷统计图表,分析应用程序的开发质量。

    15、你所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)?

    参考答案:Compatibility Testing(兼容性测试),也称“Configuration testing(配置测试)”,测试软件是否和系统的其它与之交互的元素之间兼容,如:浏览器、操作系统、硬件等。验证测试对象在不同的软件和硬件配置中的运行情况。

     

    Functional testing (功能测试),也称为behavioral testing(行为测试),根据产品特征、操作描述和用户方案,测试一个产品的特性和可操作行为以确定它们满足设计需求。本地化软件的功能测试,用于验证应用程序或网站对目标用户能正确工作。使用适当的平台、浏览器和测试脚本,以保证目标用户的体验将足够好,就像应用程序是专门为该市场开发的一样。

    Performance testing(性能测试),评价一个产品或组件与性能需求是否符合的测试。包括负载测试、强度测试、数据库容量测试、基准测试等类型。

    16、软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?

    参考答案:5C标准

    17、Beta测试与Alpha测试有什么区别?

    参考答案:Beta testing(β测试),测试是软件的多个用户在一个或多个用户的实际使用环境下进行的测试。开发者通常不在测试现场

    Alpha testing (α测试),是由一个用户在开发环境下进行的测试,也可以是公司内部的用户在模拟实际操作环境下进行的受控测试

    18、软件的评审一般由哪些人参加?其目的是什么?

    参考答案:

    在正式的会议上将软件项目的成果(包括各阶段的文档、产生的代码等)提交给用户、客户或有关部门人员对软件产品进行评审和批准。其目的是找出可能影响软件产品质量、开发过程、维护工作的适用性和环境方面的设计缺陷,并采取补救措施,以及找出在性能、安全性和经济方面的可能的改进。

    人员:用户、客户或有关部门开发人员,测试人员,需求分析师都可以,就看处于评审那个阶段

    19、测试活动中,如果发现需求文档不完善或者不准确,怎么处理?

    参考答案:

    测试需求分析发现需求文档不完善或者不准确,应该立即和相关人员进行协调交流。

    20、阶段评审与项目评审有什么区别?

    参考答案:

    阶段评审对项目各阶段评审:对阶段成果和工作

    项目评审对项目总体评审:对工作和产品

    21、阐述工作版本的定义?

    参考答案:

    构造号: BUILD

    22、什么是桩模块?什么是驱动模块?

    参考答案:

    桩模块:被测模块调用模块

    驱动模块调用被测模块

    23、什么是扇入?什么是扇出?

    参考答案:

    扇入:被调次数,扇出:调其它模块数目

    24、你认为做好测试计划工作的关键是什么?

    参考答案:

    软件测试计划就是在软件测试工作正式实施之前明确测试的对象,并且通过对资源、时间、风险、测试范围和预算等方面的综合分析和规划,保证有效的实施软件测试;

    做好测试计划工作的关键:目的,管理,规范

    1. 明确测试的目标,增强测试计划的实用性

    编写软件测试计划得重要目的就是使测试过程能够发现更多的软件缺陷,因此软件测试计划的价值取决于它对帮助管理测试项目,并且找出软件潜在的缺陷。因此,软件测试计划中的测试范围必须高度覆盖功能需求,测试方法必须切实可行,测试工具并且具有较高的实用性,便于使用,生成的测试结果直观、准确

    2.坚持“5W”规则,明确内容与过程

    “5W”规则指的是“What(做什么)”、“Why(为什么做)”、“When(何时做)”、“Where(在哪里)”、“How(如何做)”。利用“5W”规则创建软件测试计划,可以帮助测试团队理解测试的目的(Why),明确测试的范围和内容(What),确定测试的开始和结束日期(When),指出测试的方法和工具(How),给出测试文档和软件的存放位置(Where)。

    3.采用评审和更新机制,保证测试计划满足实际需求

    测试计划写作完成后,如果没有经过评审,直接发送给测试团队,测试计划内容的可能不准确或遗漏测试内容,或者软件需求变更引起测试范围的增减,而测试计划的内容没有及时更新,误导测试执行人员。

    4. 分别创建测试计划与测试详细规格、测试用例

    应把详细的测试技术指标包含到独立创建的测试详细规格文档,把用于指导测试小组执行测试过程的测试用例放到独立创建的测试用例文档或测试用例管理数据库中。测试计划和测试详细规格、测试用例之间是战略和战术的关系,测试计划主要从宏观上规划测试活动的范围、方法和资源配置,而测试详细规格、测试用例是完成测试任务的具体战术。

    25、你认为做好测试用例工作的关键是什么?

    参考答案:

     需求和设计文档的理解程度,对系统的熟悉程度

    26、简述一下缺陷的生命周期?

    参考答案:提交->确认->分配->修复->验证->关闭

    27、软件的安全性应从哪几个方面去测试?

    参考答案:

    (1)用户认证机制:如数据证书、智能卡、双重认证、安全电子交易协议

    (2)加密机制

    (3)安全防护策略:如安全日志、入侵检测、隔离防护、漏洞扫描

    (4)数据备份与恢复手段:存储设备、存储优化、存储保护、存储管理

    (5)防病毒系统

    28、软件配置管理工作开展的情况和认识?

    参考答案:

    软件配置管理贯穿于软件开发、测试活动的始终,覆盖了开发、测试活动的各个环节,它的重要作用之一就是要全面的管理保存各个配置项,监控各配置项的状态,并向项目经理及相关的人员报告,从而实现对软件过程的控制。

    软件测试配置管理包括4个最基本的活动:

    配置项标识

    配置项控制

    配置项状态报告

    配置审计

           软件配置管理通常借助工具来辅助,主要有MS SourceSafe、Rational ClearCase等

    29、你觉得软件测试通过的标准应该是什么样的?

    参考答案:

        缺陷密度值达到客户的要求

    30、引入测试管理的含义?

    参考答案:风险分析,进度控制、角色分配、质量控制

    31、一套完整的测试应该由哪些阶段组成?

    参考答案:测试计划、测试设计与开发、测试实施、测试评审与测试结论

    32、单元测试的主要内容?

    参考答案:

     模块接口测试、局部数据结构测试、路径测试、错误处理测试、边界测试

    33、集成测试也叫组装测试或者联合测试,请简述集成测试的主要内容?

    参考答案:

    (1)在把各个模块连接起来的时候,穿越模块接口的数据是否会丢失;

     (2)一个模块的功能是否会对另一个模块的功能产生不利的影响;

     (3)各个子功能组合起来,能否达到预期要求的父功能;

     (4)全局数据结构是否有问题;

     (5)单个模块的误差累积起来,是否会放大,从而达到不能接受的程度。

    34、简述集成测试与系统测试关系?

    参考答案:

     (1)集成测试的主要依据概要设计说明书,系统测试的主要依据是需求设计说明书;

     (2)集成测试是系统模块的测试,系统测试是对整个系统的测试,包括相关的软硬件平台、网络以及相关外设的测试。

    35、软件测试的文档测试应当贯穿于软件生命周期的全过程,其中用户文档是文档测试的重点。那么软件系统的用户文档包括哪些?

    参考答案:

      用户手册

      安装和设置指导

      联机帮助

      指南、向导

      样例、示例和模板

      授权/注册登记表

    最终用户许可协议

    36、软件系统中除用户文档之外,文档测试还应该关注哪些文档?

    参考答案:

    开发文档

    软件需求说明书

        数据库设计说明书

        概要设计说明书

        详细设计说明书

        可行性研究报告

    管理文档

        项目开发计划

        测试计划

        测试报告

        开发进度月报

        开发总结报告

    37、简述软件系统中用户文档的测试要点?

    参考答案:

     (1)读者群。文档面向的读者定位要明确。对于初级用户、中级用户以及高级用户应该有不同的定位

     (2)术语。文档中用到的术语要适用与定位的读者群,用法一致,标准定义与业界规范相吻合。

     (3)正确性。测试中需检查所有信息是否真实正确,查找由于过期产品说明书和销售人员夸大事实而导致的错误。检查所有的目录、索引和章节引用是否已更新,尝试链接是否准确,产品支持电话、地址和邮政编码是否正确。

     (4)完整性。对照软件界面检查是否有重要的分支没有描述到,甚至是否有整个大模块没有描述到。

     (5)一致性。按照文档描述的操作执行后,检查软件返回的结果是否与文档描述的相同。

     (6)易用性。对关键步骤以粗体或背景色给用户以提示,合理的页面布局、适量的图表都可以给用户更高的易用性。需要注意的是文档要有助于用户排除错误。不但描述正确操作,也要描述错误处理办法。文档对于用户看到的错误信息应当有更详细的文档解释。

     (7)图表与界面截图。检查所有图表与界面截图是否与发行版本相同。

     (8)样例与示例。像用户一样载入和使用样例。如果是一段程序,就输入数据并执行它。以每一个模块制作文件,确认它们的正确性。

     (9)语言。不出现错别字,不要出现有二义性的说法。特别要注意的是屏幕截图或绘制图形中的文字。

     (10)印刷与包装。检查印刷质量;手册厚度与开本是否合适;包装盒的大小是否合适;有没有零碎易丢失的小部件等等。

    38、单元测试主要内容是什么?

    参考答案:

    单元测试大多数由开发人员来完成,测试人员技术背景较好或者开发系统软件时可能会安排测试人员进行单元测试,大多数进行的单元测试都是开发人员调试程序或者开发组系统联合调试的过程。讨论这个问题主要是扩充一下读者的视野。

    单元测试一般包括五个方面的测试:

    (1)模块接口测试:模块接口测试是单元测试的基础。只有在数据能正确流入、流出模块的前提下,其他测试才有意义。模块接口测试也是集成测试的重点,这里进行的测试主要是为后面打好基础。测试接口正确与否应该考虑下列因素:

    -输入的实际参数与形式参数的个数是否相同;

    -输入的实际参数与形式参数的属性是否匹配;

    -输入的实际参数与形式参数的量纲是否一致;

    -调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;

    -调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;

    -调用其他模块时所给实际参数的量纲是否与被调模块的形参量纲一致;

    -调用预定义函数时所用参数的个数、属性和次序是否正确;

    -是否存在与当前入口点无关的参数引用;

    -是否修改了只读型参数;

    -对全程变量的定义各模块是否一致;

    -是否把某些约束作为参数传递。

    如果模块功能包括外部输入输出,还应该考虑下列因素:

    -文件属性是否正确;

    -OPEN/CLOSE语句是否正确;

    -格式说明与输入输出语句是否匹配;

    -缓冲区大小与记录长度是否匹配;

    -文件使用前是否已经打开;

    -是否处理了文件尾;

    -是否处理了输入/输出错误;

    -输出信息中是否有文字性错误。

    -局部数据结构测试;

    -边界条件测试;

    -模块中所有独立执行通路测试;

    (2)局部数据结构测试:检查局部数据结构是为了保证临时存储在模块内的数据在程序执行过程中完整、正确,局部功能是整个功能运行的基础。重点是一些函数是否正确执行,内部是否运行正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现下面几类错误:

    -不合适或不相容的类型说明;

    -变量无初值;

    -变量初始化或省缺值有错;

    -不正确的变量名(拼错或不正确地截断);

    -出现上溢、下溢和地址异常。

    (3)边界条件测试:边界条件测试是单元测试中最重要的一项任务。众所周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。边界条件测试是一项基础测试,也是后面系统测试中的功能测试的重点,边界测试执行的较好,可以大大提高程序健壮性。

    (4)模块中所有独立路径测试:在模块中应对每一条独立执行路径进行测试,单元测试的基本任务是保证模块中每条语句至少执行一次。测试目的主要是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。具体做法就是程序员逐条调试语句。常见的错误包括:

    -误解或用错了算符优先级;

    -混合类型运算;

    -变量初值错;

    -精度不够;

    -表达式符号错。

    比较判断与控制流常常紧密相关,测试时注意下列错误:

    -不同数据类型的对象之间进行比较;

    -错误地使用逻辑运算符或优先级;

    -因计算机表示的局限性,期望理论上相等而实际上不相等的两个量相等;

    -比较运算或变量出错;

    -循环终止条件或不可能出现;

    -迭代发散时不能退出;

    -错误地修改了循环变量。

    模块的各条错误处理通路测试:程序在遇到异常情况时不应该退出,好的程序应能预见各种出错条件,并预设各种出错处理通路。如果用户不按照正常操作,程序就退出或者停止工作,实际上也是一种缺陷,因此单元测试要测试各种错误处理路径。一般这种测试着重检查下列问题:

    -输出的出错信息难以理解;

    -记录的错误与实际遇到的错误不相符;

    -在程序自定义的出错处理段运行之前,系统已介入;

    -异常处理不当;

    -错误陈述中未能提供足够的定位出错信息。

    39、如何理解强度测试?

    参考答案:

    强度测试是为了确定系统在最差工作环境的工作能力,也可能是用于验证在标准工作压力下的各种资源的最下限指标。

    它和压力测试的目标是不同的,压力测试是在标准工作环境下,不断增加系统负荷,最终测试出该系统能力达到的最大负荷(稳定和峰值),而强度测试则是在非标准工作环境下,甚至不断人为降低系统工作环境所需要的资源,如网络带宽,系统内存,数据锁等等,以测试系统在资源不足的情况下的工作状态,通过强度测试,可以确定本系统正常工作的最差环境.

    强度测试和压力测试的测试指标相近,大多都是与时间相关的指标,如并发量(吞吐量),延迟(最大\最小\平均)以及顺序指标等

    强度测试需要对系统的结构熟悉,针对系统的特征设计强度测试的方法

    40、如何理解压力、负载、性能测试测试?

    参考答案:

    性能测试是一个较大的范围,实际上性能测试本身包含了性能、强度、压力、负载等多方面的测试内容。

    压力测试是对服务器的稳定性以及负载能力等方面的测试,是一种很平常的测试。增大访问系统的用户数量、或者几个用户进行大数据量操作都是压力测试。而负载测试是压力相对较大的测试,主要是测试系统在一种或者集中极限条件下的相应能力,是性能测试的重要部分。100个用户对系统进行连续半个小时的访问可以看作压力测试,那么连续访问8个小时就可以认为负载测试,1000个用户连续访问系统1个小时也可以看作是负载测试。

    实际上压力测试和负载测试没有明显的区分。测试人员应该站在关注整体性能的高度上来对系统进行测试。

    41、什么是系统瓶颈?

    参考答案:

    瓶颈主要是指整个软硬件构成的软件系统某一方面或者几个方面能力不能满足用户的特定业务要求,“特定”是指瓶颈会在某些条件下会出现,因为毕竟大多数系统在投入前。

    严格的从技术角度讲,所有的系统都会有瓶颈,因为大多数系统的资源配置不是协调的,例如CPU使用率刚好达到100%时,内存也正好耗尽的系统不是很多见。因此我们讨论系统瓶颈要从应用的角度讨论:关键是看系统能否满足用户需求。在用户极限使用系统的情况下,系统的响应仍然正常,我们可以认为改系统没有瓶颈或者瓶颈不会影响用户工作。

    因此我们测试系统瓶颈主要是实现下面两个目的:

    -发现“表面”的瓶颈。主要是模拟用户的操作,找出用户极限使用系统时的瓶颈,然后解决瓶颈,这是性能测试的基本目标。

    -发现潜在的瓶颈并解决,保证系统的长期稳定性。主要是考虑用户在将来扩展系统或者业务发生变化时,系统能够适应变化。满足用户目前需求的系统不是最好的,我们设计系统的目标是在保证系统整个软件生命周期能够不断适应用户的变化,或者通过简单扩展系统就可以适应新的变化。

    42、文档测试主要包含什么内容?

    参考答案:

    在国内软件开发管理中,文档管理几乎是最弱的一项,因而在测试工作中特别容易忽略文档测试也就不足为奇了。要想给用户提供完整的产品,文档测试是必不可少的。文档测试一般注重下面几个方面:

    文档的完整性:主要是测试文档内容的全面性与完整性,从总体上把握文档的质量。例如用户手册应该包括软件的所有功能模块。

    描述与软件实际情况的一致性:主要测试软件文档与软件实际的一致程度。例如用户手册基本完整后,我们还要注意用户手册与实际功能描述是否一致。因为文档往往跟不上软件版本的更新速度。

    易理解性:主要是检查文档对关键、重要的操作有无图文说明,文字、图表是否易于理解。对于关键、重要的操作仅仅只有文字说明肯定是不够的,应该附有图表使说明更为直观和明了。

    文档中提供操作的实例:这项检查内容主要针对用户手册。对主要功能和关键操作提供的应用实例是否丰富,提供的实例描述是否详细。只有简单的图文说明,而无实例的用户手册看起来就像是软件界面的简单拷贝,对于用户来说,实际上没有什么帮助。

    印刷与包装质量:主要是检查软件文档的商品化程度。有些用户手册是简单打印、装订而成,过于粗糙,不易于用户保存。优秀的文档例如用户手册和技术白皮书,应提供商品化包装,并且印刷精美。

    43、功能测试用例需要详细到什么程度才是合格的?

    参考答案:

    这个问题也是测试工程师经常问的问题。有人主张测试用例详细到每个步骤执行什么都要写出来,目的是即使一个不了解系统的新手都可以按照测试用例来执行工作。主张这类写法的人还可以举出例子:欧美、日本等软件外包文档都是这样做的。

    另外一种观点就是主张写的粗些,类似于编写测试大纲。主张这种观点的人是因为软件开发需求管理不规范,变动十分频繁,因而不能按照欧美的高标准来编写测试用例。这样的测试用例容易维护,可以让测试执行人员有更大的发挥空间。

    实际上,软件测试用例的详细程度首先要以覆盖到测试点为基本要求。举个例子:“用户登陆系统”的测试用例可以不写出具体的执行数据,但是至少要写出五种以上情况(),如果只用一句话覆盖了这个功能是不合格的测试用例。覆盖功能点不是指列出功能点,而是要写出功能点的各个方面(如果组合情况较多时可以采用等价划分)。

    另一个影响测试用例的就是组织的开发能力和测试对象特点。如果开发力量比较落后,编写较详细的测试用例是不现实的,因为根本没有那么大的资源投入,当然这种情况很随着团队的发展而逐渐有所改善。测试对象特点重点是指测试对象在进度、成本等方面的要求,如果进度较紧张的情况下,是根本没有时间写出高质量的测试用例的,甚至有些时候测试工作只是一种辅助工作,因而不编写测试用例。

    因此,测试用例的编写要根据测试对象特点、团队的执行能力等各个方面综合起来决定编写策略。最后要注意的是测试人员一定不能抱怨,力争在不断提高测试用例编写水平的同时,不断地提高自身能力。

    44、配置和兼容性测试的区别是什么?

    参考答案:

    配置测试的目的是保证软件在其相关的硬件上能够正常运行,而兼容性测试主要是测试软件能否与不同的软件正确协作。

    配置测试的核心内容就是使用各种硬件来测试软件的运行情况,一般包括:

    (1)软件在不同的主机上的运行情况,例如Dell和Apple;

    (2)软件在不同的组件上的运行情况,例如开发的拨号程序要测试在不同厂商生产的Modem上的运行情况;

    (3)不同的外设;

    (4)不同的接口;

    (5)不同的可选项,例如不同的内存大小;

    兼容性测试的核心内容:

    (1)测试软件是否能在不同的操作系统平台上兼容;

    (2)测试软件是否能在同一操作系统平台的不同版本上兼容;

    (3)软件本身能否向前或者向后兼容;

    (4)测试软件能否与其它相关的软件兼容;

    (5)数据兼容性测试,主要是指数据能否共享;

    配置和兼容性测试通称对开发系统类软件比较重要,例如驱动程序、操作系统、数据库管理系统等。具体进行时仍然按照测试用例来执行。

    45、软件文档测试主要包含什么?

    参考答案:

    随着软件文档系统日益庞大,文档测试已经成为软件测试的重要内容。文档测试对象主要如下:

    -包装文字和图形;

    -市场宣传材料、广告以及其它插页;

    -授权、注册登记表;

    -最终用户许可协议;

    -安装和设置向导;

    -用户手册;

    -联机帮助;

    -样例、示范例子和模板;

    -……

    文档测试的目的是提高易用性和可靠性,降低支持费用,因为用户通过文档就可以自己解决问题。因文档测试的检查内容主要如下:

    -读者对象——主要是文档的内容是否能让该级别的读者理解;

    -术语——主要是检查术语是否适合读者;

    -内容和主题——检查主题是否合适、是否丢失、格式是否规范等;

    -图标和屏幕抓图——检查图表的准确度和精确度;

    -样例和示例——是否与软件功能一致;

    -拼写和语法;

    -文档的关联性——是否与其它相关文档的内容一致,例如与广告信息是否一致;

    文档测试是相当重要的一项测试工作,不但要给予充分的重视,更要要认真的完成,象做功能测试一样来对待文档测试。

    46、没有产品说明书和需求文档地情况下能够进行黑盒测试吗?

    参考答案:

    这个问题是国内测试工程师经常遇到的问题,根源就是国内软件开发文档管理不规范,对变更的管理方法就更不合理了。实际上没有任何文档的时候,测试人员是能够进行黑盒测试的,这种测试方式我们可以称之为探索测试,具体做法就是测试工程师根据自己的专业技能、领域知识等不断的深入了解测试对象、理解软件功能,进而发现缺陷。

    在这种做法基本上把软件当成了产品说明书,测试过程中要和开发人员不断的进行交流。尤其在作项目的时候,进度压力比较大,可以作为加急测试方案。最大的风险是不知道有些特性是否被遗漏。

    47、测试中的“杀虫剂怪事”是指什么?

    参考答案:

    “杀虫剂怪事”一词由BorisBeizer在其编著的《软件测试技术》第二版中提出。用于描述测试人员对同一测试对象进行的测试次数越多,发现的缺陷就会越来越少的现象。就像老用一种农药,害虫就会有免疫力,农药发挥不了效力。这种现象的根本原因就是测试人员对测试软件过于熟悉,形成思维定势。

    为了克服这种现象,测试人员需要不断编写新的测试程序或者测试用例,对程序的不同部分进行测试,以发现更多的缺陷。也可以引用新人来测试软件,刚刚进来的新手往往能发现一些意想不到的问题。

    48、在配置测试中,如何判断发现的缺陷是普通问题还是特定的配置问题?

    参考答案:

    在进行配置测试时,测试工程师仍然会发现一些普通的缺陷,也就是与配置环境无关的缺陷。因此判断新发现的问题,需要在不同的配置中重新执行发现软件缺陷的步骤,如果软件缺陷不出现了,就可能是配置缺陷;如果在所有的配置中都出现,就可能是普通缺陷。

    需要注意的是,配置问题可以在一大类配置中出现。例如,拨号程序可能在所有的外置Modem中都存在问题,而内置的Modem不会有任何问题。

    49、为什么尽量不要让时间有富裕的员工去做一些测试?

    参考答案:

    表面上看这体现了管理的效率和灵活性,但实际上也体现了管理者对测试的轻视。测试和测试的人有很大关系。测试工作人员应该是勤奋并富有耐心,善于学习、思考和发现问题,细心有条理,总结问题,如果具备这样的优点,做其它工作同样也会很出色,因此这里还有一个要求,就是要喜欢测试这项工作。如果他是专职的,那么肯定更有经验和信心。国内的小伙子好象都喜欢做程序员,两者工作性质不同,待遇不同,地位不同,对自我实现的价值的认识也不同,这是行业的一个需要改善的问题。如果只是为了完成任务而完成任务,或者发现了几个问题就觉得满意了,这在任何其它工作中都是不行的。

    50、完全测试程序是可能的吗?

    参考答案:

    软件测试初学者可能认为拿到软件后需要进行完全测试,找到全部的软件缺陷,使软件“零缺陷”发布。实际上完全测试是不可能的。主要有以下一个原因:

    -完全测试比较耗时,时间上不允许;

    -完全测试通常意味着较多资源投入,这在现实中往往是行不通的;

    -输入量太大,不能一一进行测试;

    -输出结果太多,只能分类进行验证;

    -软件实现途径太多;

    -软件产品说明书没有客观标准,从不同的角度看,软件缺陷的标准不同;

    因此测试的程度要根据实际情况确定。

    51、软件测试的风险主要体现在哪里?

    参考答案:

    我们没有对软件进行完全测试,实际就是选择了风险,因为缺陷极有可能存在没有进行测试的部分。举个例子,程序员为了方便,在调试程序时会弹出一些提示信息框,而这些提示只在某种条件下会弹出,碰巧程序发布前这些代码中的一些没有被注释掉。在测试时测试工程师又没有对其进行测试。如果客户碰到它,这将是代价昂贵的缺陷,因为交付后才被客户发现。

    因此,我们要尽可能的选择最合适的测试量,把风险降低到最小。

    52、发现的缺陷越多,说明软件缺陷越多吗?

    参考答案:

    这是一个比较常见的现象。测试工程师在没有找到缺陷前会绞尽脑汁的思考,但是找到一个后,会接二连三的发现很多缺陷,颇有个人成就感。其中的原因主要如下:

    -代码复用、拷贝代码导致程序员容易犯相同的错误。类的继承导致所有的子类会包含基类的错误,反复拷贝同一代码意味可能也复制了缺陷。

    -程序员比较劳累是可以导致某些连续编写的功能缺陷较多。程序员加班是一种司空见惯的现象,因此体力不只时容易编写一些缺陷较多的程序。而这些连续潜伏缺陷恰恰时测试工程师大显身手的地方。

    “缺陷一个连着一个”不是一个客观规律,只是一个常见的现象。如果软件编写的比较好,这种现象就不常见了。测试人员只要严肃认真的测试程序就可以了。

    53、所有的软件缺陷都能修复吗?所有的软件缺陷都要修复吗?

    参考答案:

    从技术上讲,所有的软件缺陷都是能够修复的,但是没有必要修复所有的软件缺陷。测试人员要做的是能够正确判断什么时候不能追求软件的完美。对于整个项目团队,要做的是对每一个软件缺陷进行取舍,根据风险决定那些缺陷要修复。发生这种现象的主要原因如下:

    -没有足够的时间资源。在任何一个项目中,通常情况下开发人员和测试人员都是不够用的,而且在项目中没有预算足够的回归测试时间,再加上修改缺陷可能引入新的缺陷,因此在交付期限的强大压力下,必须放弃某些缺陷的修改。

    -有些缺陷只是特殊情况下出现,这种缺陷处于商业利益考虑,可以在以后升级中进行修复。

    -不是缺陷的缺陷。我们经常会碰到某些功能方面的问题被当成缺陷来处理,这类问题可以以后有时间时考虑再处理。

    最后要说的是,缺陷是否修改要由软件测试人员、项目经理、程序员共同讨论来决定是否修复,不同角色的人员从不同的角度来思考,以做出正确的决定。

    54、软件测试人员就是QA吗?

    参考答案:

    软件测试人员的职责是尽可能早的找出软件缺陷,确保得以修复。而质量保证人员(QA)主要职责是创建或者制定标准和方法,提高促进软件开发能力和减少软件缺陷。测试人员的主要工作是测试,质量保证人员日常工作重要内容是检查与评审,测试工作也是测试保证人员的工作对象。

    软件测试和质量是相辅相成的关系,都是为了提高软件质量而工作。

    55、如何减少测试人员跳槽带来的损失?

    参考答案:

    在IT行业里跳槽已经是一种司空见惯的现象,而且跳槽无论给公司还是给个人都会带来一定的损失。测试队伍也无疑会面临跳槽的威胁,作为测试经理管理者,只有从日常工作中开始做起,最能最大限度的减少损失。建议我们从以下两个方面做起:

    -加强部门内员工之间的互相学习,互相学习是建立学习型组织的基本要求,是知识互相转移的过程。在此基础上,可以把个人拥有的技术以知识的形式沉积下来,也就完成了隐性知识到显性知识的转化。

    -通常情况下,企业能为员工提供足够大的发展空间时,如果不是待遇特别低,员工都不会主动离开企业。因此我们要想留住员工,管理者就应该把员工的个人成长和企业的发展联系起来,为员工设定合理发展规划并付诸实现。不过这项要求做起来比较,要有比较好的企业文化为依托。

    56、测试产品与测试项目的区别是什么?

    参考答案:

    习惯上把开发完成后进行商业化、几乎不进行代码修改就可以售给用户使用的软件成为软件产品,也就是可以买“卖拷贝”的软件,例如Windows2000。而通常把针对一个或者几个特定的用户而开发的软件成为软件项目,软件项目是一种个性化的产品,可以是按照用户要求全部重新开发,也可以修改已有的软件产品来满足特定的用户需求。项目和产品的不同特点,决定我们测试产品和测试项目仍然会有很多不同的地方:

    -质量要求不同。通常产品的质量要高一些,修复发布后产品的缺陷成本较高,甚至会带来很多负面的影响。而做项目通常面向某一用户,虽然质量越高越好,但是一般只要满足用户要求就可以了。

    -测试资源投入多少不同。做软件产品通常是研发中心来开发,进度压力要小些。同时由于质量要求高,因此会投入较多的人力、物力资源。

    -项目最后要和用户共同验收测试,这是产品测试不具有的特点。

    此外,测试产品与测试项目在缺陷管理方面、测试策略制定都会有很大不同,测试管理者应该结合具体的环境,恰如其分的完成工作。

    57、和用户共同测试(UAT测试)的注意点有哪些?

    参考答案:

    软件产品在投产前,通常都会进行用户验收测试。如果用户验收测试没有通过,直接结果就是那不到“Money”,间接影响是损害了公司的形象,而后者的影响往往更严重。根据作者的经验,用户验收测试一定要让用户满意。

    实际上用户现场测试更趋于是一种演示。在不欺骗用户的前提下,我们向用户展示我们软件的优点,最后让“上帝”满意并欣然掏出“银子”才是我们的目标。因此用户测试要注意下面的事项:

    (1)用户现场测试不可能测试全部功能,因此要测试核心功能。这需要提前做好准备,这些核心功能一定要预先经过测试,证明没有问题才可以和用户共同进行测试。测试核心模块的目的是建立用户对软件的信心。当然如果这些模块如果问题较多,不应该进行演示。

    (2)如果某些模块确实有问题,我们可以演示其它重要的业务功能模块,必要时要向用户做成合理的解释。争得时间后,及时修改缺陷来弥补。

    (3)永远不能欺骗用户,蒙混过关。道理很简单,因为软件是要给用户用的,问题早晚会暴露出来,除非你可以马上修改。

    和用户进行测试还要注意各种交流技巧,争取不但短期利益得到了满足,还要为后面得合作打好基础。

    58、如何编写提交给用户的测试报告?

    参考答案:

    随着测试工作越来越受重视,开发团队向客户提供测试文档是不可避免的事情。很多人会问:“我们可以把工作中的测试报告提供给客户吗?”答案是否定的。因为提供内部测试报告,可能会让客户失去信心,甚至否定项目。

    测试报告一般分为内部测试报告和外部测试报告。内部报告是我们在测试工作中的项目文档,反映了测试工作的实施情况,这里不过多讨论,读者可以参考相关教材。这里主要讨论一下外部测试报告的写法,一般外部测试报告要满足下面几个要求:

    -根据内部测试报告进行编写,一般可以摘录;

    -不可以向客户报告严重缺陷,即使是已经修改的缺陷,开发中的缺陷也没有必要让客户知道;

    -报告上可以列出一些缺陷,但必须是中级的缺陷,而且这些缺陷必须是修复的;

    -报告上面的内容尽量要真实可靠;

    -整个测试报告要仔细审阅,力争不给项目带来负面作用,尤其是性能测试报告。

    总之,外部测试报告要小心谨慎的编写。

    59、测试工具在测试工作中是什么地位?

    参考答案:

    国内的很多测试工程师对测试工具相当迷恋,尤其是一些新手,甚至期望测试工具可以取代手工测试。测试工具在测试工作中起的是辅助作用,一般用来提高测试效率。自动化测试弥补了手工测试的不足,减轻一定的工作量。实际上测试工具是无法替代大多数手工测试的,而一些诸如性能测试等自动化测试也是手工所不能完成的。

    对于自动测试技术,应当依据软件的不同情况来分别对待,一般自动技术会应用在引起大量重复性工作的地方、系统的压力点、以及任何适合使用程序解决大批量输入数据的地方。然后再寻找合适的自动测试工具,或者自己开发测试程序。一定不要为了使用测试工具而使用。

    60、什么是软件测试,软件测试的目的?

    参考答案:

    61、简述负载测试与压力测试的区别。

    参考答案:

        压力测试(Stress Testing)

    压力测试的主要任务就是获取系统正确运行的极限,检查系统在瞬间峰值负荷下正确执行的能力。例如,对服务器做压力测试时就可以增加并发操作的用户数量;或者不停地向服务器发送请求;或一次性向服务器发送特别大的数据等。看看服务器保持正常运行所能达到的最大状态。人们通常使用测试工具来完成压力测试,如模拟上万个用户从终端同时登录,这是压力测试中常常使用的方法。

    负载测试(Volume Testing)

    用于检查系统在使用大量数据的时候正确工作的能力,即检验系统的能力最高能达到什么程度。例如,对于信息检索系统,让它使用频率达到最大;对于多个终端的分时系统,让它所有的终端都开动。在使整个系统的全部资源达到“满负荷”的情形下,测试系统的承受能力。

    62、写出bug报告流转的步骤,每步的责任人及主要完成的工作。

    参考答案:(要结合自己实际的工作经验进行回答,不同公司略有区别)

        测试人员提交新的Bug入库,错误状态为New。

    高级测试员/测试经理验证错误,如果确认是错误,分配给开发组。设置状态为Open。如果不是错误,则拒绝,设置为Declined状态。

    开发经理分配bug至对应的模块开发人员。

    开发人员查询状态为Open的Bug,如果不是错误,则置状态为Declined;如果是Bug则修复并置状态为Fixed。不能解决的Bug,要留下文字说明及保持Bug为Open状态。

    对于不能解决和延期解决的Bug,不能由开发人员自己决定,一般要通过某种会议(评审会)通过才能认可。

    测试人员查询状态为Fixed的Bug,然后验证Bug是否已解决,如解决,置Bug的状态为Closed,如没有解决,置bug状态为Reopen。

    63、写出bug报告当中一些必备的内容。

    参考答案:

           硬件平台和操作系统

           测试应用的硬件平台(Platform),通常选择“PC”。

           测试应用的操作系统平台(OS)。

    a)        版本

           提交缺陷报告时通过该字段标识此缺陷存在于被测试软件的哪个版本。

    b)        Bug报告优先级

    c)         Bug状态

    d)        Bug的编号

    e)         发现人

    f)         提交人

    g)        指定处理人

    h)        概述

    i)          从属关系

    j)         详细描述

    k)        严重程度

    l)          所属模块

    m)      附件

    n)        提交日期

    64、开发人员老是犯一些低级错误怎么解决?

    参考答案:

    这种现象在开发流程不规范的团队里特别常见,尤其是一些“作坊式”的团队里。解决这种问题一般从两个方面入手:

    一方面从开发管理入手,也就是从根源来解决问题。可以制定规范的开发流程,甚至可以制定惩罚制度,还有就是软件开发前做好规划设计。

    另一方面就是加强测试,具体做法就是加强开发人员的自己测试,把这些问题“消灭”在开发阶段,这是比较好的做法,读者可以参考第13章试案例分析的“13.1.2缺陷反复出现,谁的责任”小节,13.1.2专门讨论了这类问题的方法。

    此外,还可以通过规范的缺陷管理来对开发人员进行控制,比如测试部门整理出常见的缺陷,让开发人员自己对照进行检查,以减少这类低级错误的发生。

    开发人员犯错误是正常的现象,作为测试人员一定不能抱怨,要认认真真的解决问题才是上策。

    65、画出软件测试的V模型图。

      参考答案:

            

    66、为什么要在一个团队中开展软件测试工作?

    参考答案:

    因为没有经过测试的软件很难在发布之前知道该软件的质量,就好比ISO质量认证一样,测试同样也需要质量的保证,这个时候就需要在团队中开展软件测试的工作。在测试的过程发现软件中存在的问题,及时让开发人员得知并修改问题,在即将发布时,从测试报告中得出软件的质量情况。

    67、您在以往的测试工作中都曾经具体从事过哪些工作?其中最擅长哪部分工作?

    参考答案:(根据项目经验不同,灵活回答即可)

    我曾经做过web测试,后台测试,客户端软件,其中包括功能测试性能测试,用户体验测试。最擅长的是功能测试

    68、您所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)

    参考答案:

    测试类型有:功能测试,性能测试,界面测试。

      功能测试在测试工作中占的比例最大,功能测试也叫黑盒测试。是把测试对象看作一个黑盒子。利用黑盒测试法进行动态测试时,需要测试软件产品的功能,不需测试软件产品的内部结构和处理过程。采用黑盒技术设计测试用例的方法有:等价类划分、边界值分析、错误推测、因果图和综合策略。

      性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。负载测试和压力测试都属于性能测试,两者可以结合进行。通过负载测试,确定在各种工作负载下系统的性能,目标是测试当负载逐渐增加时,系统各项性能指标的变化情况。压力测试是通过确定一个系统的瓶颈或者不能接收的性能点,来获得系统能提供的最大服务级别的测试。

      界面测试,界面是软件与用户交互的最直接的层,界面的好坏决定用户对软件的第一印象。而且设计良好的界面能够引导用户自己完成相应的操作,起到向导的作用。同时界面如同人的面孔,具有吸引用户的直接优势。设计合理的界面能给用户带来轻松愉悦的感受和成功的感觉,相反由于界面设计的失败,让用户有挫败感,再实用强大的功能都可能在用户的畏惧与放弃中付诸东流。

      区别在于,功能测试关注产品的所有功能上,要考虑到每个细节功能,每个可能存在的功能问题。性能测试主要关注于产品整体的多用户并发下的稳定性和健壮性。界面测试更关注于用户体验上,用户使用该产品的时候是否易用,是否易懂,是否规范(快捷键之类的),是否美观(能否吸引用户的注意力),是否安全(尽量在前台避免用户无意输入无效的数据,当然考虑到体验性,不能太粗鲁的弹出警告)?做某个性能测试的时候,首先它可能是个功能点,首先要保证它的功能是没问题的,然后再考虑该功能点的性能测试

    69、您认为做好测试用例设计工作的关键是什么?

    参考答案:

           白盒测试用例设计的关键是以较少的用例覆盖尽可能多的内部程序逻辑结果

    黑盒法用例设计的关键同样也是以较少的用例覆盖模块输出和输入接口。不可能做到完全测试,以最少的用例在合理的时间内发现最多的问题

    70、请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系。

    参考答案:

           黑盒测试:已知产品的功能设计规格,可以进行测试证明每个实现了的功能是否符合要求。

      白盒测试:已知产品的内部工作过程,可以通过测试证明每种内部操作是否符合设计规格要求,所有内部成分是否以经过检查。

      软件的黑盒测试意味着测试要在软件的接口处进行。这种方法是把测试对象看做一个黑盒子,测试人员完全不考虑程序内部的逻辑结构和内部特性,只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明。因此黑盒测试又叫功能测试或数据驱动测试。黑盒测试主要是为了发现以下几类错误:

      1、是否有不正确或遗漏的功能?

      2、在接口上,输入是否能正确的接受?能否输出正确的结果?

      3、是否有数据结构错误或外部信息(例如数据文件)访问错误?

      4、性能上是否能够满足要求?

      5、是否有初始化或终止性错误?

      软件的白盒测试是对软件的过程性细节做细致的检查。这种方法是把测试对象看做一个打开的盒子,它允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。通过在不同点检查程序状态,确定实际状态是否与预期的状态一致。因此白盒测试又称为结构测试或逻辑驱动测试。白盒测试主要是想对程序模块进行如下检查:

      1、对程序模块的所有独立的执行路径至少测试一遍。

      2、对所有的逻辑判定,取“真”与取“假”的两种情况都能至少测一遍。

      3、在循环的边界和运行的界限内执行循环体。

      4、测试内部数据结构的有效性,等等。

      单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。

      单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。

      集成测试(也叫组装测试,联合测试)是单元测试的逻辑扩展。它的最简单的形式是:两个已经测试过的单元组合成一个组件,并且测试它们之间的接口。从这一层意义上讲,组件是指多个单元的集成聚合。在现实方案中,许多单元组合成组件,而这些组件又聚合成程序的更大部分。方法是测试片段的组合,并最终扩展进程,将您的模块与其他组的模块一起测试。最后,将构成进程的所有模块一起测试。

      系统测试是将经过测试的子系统装配成一个完整系统来测试。它是检验系统是否确实能提供系统方案说明书中指定功能的有效方法。(常见的联调测试)

      系统测试的目的是对最终软件系统进行全面的测试,确保最终软件系统满足产品需求并且遵循系统设计。

      验收测试是部署软件之前的最后一个测试操作。验收测试的目的是确保软件准备就绪,并且可以让最终用户将其用于执行软件的既定功能和任务。

    验收测试是向未来的用户表明系统能够像预定要求那样工作。经集成测试后,已经按照设计把所有的模块组装成一个完整的软件系统,接口错误也已经基本排除了,接着就应该进一步验证软件的有效性,这就是验收测试的任务,即软件的功能和性能如同用户所合理期待的那样。

    71、测试计划工作的目的是什么?测试计划工作的内容都包括什么?其中哪些是最重要的?

    参考答案:

           软件测试计划是指导测试过程的纲领性文件,包含了产品概述、测试策略、测试方法、测试区域、测试配置、测试周期、测试资源、测试交流、风险分析等内容。借助软件测试计划,参与测试的项目成员,尤其是测试管理人员,可以明确测试任务和测试方法,保持测试实施过程的顺畅沟通,跟踪和控制测试进度,应对测试过程中的各种变更。

    测试计划和测试详细规格、测试用例之间是战略和战术的关系,测试计划主要从宏观上规划测试活动的范围、方法和资源配置,而测试详细规格、测试用例是完成测试任务的具体战术。所以其中最重要的是测试测试策略和测试方法(最好是能先评审)

    72、您所熟悉的测试用例设计方法都有哪些?请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。

    参考答案:

           1.等价类划分

      划分等价类: 等价类是指某个输入域的子集合.在该子集合中,各个输入数据对于揭露程序中的错误都是等效的.并合理地假定:测试某等价类的代表值就等于对这一类其它值的测试.因此,可以把全部输入数据合理划分为若干等价类,在每一个等价类中取一个数据作为测试的输入条件,就可以用少量代表性的测试数据.取得较好的测试结果.等价类划分可有两种不同的情况:有效等价类和无效等价类.

      2.边界值分析法

      边界值分析方法是对等价类划分方法的补充。测试工作经验告诉我,大量的错误是发生在输入或输出范围的边界上,而不是发生在输入输出范围的内部.因此针对各种边界情况设计测试用例,可以查出更多的错误.

      使用边界值分析方法设计测试用例,首先应确定边界情况.通常输入和输出等价类的边界,就是应着重测试的边界情况.应当选取正好等于,刚刚大于或刚刚小于边界的值作为测试数据,而不是选取等价类中的典型值或任意值作为测试数据.

        3.错误推测法

      基于经验和直觉推测程序中所有可能存在的各种错误, 从而有针对性的设计测试用例的方法.

      错误推测方法的基本思想: 列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据他们选择测试用例. 例如, 在单元测试时曾列出的许多在模块中常见的错误. 以前产品测试中曾经发现的错误等, 这些就是经验的总结. 还有, 输入数据和输出数据为0的情况. 输入表格为空格或输入表格只有一行. 这些都是容易发生错误的情况. 可选择这些情况下的例子作为测试用例.

        4.因果图方法

      前面介绍的等价类划分方法和边界值分析方法,都是着重考虑输入条件,但未考虑输入条件之间的联系, 相互组合等. 考虑输入条件之间的相互组合,可能会产生一些新的情况. 但要检查输入条件的组合不是一件容易的事情, 即使把所有输入条件划分成等价类,他们之间的组合情况也相当多. 因此必须考虑采用一种适合于描述对于多种条件的组合,相应产生多个动作的形式来考虑设计测试用例. 这就需要利用因果图(逻辑模型). 因果图方法最终生成的就是判定表. 它适合于检查程序输入条件的各种组合情况.

    73、请以您以往的实际工作为例,详细的描述一次测试用例设计的完整的过程。

    参考答案:

           就说最近的这次网站功能的测试吧

      首先:得到相关文档(需求文档和设计文档),理解需求和设计设计思想后,想好测试策略(测试计划简单点就OK了),考虑到测试环境,测试用例,测试时间等问题。

      第二步:设计测试用例,测试策略是:把网站部分的功能点测试完,然后在进行系统测试(另外个模块呢有另一个测试人员负责,可以进行联调测试),网站模块的测试基本是功能测试和界面测试(用户并发的可能性很小,所以不考虑):这次的网站的输入数据呢是使用数据库中的某张表记录,如果表中某一数据记录中新加进来的(还没有被处理的,有个标志位),网站启动后会立刻去刷那张表,得到多条数据,然后在进行处理。处理过程中,会经历3个步骤,网站才算完成了它的任务。有3个步骤呢,就可以分别对  这3个步骤进行测试用例的设计,尽量覆盖到各种输入情况(包括数据库中的数据,用户的输入等),得出了差不多50个用例。界面测试,也就是用户看的到的地方,包括发送的邮件和用户填写资料的页面展示。

      第三步:搭建测试环境(为什么这个时候考虑测试环境呢?因为我对网站环境已经很熟了,只有有机器能空于下来做该功能测试就可以做了),因为网站本身的环境搭建和其他的系统有点不同,它需要的测试环境比较麻烦,需要web服务器(Apache,tomcat),不过这次需求呢,网站部分只用到了tomcat,所以只要有tomcat即可

      第四步:执行测试

    74、您以往是否曾经从事过性能测试工作?如果有,请尽可能的详细描述您以往的性能测试工作的完整过程。

    参考答案:(以自己最熟悉的性能测试项目为例)

           是的,曾经做过网站方面的性能测试,虽然做的时间并不久(2个月吧),当时呢,是有位网站性能测试经验非常丰富的前辈带着我一起做。

    性能测试类型包括负载测试,强度测试,容量测试等

      负载测试:负载测试是一种性能测试指数据在超负荷环境中运行,程序是否能够承担。

      强度测试:强度测试是一种性能测试,他在系统资源特别低的情况下软件系统运行情况

      容量测试:确定系统可处理同时在线的最大用户数   

      在网站流量逐渐加大的情况下,开始考虑做性能测试了,首先要写好性能测试计划,根据运营数据得出流量最大的页面(如果是第一次的话,一般是首页,下载页,个人帐户页流量最大,而且以某种百分比),

      Web服务器指标指标:

      * Avg Rps: 平均每秒钟响应次数=总请求时间 / 秒数;

      * Successful Rounds:成功的请求;

      * Failed Rounds :失败的请求;

      * Successful Hits :成功的点击次数;

      * Failed Hits :失败的点击次数;

      * Hits Per Second :每秒点击次数;

      * Successful Hits Per Second :每秒成功的点击次数;

      * Failed Hits Per Second :每秒失败的点击次数;

      * Attempted Connections :尝试链接数; 

    75、你对测试最大的兴趣在哪里?为什么?

    参考答案:

           最大的兴趣就是测试有难度,有挑战性!做测试越久越能感觉到做好测试有多难。曾经在无忧测试网上看到一篇文章,是关于如何做好一名测试工程师。一共罗列了11,12点,有部分是和人的性格有关,有部分需要后天的努力。但除了性格有关的1,2点我没有把握,其他点我都很有信心做好它。

      刚开始进入测试行业时,对测试的认识是从无忧测试网上了解到的一些资料,当时是冲着做测试需要很多技能才能做的好,虽然入门容易,但做好很难,比开发更难,虽然当时我很想做开发(学校专业课我基本上不缺席,因为我喜欢我的专业),但看到测试比开发更难更有挑战性,想做好测试的意志就更坚定了。

      不到一年半的测试工作中,当时的感动和热情没有减退一点(即使环境问题以及自身经验,技术的不足,做测试的你一定也能理解)。

      我觉得做测试整个过程中有2点让我觉得很有难度(对我来说,有难度的东西我就非常感兴趣),第一是测试用例的设计,因为测试的精华就在测试用例的设计上了,要在版本出来之前,把用例写好,用什么测试方法写?(也就是测试计划或测试策略),如果你刚测试一个新任务时,你得花一定的时间去消化业务需求和技术基础,业务需求很好理解(多和产品经理和开发人员沟通就能达到目的),而技术基础可就没那么简单了,这需要你自觉的学习能力,比如说网站吧,最基本的技术知识你要知道网站内部是怎么运作的的,后台是怎么响应用户请求的?测试环境如何搭建?这些都需要最早的学好。至少在开始测试之前能做好基本的准备,可能会遇到什么难题?需求细节是不是没有确定好?这些问题都能在设计用例的时候发现。

      第二是发现BUG的时候了,这应该是测试人员最基本的任务了,一般按测试用例开始测试就能发现大部分的bug,还有一部分bug需要测试的过程中更了解所测版本的情况获得更多信息,补充测试用例,测试出bug。还有如何发现bug?这就需要在测试用例有效的情况下,通过细心和耐心去发现bug了,每个用例都有可能发现bug,每个地方都有可能出错,所以测试过程中思维要清晰(测试过程数据流及结果都得看仔细了,bug都在里面发现的)。如何描述bug也很有讲究,bug在什么情况下会产生,如果条件变化一点点,就不会有这个bug,以哪些最少的操作步骤就能重现这个bug,这个bug产生的规律是什么?如果你够厉害的话,可以帮开发人员初步定位问题。

    76、你以前工作时的测试流程是什么?

    参考答案:(灵活回答)

    公司对测试流程没有规定如何做,但每个测试人员都有自己的一套测试流程。我说下我1年来不断改正(自己总结,吸取同行的方法)后的流程吧。需求评审(有开发人员,产品经理,测试人员,项目经理)->需求确定(出一份确定的需求文档)->开发设计文档(开发人员在开始写代码前就能输出设计文档)->想好测试策略,写出测试用例->发给开发人员和测试经理看看(非正式的评审用例)->接到测试版本->执行测试用例(中间可能会补充用例)->提交bug(有些bug需要开发人员的确定(严重级别的,或突然发现的在测试用例范围之外的,难以重现的),有些可以直接录制进TD)->开发人员修改(可以在测试过程中快速的修改)->回归测试(可能又会发现新问题,再按流程开始跑)。

    77、当开发人员说不是BUG时,你如何应付?

    参考答案:

      开发人员说不是bug,有2种情况,一是需求没有确定,所以我可以这么做,这个时候可以找来产品经理进行确认,需不需要改动,3方商量确定好后再看要不要改。二是这种情况不可能发生,所以不需要修改,这个时候,我可以先尽可能的说出是BUG的依据是什么?如果被用户发现或出了问题,会有什么不良结果?程序员可能会给你很多理由,你可以对他的解释进行反驳。如果还是不行,那我可以给这个问题提出来,跟开发经理和测试经理进行确认,如果要修改就改,如果不要修改就不改。其实有些真的不是bug,我也只是建议的方式写进TD中,如果开发人员不修改也没有大问题。如果确定是bug的话,一定要坚持自己的立场,让问题得到最后的确认。

    78、软件的构造号与版本号之间的区别?BVT(BuildVerificationTest)

    参考答案:版本控制命名格式: 主版本号.子版本号[.修正版本号[.编译版本号 ]]

    Major.Minor [.Revision[.Build]]

          应根据下面的约定使用这些部分:

    Major :具有相同名称但不同主版本号的程序集不可互换。例如,这适用于对产品的大量重写,这些重写使得无法实现向后兼容性。

    Minor :如果两个程序集的名称和主版本号相同,而次版本号不同,这指示显著增强,但照顾到了向后兼容性。例如,这适用于产品的修正版或完全向后兼容的新版本。

    Build :内部版本号的不同表示对相同源所作的重新编译。这适合于更改处理器、平台或编译器的情况。

    Revision :名称、主版本号和次版本号都相同但修订号不同的程序集应是完全可互换的。这适用于修复以前发布的程序集中的安全漏洞。

    BVT(BuildVerificationTest):

    作为Build的一部分,主要是通过对基本功能、特别是关键功能的测试,保证新增代码没有导致功能失效,保证版本的持续稳定。实现BVT方式是有以下几种:1、测试人员手工验证关键功能实现的正确性。特点:这是传统开发方法中,通常采用的方式。无需维护测试脚本的成本,在测试人力资源充足,测试人员熟悉业务、并对系统操作熟练情况下效率很高,比较灵活快速。缺点:人力成本较高;对测试人员能力有一定要求;测试人员面对重复的工作,容易产生疲倦懈怠,从而影响测试质量。2、借助基于GUI的自动化功能测试工具来完成,将各基本功能操作录制成测试脚本,每次回放测试脚本验证功能实现的正确性。特点:能够模拟用户操作完成自动的测试,从UI入口到业务实现,每一层的代码实现都经过验证;节约人力成本;降低测试人员重复劳动的工作量,机器不会疲倦;缺点:对于UI变动比较频繁的系统来说,这种方式的维护成本很高,实施起来非常困难。另外,在项目周期较短且后续无延续性或继承的情况下,也不推荐使用此方式。3、由开发人员通过自动化测试工具完成业务层的BVT测试。特点:通过对业务层关键功能的持续集成测试,保证系统功能的持续稳定。可以结合DailyBuild,做为Build的一部分,自动实现并输入BVT报告。缺点:仅对业务规则实现的正确性进行了测试,对表现层无法测试到,对于诸如:前台页面控件各种事件响应、页面元素变化等方面的问题无法保证。

    79、您以往的工作中,一条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?

    参考答案:

    80、您以往所从事的软件测试工作中,是否使用了一些工具来进行软件缺陷(Bug)的管理?如果有,请结合该工具描述软件缺陷(Bug)跟踪管理的流程。

    参考答案:

    81、您认为性能测试工作的目的是什么?做好性能测试工作的关键是什么?

    参考答案:

    82、单元测试、集成测试、系统测试的侧重点是什么?

           参考答案:

    83、集成测试通常都有那些策略?

    参考答案:

    84、一个缺陷测试报告的组成

    参考答案:

    85、基于WEB信息管理系统测试时应考虑的因素有哪些?

    参考答案:

    86、软件测试项目从什么时候开始,?为什么?

    参考答案:

    87、需求测试注意事项有哪些?

    参考答案:

    88、简述一下缺陷的生命周期

    参考答案:

    89、你在你所在的公司是怎么开展测试工作的?是如何组织的?

    参考答案:

    90、你认为理想的测试流程是什么样子?

    参考答案:

    91、您在从事性能测试工作时,是否使用过一些测试工具?如果有,请试述该工具的工作原理,并以一个具体的工作中的例子描述该工具是如何在实际工作中应用的。

    参考答案:        

    92、软件测试活动的生命周期是什么?

    参考答案:

    93、请画出软件测试活动的流程图?

    参考答案:

    94、针对缺陷采取怎样管理措施?

    参考答案:

    95、什么是测试评估?测试评估的范围是什么?

    参考答案:

    96、如果能够执行完美的黑盒测试,还需要进行白盒测试吗?为什么?

    参考答案:

    97、测试结束的标准是什么?

    参考答案:

    98、软件验收测试除了alpha ,beta测试以外,还有哪一种?

    参考答案:

    99、做测试多久了?以前做过哪些项目?你们以前测试的流程是怎样的?用过哪些测试工具?

    参考答案:

    100、请就如何在开发中进行软件质量控制说说你的看法

    参考答案:

    101、一套完整的测试应该由哪些阶段组成?分别阐述一下各个阶段。

    102、软件测试的类型有那些?分别比较这些不同的测试类型的区别与联系。

    103、测试用例通常包括那些内容?着重阐述编制测试用例的具体做法

    104、在分别测试winform的C/S结构与测试WEB结构的软件是,应该采取什么样的方法分别测试?他们存在什么样的区别与联系?

    105、在测试winform的C/S结构软件时,发现这个软件的运行速度很慢,您会认为是什么原因?您会采取哪些方法去检查这个原因?

    106、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程

    107、你都用什么测试方法

    针对不同的产品或者系统或者模块,有不同的测试方法。总体而言有白盒测试和黑盒测试。

    108、怎么编写案例

    案例的编写与测试阶段的定义有很大的关系。系统测试和unit测试的案例可能不同。总体而言测试案例根据系统的需求而定。

    109、怎么才能够全面的测试到每一个点

    测试的全面性主要需要在设计测试计划的时候考虑,从测试策略,产品需求等等多个角度考虑从而定义全部的测试点。

    110、谈谈软件测试技术,以及如何提高

    111、谈谈软件测试职业发展,以及个人的打算

    112、谈谈软件测试在企业的地位,也可以结合软件生命周期来谈

    113、一般公司里实际的软件测试流程是什么样的?你们公司又是怎样的?

    114、软件工程师要具有那些素质?

    115、你会哪些测试工具?怎么操作?

    116、你能不能说下你的3到5年的职业计划(规划)

    117、你觉得你来应聘有那些优势?

    其他问题:(有可能清晰的思路比确切的答案更重要)

    对测试的理解——考查点:基本的测试知识,对测试是否认可

    谈一谈过去自己的工作——考查点:了解经历、提供进一步提问的素材,表达能力、测试技能

    测试设计的方法并举例说明——考查点:测试技术的使用

    测试工具——考查点:熟悉程度,能否与当前工作匹配?

    如何做计划?如何跟踪计划?——考查点:日常工作能力

    如果开发人员提供的版本不满足测试的条件,如何做?——考查点:与开发人员协作的能力

    熟悉unix系统、oracle数据库吗?——考查点:是否具备系统知识

    做过开发吗?写过哪些代码?——考查点:开发技能

    阅读英语文章,给出理解说明?——考查点:部分英语能力

    文档的意义——考查点:是否善于思考?(最简单的概念,不同层次的理解)

    假如进入我们公司,对我们哪些方面会有帮助?——考查点:讲讲自己的特长

    随便找一件物品,让其测试——考查点:测试的实际操作能力

    有一个新的软件,假如你是测试工程师,该如何做——考查点:实际项目经验、是否有带领测试团队的经验和潜力

    开发及环境搭建类面试题

    1、描述软件产生内存泄露的原因以及检查方式。(可以结合一种开发语言进行描述)

    参考答案:

    内存泄露的原因,主要是由于开发过程当中申请了计算机资源(例如对象、内存等),但是使用资源完成以后没有及时释放资源导致的。例如在C语言当中使用了malloc申请了内存,但是未使用free来释放内存。

    2、简述什么是值传递,什么是地址传递,两者区别是什么?

    参考答案:

    值传递主调函数传递给被调函数的是值的拷贝,不是原值;地址传递主调函数传递给被调函数的是值的地址。区别是值传递被调函数中的操作不改变主调函数的值,而地址传递则不同。

    3、结构化程序设计和面向对象程序设计各自的特点及优缺点是什么?

    参考答案:(不需要回答如此复杂)

    结构化程序设计思想采用了模块分解与功能抽象和自顶向下、分而治之的方法,从而有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子程序,便于开发和维护。它的重点在于把功能进行分解。但是由于在实际开发过程当中需求会经常发生变化,因此,它不能很好的适应需求变化的开发过程。结构化程序设计是面向过程的。

    面向对象程序设计以需求当中的数据作为中心,来进行设计,具有良好的代码重用性。

    封装性:也叫数据隐藏,用户无需知道内部工作流程,只要知道接口和操作就可以的,C++中一般用类来实现封装。

    继承性:一种支持重用的思想,在现有的类型派生出新的子类,例如新型电视机在原有型号的电视机上增加若干中功能而得到,新型电视机是原有电视机的派生,继承了原有电视机的属性,并增加了新的功能。

    多态性:指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。

    动态联编:指一个计算机程序自身彼此关联的过程,按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。

    4、简述什么是存储过程和触发器?

    参考答案:

    存储过程:是数据库中的一个对象,Transact-SQL 语句的预编译集合,这些语句在一个名称下存储并作为一个单元进行处理。(可以理解为C语言中的函数,有参数、返回值等函数特性)

    触发器是一种特殊类型的存储过程,当使用下面的一种或多种数据修改操作在指定表中对数据进行修改时,触发器会生效:UPDATE、INSERT 或 DELETE。

    5、使用C语言编写一个函数,用于交换两个变量的值(地址传递)。

           参考答案:

           void Swap(int *a,int *b)

    {

                  int temp;

                  int temp=*a;

                  int *a=*b;

                  int *b=temp;

    }

    6、请简述DNS、活动目录、域的概念。

    参考答案:

    DNS:域名服务,作用是将网络域名解析成IP地址;

    活动目录:微软提供的目录服务的一种,它存储有关网络上的对象信息,并使管理员和用户更方便的查找和使用这类信息;

    域:网络系统的一个安全边界,在一个域当中,计算机和用户共享一些列的安全信息。

    7、描述TCP/IP协议的层次结构,以及每一层中重要协议。

    参考答案:(可以回答五层结构)

    TCP/IP

    协议

    应用层/Application

    HTTP、SMTP、FTP

    传输层/Transport

    TCP、UDP

    网络层/Network

    IP

    链路层/Link

    ARP、RARP

          

     

     

     

     

     

    8、简述子网掩码的用途。

    参考答案:

        子网掩码主要用来判断两个IP地址是否处在同一个局域网当中;子网掩码是由连续的2进制1组成的。子网掩码和IP地址进行按位与运算后,结果一致,表示处于一个局域网当中,如果不一致,表示不再一个局域网当中,需要寻找路由。

    9、说出4种以上常用的操作系统及其主要的应用范围(微软的操作系统除外)。

    参考答案:

    Linux(Red Hat、SUSE、Debian、Trubo Linux):主要用于搭建各类服务器

    MAC OS:苹果机的操作系统,用于图像处理

    Unix(AIX:IBM服务器的专用操作系统;

    Solaris:Sun操作系统;FreeBSD、NetBSD)

    10、在Linux系统中,一个文件的访问权限是755,其含义是什么?

    参考答案:

           755表示该文件所有者对该文件具有读、写、执行权限,该文件所有者所在组用户及其他用户对该文件具有读和执行权限。

    11、Windows操作系统中PATH环境变量的作用是什么?

    参考答案:

           PATH是Windows操作系统环境变量,PATH作用是用户在命令行窗口执行一个命令,则在PATH变量设置的目录下依次寻找该命令或对应的执行文件,若找到,则执行,若没有找到,则命令行窗口返回无效命令。

    12、Ghost的主要用途和常用方法?

    参考答案:

    Ghost是一个非常著名的硬盘克隆工具。该工具的主要作用是可以将一个硬盘或硬盘中的某个分区原封不动的复制到另一个硬盘或其他的分区中。如果你需要备份启动分区或者是需要在多台机器上安装相应的系统和应用程序,都可以通过Ghost来实现,相信通过这个工具备份,恢复速度和硬盘安装速度会成倍的提高。

    Norton Ghost有一个很大的特点,就是在克隆硬盘时不会改变任何文件信息,程序可以很好的支持FAT16、FAT32以及NTFS格式的文件分配结构(其中包括Windows 2000的文件分配格式),虽然是DOS环境下运行的程序,但工具可支持Win 9x的长文件名特性。

    常用方法包括:硬盘克隆、分区克隆、硬盘或分区克隆成镜像文件等。

    13、在RedHat中,从root用户切到userl用户,一般用什么命令?

    参考答案:su

    su user1  切换到user1,但切换后的当前目录还是root访问的目录

    su – user1 切换到user1,并且当前目录切换到user1的根目录下(/home/user1/)

    14、Linux中,一般怎么隐藏文件?

    参考答案:文件名以一个.开头

    15、如何将自己的本地磁盘(D)做成FTP供远端主机使用?

    参考答案:Windows下安装FTP服务,并将FTP的根目录指向D盘即可。

    16、对RUP.CMM,CMMI,XP,PSP.TSP的认识?

    参考答案:软件过程标准:CMMI、PSP、TSP、RUP、软件工程规范国家标准;(AP、XP、ASD等开发过程思想好像还不能称其为标准)

    RUP(Rational Unified Process)是Rational公司提出的一套开发过程模型,它是一个面向对象软件工程的通用业务流程。它描述了一系列相关的软件工程流程,它们具有相同的结构,即相同的流程构架。RUP 为在开发组织中分配任务和职责提供了一种规范方法,其目标是确保在可预计的时间安排和预算内开发出满足最终用户需求的高品质的软件。RUP具有两个轴,一个轴是时间轴,这是动态的。另一个轴是工作流轴,这是静态的。在时间轴上,RUP划分了四个阶段:初始阶段、细化阶段、构造阶段和发布阶段。每个阶段都使用了迭代的概念。在工作流轴上,RUP设计了六个核心工作流程和三个核心支撑工作流程,核心工作流轴包括:业务建模工作流、需求工作流、分析设计工作流、实现工作流、测试工作流和发布工作流。核心支撑工作流包括:环境工作流、项目管理工作流和配置与变更管理工作流。RUP 汇集现代软件开发中多方面的最佳经验,并为适应各种项目及组织的需要提供了灵活的形式。作为一个商业模型,它具有非常详细的过程指导和模板。但是同样由于该模型比较复杂,因此在模型的掌握上需要花费比较大的成本。尤其对项目管理者提出了比较高的要求。

    CMM(Capability Maturity Model能力成熟度模型) 由美国卡内基-梅隆大学的软件工程研究所(简称SEI)受美国国防部委托,于1991年研究制定,初始的主要目的是为了评价美国国防部的软件合同承包组织的能力,后因为在软件企业应用CMM模型实施过程改进取得较大的成功,所以在全世界范围内被广泛使用,SEI同时建立了主任评估师评估制度,CMM的评估方法为CBA-IPI。CMM的本质是软件管理工程的一个部分。它是对于软件组织在定义,实现,度量,控制和改善其软件过程的进程中各个发展阶段的描述。他通过5个不断进化的层次来评定软件生产的历史与现状:初始层是混沌的过程;可重复层是经过训练的软件过程;定义层是标准一致的软件过程;管理层是可预测的软件过程;优化层是能持续改善的软件过程。

    CMM/PSP/TSP即软件能力成熟度模型/ 个体软件过程/群组软件过程,是1987年美国 Carnegie Mellon 大学软件工程研究所(CMU/SEI)以W.S.Humphrey为首的研究组发表的研究成果"承制方软件工程能力的评估方法"。

    CMMI是SEI于2000年发布的CMM的新版本。CMMI不但包括了软件开发过程改进,还包含系统集成、软硬件采购等方面的过程改进内容。

    CMMI纠正了CMM存在的一些缺点,使其更加适用企业的过程改进实施。CMMI适用SCAMPI评估方法。需要注意的是,SEI没有废除CMM模型,只是停止了CMM评估方法:CBA-IPI。现在如要进行CMM评估,需使用SCAMPI方法。但CMMI模型最终代替CMM模型的趋势不可避免。

    XP (极限编程)规定了一组核心价值和方法,可以让软件开发人员发挥他们的专长:编写代码。XP 消除了大多数重量型过程的不必要产物,通过减慢开发速度、耗费开发人员的精力(例如干特图、状态报告,以及多卷需求文档)从目标偏离。

    XP 的核心价值:交流、简单、反馈、勇气。

    17、DNS是什么,它是如何工作的?

    参考答案:域名解析服务。用于将域名解析为IP,或反和将IP解析为域名。

    客户机可指定DNS服务器来解析,或用本机hosts文件进行解析。

    Windows下配置DNS服务器在《搭建Windows测试环境》中有。

    18、防火墙如何保证安全的?主要有哪些?

    参考答案:防火墙分类1

    从防火墙的软、硬件形式来分的话,防火墙可以分为软件防火墙和硬件防火墙以及芯片级防火墙。

    第一种:软件防火墙

    软件防火墙运行于特定的计算机上,它需要客户预先安装好的计算机操作系统的支持,一般来说这台计算机就是整个网络的网关。俗称“个人防火墙”。软件防火墙就像其它的软件产品一样需要先在计算机上安装并做好配置才可以使用。防火墙厂商中做网络版软件防火墙最出名的莫过于Checkpoint。使用这类防火墙,需要网管对所工作的操作系统平台比较熟悉。

    第二种:硬件防火墙

    这里说的硬件防火墙是指“所谓的硬件防火墙”。之所以加上"所谓"二字是针对芯片级防火墙说的了。它们最大的差别在于是否基于专用的硬件平台。目前市场上大多数防火墙都是这种所谓的硬件防火墙,他们都基于PC架构,就是说,它们和普通的家庭用的PC没有太大区别。在这些PC架构计算机上运行一些经过裁剪和简化的操作系统,最常用的有老版本的Unix、Linux和FreeBSD系统。值得注意的是,由于此类防火墙采用的依然是别人的内核,因此依然会受到OS(操作系统)本身的安全性影响。

    传统硬件防火墙一般至少应具备三个端口,分别接内网,外网和DMZ区(非军事化区),现在一些新的硬件防火墙往往扩展了端口,常见四端口防火墙一般将第四个端口做为配置口、管理端口。很多防火墙还可以进一步扩展端口数目。

    第三种:芯片级防火墙

    芯片级防火墙基于专门的硬件平台,没有操作系统。专有的ASIC芯片促使它们比其他种类的防火墙速度更快,处理能力更强,性能更高。做这类防火墙最出名的厂商有NetScreen、FortiNet、Cisco等。这类防火墙由于是专用OS(操作系统),因此防火墙本身的漏洞比较少,不过价格相对比较高昂。

    防火墙技术虽然出现了许多,但总体来讲可分为“包过滤型”和“应用代理型”两大类。前者以以色列的Checkpoint防火墙和美国Cisco公司的PIX防火墙为代表,后者以美国NAI公司的Gauntlet防火墙为代表。

    (1). 包过滤(Packet filtering)型

    包过滤型防火墙工作在OSI网络参考模型的网络层和传输层,它根据数据包头源地址,目的地址、端口号和协议类型等标志确定是否允许通过。只有满足过滤条件的数据包才被转发到相应的目的地,其余数据包则被从数据流中丢弃。

    包过滤方式是一种通用、廉价和有效的安全手段。之所以通用,是因为它不是针对各个具体的网络服务采取特殊的处理方式,适用于所有网络服务;之所以廉价,是因为大多数路由器都提供数据包过滤功能,所以这类防火墙多数是由路由器集成的;之所以有效,是因为它能很大程度上满足了绝大多数企业安全要求。

    在整个防火墙技术的发展过程中,包过滤技术出现了两种不同版本,称为“第一代静态包过滤”和“第二代动态包过滤”。

    ●第一代静态包过滤类型防火墙

    这类防火墙几乎是与路由器同时产生的,它是根据定义好的过滤规则审查每个数据包,以便确定其是否与某一条包过滤规则匹配。过滤规则基于数据包的报头信息进行制订。报头信息中包括IP源地址、IP目标地址、传输协议(TCP、UDP、ICMP等等)、TCP/UDP目标端口、ICMP消息类型等。

    ●第二代动态包过滤类型防火墙

    这类防火墙采用动态设置包过滤规则的方法,避免了静态包过滤所具有的问题。这种技术后来发展成为包状态监测(Stateful Inspection)技术。采用这种技术的防火墙对通过其建立的每一个连接都进行跟踪,并且根据需要可动态地在过滤规则中增加或更新条目。

    包过滤方式的优点是不用改动客户机和主机上的应用程序,因为它工作在网络层和传输层,与应用层无关。但其弱点也是明显的:过滤判别的依据只是网络层和传输层的有限信息,因而各种安全要求不可能充分满足;在许多过滤器中,过滤规则的数目是有限制的,且随着规则数目的增加,性能会受到很大地影响;由于缺少上下文关联信息,不能有效地过滤如UDP、RPC(远程过程调用)一类的协议;另外,大多数过滤器中缺少审计和报警机制,它只能依据包头信息,而不能对用户身份进行验证,很容易受到“地址欺骗型”攻击。对安全管理人员素质要求高,建立安全规则时,必须对协议本身及其在不同应用程序中的作用有较深入的理解。因此,过滤器通常是和应用网关配合使用,共同组成防火墙系统。

     (2). 应用代理(Application Proxy)型

    应用代理型防火墙是工作在OSI的最高层,即应用层。其特点是完全"阻隔"了网络通信流,通过对每种应用服务编制专门的代理程序,实现监视和控制应用层通信流的作用。其典型网络结构如图所示。

    在代理型防火墙技术的发展过程中,它也经历了两个不同的版本,即:第一代应用网关型代理防火和第二代自适应代理防火墙。

    第一代应用网关(Application Gateway)型防火墙

    这类防火墙是通过一种代理(Proxy)技术参与到一个TCP连接的全过程。从内部发出的数据包经过这样的防火墙处理后,就好像是源于防火墙外部网卡一样,从而可以达到隐藏内部网结构的作用。这种类型的防火墙被网络安全专家和媒体公认为是最安全的防火墙。它的核心技术就是代理服务器技术。

    第二代自适应代理(Adaptive proxy)型防火墙

    它是近几年才得到广泛应用的一种新防火墙类型。它可以结合代理类型防火墙的安全性和包过滤防火墙的高速度等优点,在毫不损失安全性的基础之上将代理型防火墙的性能提高10倍以上。组成这种类型防火墙的基本要素有两个:自适应代理服务器(Adaptive Proxy Server)与动态包过滤器(Dynamic Packet filter)。

    在“自适应代理服务器”与“动态包过滤器”之间存在一个控制通道。在对防火墙进行配置时,用户仅仅将所需要的服务类型、安全级别等信息通过相应Proxy的管理界面进行设置就可以了。然后,自适应代理就可以根据用户的配置信息,决定是使用代理服务从应用层代理请求还是从网络层转发包。如果是后者,它将动态地通知包过滤器增减过滤规则,满足用户对速度和安全性的双重要求。

    代理类型防火墙的最突出的优点就是安全。由于它工作于最高层,所以它可以对网络中任何一层数据通信进行筛选保护,而不是像包过滤那样,只是对网络层的数据进行过滤。

    另外代理型防火墙采取是一种代理机制,它可以为每一种应用服务建立一个专门的代理,所以内外部网络之间的通信不是直接的,而都需先经过代理服务器审核,通过后再由代理服务器代为连接,根本没有给内、外部网络计算机任何直接会话的机会,从而避免了入侵者使用数据驱动类型的攻击方式入侵内部网。

    代理防火墙的最大缺点就是速度相对比较慢,当用户对内外部网络网关的吞吐量要求比较高时,代理防火墙就会成为内外部网络之间的瓶颈。那因为防火墙需要为不同的网络服务建立专门的代理服务,在自己的代理程序为内、外部网络用户建立连接时需要时间,所以给系统性能带来了一些负面影响,但通常不会很明显。

    防火墙分类3

    从防火墙结构上分,防火墙主要有:单一主机防火墙、路由器集成式防火墙和分布式防火墙三种。

    单一主机防火墙是最为传统的防火墙,独立于其它网络设备,它位于网络边界。

    这种防火墙其实与一台计算机结构差不多(如下图),同样包括CPU、内存、硬盘等基本组件,当然主板更是不能少了,且主板上也有南、北桥芯片。它与一般计算机最主要的区别就是一般防火墙都集成了两个以上的以太网卡,因为它需要连接一个以上的内、外部网络。其中的硬盘就是用来存储防火墙所用的基本程序,如包过滤程序和代理服务器程序等,有的防火墙还把日志记录也记录在此硬盘上。虽然如此,但我们不能说它就与我们平常的PC机一样,因为它的工作性质,决定了它要具备非常高的稳定性、实用性,具备非常高的系统吞吐性能。正因如此,看似与PC机差不多的配置,价格甚远。

    随着防火墙技术的发展及应用需求的提高,原来作为单一主机的防火墙现在已发生了许多变化。最明显的变化就是现在许多中、高档的路由器中已集成了防火墙功能,还有的防火墙已不再是一个独立的硬件实体,而是由多个软、硬件组成的系统,这种防火墙,俗称“分布式防火墙”。

    原来单一主机的防火墙由于价格非常昂贵,仅有少数大型企业才能承受得起,为了降低企业网络投资,现在许多中、高档路由器中集成了防火墙功能。如Cisco IOS防火墙系列。但这种防火墙通常是较低级的包过滤型。这样企业就不用再同时购买路由器和防火墙,大大降低了网络设备购买成本。

    分布式防火墙再也不是只是位于网络边界,而是渗透于网络的每一台主机,对整个内部网络的主机实施保护。在网络服务器中,通常会安装一个用于防火墙系统管理软件,在服务器及各主机上安装有集成网卡功能的PCI防火墙卡,这样一块防火墙卡同时兼有网卡和防火墙的双重功能。这样一个防火墙系统就可以彻底保护内部网络。各主机把任何其它主机发送的通信连接都视为“不可信”的,都需要严格过滤。而不是传统边界防火墙那样,仅对外部网络发出的通信请求“不信任”。

    防火墙分类4

    如果按防火墙的应用部署位置分,可以分为边界防火墙、个人防火墙和混合防火墙三大类。

    边界防火墙是最为传统的那种,它们于内、外部网络的边界,所起的作用的对内、外部网络实施隔离,保护边界内部网络。这类防火墙一般都是硬件类型的,价格较贵,性能较好。

    个人防火墙安装于单台主机中,防护的也只是单台主机。这类防火墙应用于广大的个人用户,通常为软件防火墙,价格最便宜,性能也最差。

    混合式防火墙可以说就是“分布式防火墙”或者“嵌入式防火墙”,它是一整套防火墙系统,由若干个软、硬件组件组成,分布于内、外部网络边界和内部各主机之间,既对内、外部网络之间通信进行过滤,又对网络内部各主机间的通信进行过滤。它属于最新的防火墙技术之一,性能最好,价格也最贵。

    防火墙分类5

    如果按防火墙的性能来分可以分为百兆级防火墙和千兆级防火墙两类。

    因为防火墙通常位于网络边界,所以不可能只是十兆级的。这主要是指防火的通道带宽(Bandwidth),或者说是吞吐率。当然通道带宽越宽,性能越高,这样的防火墙因包过滤或应用代理所产生的延时也越小,对整个网络通信性能的影响也就越小。

    19、目前流行的操作的系统有哪些?请举例说明安装操作系统的注意事项?

    参考答案:MS Windows系列:win 98、windows 2000系列、win XP、win 2003 Server、win Vista等等。

    UNIX类:SVRx、FreeBSD、OpenBSD、NetBSD、Solaris、各种Linux等等。Mac OS……

    多重引导时,一般先安装win操作系统,从低版本到高,再安装Linux

    20、简述一下c/s模式或者b/s模式?

    参考答案:C/S模式:客户端/服务器模式。工作原理:Client向Server提交一个请求;Server则使用一些方法处理这个请求,并将效果返回给Client。

    B/S结构,即Browser/Server(浏览器/服务器)结构,是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现,形成所谓3-tier结构。B/S结构,主要是利用了不断成熟的WWW浏览器技术,结合浏览器的多种Script语言(VBScript、JavaScript…)和ActiveX技术,用通用浏览器就实现了原来需要复杂专用软件才能实现的强大功能,并节约了开发成本,是一种全新的软件系统构造技术。

    21、TCP/UDP有哪些区别?

    参考答案:TCP-有连接,所以握手过程会消耗资源,过程为可靠连接,不会丢失数据,适合大数据量交换
    UDP-非可靠连接,会丢包,没有校验,速度快,无须握手过程

     
    TCP
    UDP
    是否连接
    面向连接
    面向非连接
    传输可靠性
    可靠的
    不可靠的
    应用场合
    传输大量数据
    少量数据
    速度

    22、ISO模型?HUB、tch、Router是ISO的第几层设备?

    参考答案:从底向上:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层

    HUB:1层(物理层);Switch:2层(数据链路层);Router:3层(网络层)

    23、内存有哪几种存储组织结构.请分别加以说明?

    参考答案:

    人力资源面试题

    1、你的测试职业发展是什么?你自认为做测试的优势在哪里?

    参考答案:

           测试经验越多,测试能力越高。所以我的职业发展是需要时间累积的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年累积测试经验,按如何做好测试工程师的要求自己,不断的更新自己改正自己,做好测试任务。

      优势在于我对测试坚定不移的信心和热情,虽然经验还不够,但测试需要的基本技能我有信心在工作中得以发挥。

    2、你为什么想离开目前的职务?

    参考答案:

    3、你对我们公司了解有多少?

    参考答案:

    4、你找工作时,最重要的考虑因素为何?

    参考答案:工作的性质和内容是否能让我发挥所长,并不断成长。

    5、为什么我们应该录取你?

    参考答案:您可以由我过去的工作表现所呈现的客观数据,明显地看出我全力以赴的工作态度。

    6、请谈谈你个人的最大特色。

    参考答案:我的坚持度很高,事情没有做到一个令人满意的结果,绝不罢手。

    7、一个测试工程师应具备那些素质和技能?

    参考答案:

    8、您认为在测试人员同开发人员的沟通过程中,如何提高沟通的效率和改善沟通的效果?维持测试人员同开发团队中其他成员良好的人际关系的关键是什么?

    参考答案:

    9、在您以往的测试工作中,最让您感到不满意或者不堪回首的事情是什么?您是如何来对待这些事情的?

    参考答案:

    10、在即将完成这次笔试前,您是否愿意谈一些自己在以往的学习和工作中获得的工作经验和心得体会?(可以包括软件测试、过程改进、软件开发或者与此无关的其他方面)

    参考答案:

    11、为什么选择测试这行?

    参考答案:

      它是一个新兴的行业,有发展潜力,而且很锻炼人,需要掌握更多的技能,比做开发要更难

      为什么值得他们公司雇用?如果我雇用你,你能给部门带来什么贡献?

        如果明知这样做不对,你还会依主管的指过去做吗

      如果你接到一个客户抱怨的电话,你确知无法解决他的问题,你会怎么处理

      你觉得什么样的人最难相处

      为什么值得他们公司雇用?

        帮助公司提高软件质量和测试部门的技术水平

      如果我雇用你,你能给部门带来什么贡献?

        分享我的测试经验和测试技能,提高测试部门技术水平

      如何从工作中看出你是个自动自觉的人

            自动自觉范围太广

         1. 工作成果

         2. 工作质量  

    12、你的工作通常能在时限内完成吗.(我想问一下就是她问这个问题的动机是什么)

    参考答案:

        在有足够的资源和合理的工作量的情况下,完全可以按时完成,并能比一般人做的更好

    13、通常你对于别人批评你会有什么样的反应

    参考答案:有错即改,无错勉之

    14、如果明知这样做不对,你还会依主管的指过去做吗?

           参考答案:

    15、如果你接到一个客户抱怨的电话,你确知无法解决他的问题,你会怎么处理?

    参考答案:

        弄清楚客户为什么抱怨?是怎么样的问题?

      如果是客服问题,提交客服部门解决

      如果是质量问题,分析原因,下一版本改进

    16、请就软件测试人员应该具备什么样的基本素质说说你的看法。

    参考答案:

    17、你在五年内的个人目标和职业目标分别是什么?

    参考答案:

    分析这个问题是用来了解你的计划能力的,通过这个问题,面试人同时还可以知道你的目标是否符合企业对你的安排。

      错误回答我想在将来的某个时候考虑这个问题。如今企业的领导者更换频繁,我认为做太多的个人计划是荒谬可笑的,不是吗?

      评论这种回答属于令人反感的一类。首先,当有人想了解你的目标时,"将来的某个时候"这种通俗说法并不奏效。其次,认为企业很脆弱,领导者更换频繁,这种说法毫无疑问会令人反感,而且也是不合理的。最后,认为做计划可笑,看不起这个问题,而且反问面试人,这些都注定了这样的求职者最终会失败。

      正确回答从现在起的五年之内,我希望能够在一个很好的职位上待几年,而且最好有一次晋升,然后就期待着下一步。不管是向上提升,还是在企业内横向调动,对我个人来说,我希望找到一家企业——一家愿意做相互投入的企业——待上一段时间。

      评论这个问题没有回答得过分具体(那样可能会产生漏洞),而且它表明你有雄心,并且思考过在企业中的成长方式。通过表达横向调动和向上提升的愿望,表明你是一个有灵活性的人。

    18、你怎样做出自己的职业选择?

    参考答案:

           分析 面试人提出这个问题是为了了解求职者的动机,看看他(她)应聘这份工作是否有什么历史渊源,是否有职业规划,是不是仅仅在漫无目的地申请很多工作。

      错误回答 我一直都想在企业界工作。自孩提时代起,我就梦想自己至少也要成为大企业的副总裁。

      评论 除了难以令人相信之外,这种回答还存在一个问题:它表明求职者会对副总裁以下的职位不感兴趣。

      正确回答 在上大学四年级前的那个夏天,我决定集中精力在某一领域谋求发展。尽管我是学商业的,但是我不知道自己最终会从事哪一行业的工作。我花了一定的时间考虑自己的目标,想清楚了自己擅长做的事情以及想从工作中得到的东西,最后我得出了一个坚定的结论,那就是这个行业是最适合我的。

      评论 这种回答表明,求职者认真地做过一些计划,缩小了自己的关注点,而且也认准了前进的方向。这种回答还表明,求职者理解个人职业规划的重要性,并且有能力做出认真的个人决策。

     

     

    展开全文
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    C#语言基础 本介绍C#语言的基础知识,希望具有C语言的读者能够基本掌握C#语言,并以此为基础,能够进一步学习用C#语言编写window应用程序和Web应用程序。当然仅靠一的内容就完全掌握C#语言是不可能的,...
    C#基础教程-c#实例教程,适合初学者。
    
    第一章 C#语言基础
    本章介绍C#语言的基础知识,希望具有C语言的读者能够基本掌握C#语言,并以此为基础,能够进一步学习用C#语言编写window应用程序和Web应用程序。当然仅靠一章的内容就完全掌握C#语言是不可能的,如需进一步学习C#语言,还需要认真阅读有关C#语言的专著。
    1.1 C#语言特点
    Microsoft.NET(以下简称.NET)框架是微软提出的新一代Web软件开发模型,C#语言是.NET框架中新一代的开发工具。C#语言是一种现代、面向对象的语言,它简化了C++语言在类、命名空间、方法重载和异常处理等方面的操作,它摒弃了C++的复杂性,更易使用,更少出错。它使用组件编程,和VB一样容易使用。C#语法和C++和JAVA语法非常相似,如果读者用过C++和JAVA,学习C#语言应是比较轻松的。
    用C#语言编写的源程序,必须用C#语言编译器将C#源程序编译为中间语言(MicroSoft Intermediate Language,MSIL)代码,形成扩展名为exe或dll文件。中间语言代码不是CPU可执行的机器码,在程序运行时,必须由通用语言运行环境(Common Language Runtime,CLR)中的既时编译器(JUST IN Time,JIT)将中间语言代码翻译为CPU可执行的机器码,由CPU执行。CLR为C#语言中间语言代码运行提供了一种运行时环境,C#语言的CLR和JAVA语言的虚拟机类似。这种执行方法使运行速度变慢,但带来其它一些好处,主要有:
     通用语言规范(Common Language Specification,CLS):.NET系统包括如下语言:C#、C++、VB、J#,他们都遵守通用语言规范。任何遵守通用语言规范的语言源程序,都可编译为相同的中间语言代码,由CLR负责执行。只要为其它操作系统编制相应的CLR,中间语言代码也可在其它系统中运行。
     自动内存管理:CLR内建垃圾收集器,当变量实例的生命周期结束时,垃圾收集器负责收回不被使用的实例占用的内存空间。不必象C和C++语言,用语句在堆中建立的实例,必须用语句释放实例占用的内存空间。也就是说,CLR具有自动内存管理功能。
     交叉语言处理:由于任何遵守通用语言规范的语言源程序,都可编译为相同的中间语言代码,不同语言设计的组件,可以互相通用,可以从其它语言定义的类派生出本语言的新类。由于中间语言代码由CLR负责执行,因此异常处理方法是一致的,这在调试一种语言调用另一种语言的子程序时,显得特别方便。
     增加安全:C#语言不支持指针,一切对内存的访问都必须通过对象的引用变量来实现,只允许访问内存中允许访问的部分,这就防止病毒程序使用非法指针访问私有成员。也避免指针的误操作产生的错误。CLR执行中间语言代码前,要对中间语言代码的安全性,完整性进行验证,防止病毒对中间语言代码的修改。
     版本支持:系统中的组件或动态联接库可能要升级,由于这些组件或动态联接库都要在注册表中注册,由此可能带来一系列问题,例如,安装新程序时自动安装新组件替换旧组件,有可能使某些必须使用旧组件才可以运行的程序,使用新组件运行不了。在.NET中这些组件或动态联接库不必在注册表中注册,每个程序都可以使用自带的组件或动态联接库,只要把这些组件或动态联接库放到运行程序所在文件夹的子文件夹bin中,运行程序就自动使用在bin文件夹中的组件或动态联接库。由于不需要在注册表中注册,软件的安装也变得容易了,一般将运行程序及库文件拷贝到指定文件夹中就可以了。
     完全面向对象:不象C++语言,即支持面向过程程序设计,又支持面向对象程序设计,C#语言是完全面向对象的,在C#中不再存在全局函数、全局变量,所有的函数、变量和常量都必须定义在类中,避免了命名冲突。C#语言不支持多重继承。
    1.2 编写控制台应用程序
    使用SDK命令行工具编写控制台程序
    第一个程序总是非常简单的,程序首先让用户通过键盘输入自己的名字,然后程序在屏幕上打印一条欢迎信息。程序的代码是这样的:
    using System;//导入命名空间。//为C#语言新增解释方法,解释到本行结束
    class Welcome//类定义,类的概念见下一节
    { /*解释开始,和C语言解释用法相同
    解释结束*/
    static void Main()//主程序,程序入口函数,必须在一个类中定义
    { Console.WriteLine("请键入你的姓名:");//控制台输出字符串
    Console.ReadLine();//从键盘读入数据,输入回车结束
    Console.WriteLine("欢迎!");
    }
    }
    可以用任意一种文本编辑软件完成上述代码的编写,然后把文件存盘,假设文件名叫做welcome.cs,C#源文件是以cs作为文件的扩展名。和C语言相同,C#语言是区分大小写的。高级语言总是依赖于许多在程序外部预定义的变量和函数。在C或C++中这些定义一般放到头文件中,用#include语句来导入这个头文件。而在C#语言中使用using语句导入名字空间,using System语句意义是导入System名字空间,C#中的using语句的用途与C++中#include语句的用途基本类似,用于导入预定义的变量和函数,这样在自己的程序中就可以自由地使用这些变量和函数。如果没有导入名字空间的话我们该怎么办呢?程序还能保持正确吗?答案是肯定的,那样的话我们就必须把代码改写成下面的样子:
    class Welcome
    { static void Main()
    { System.Console.WriteLine("请键入你的姓名:");
    System.Console.ReadLine();
    System.Console.WriteLine("欢迎!");
    }
    }
    也就是在每个Console前加上一个前缀System.,这个小原点表示Console是作为System的成员而存在的。C#中抛弃了C和C++中繁杂且极易出错的操作符象::和->等,C#中的复合名字一律通过.来连接。System是.Net平台框架提供的最基本的名字空间之一,有关名字空间的详细使用方法将在以后详细介绍,这里只要学会怎样导入名字空间就足够了。
    程序的第二行class Welcome声明了一个类,类的名字叫做Welcome。C#程序中每个变量或函数都必须属于一个类,包括主函数Main(),不能象C或C++那样建立全局变量。C#语言程序总是从Main()方法开始执行,一个程序中不允许出现两个或两个以上的Main()方法。请牢记C#中Main()方法必须被包含在一个类中,Main第一个字母必须大写,必须是一个静态方法,也就是Main()方法必须使用static修饰。static void Main()是类Welcome中定义的主函数。静态方法意义见以后章节。
    程序所完成的输入输出功能是通过Console类来完成的,Console是在名字空间System中已经定义好的一个类。Console类有两个最基本的方法WriteLine和ReadLine。ReadLine表示从输入设备输入数据,WriteLine则用于在输出设备上输出数据。
    如果在电脑上安装了Visual Studio.Net,则可以在集成开发环境中直接选择快捷键或菜单命令编译并执行源文件。如果您不具备这个条件,那么至少需要安装Microsoft.Net Framework SDK,这样才能够运行C#语言程序。Microsoft.Net Framework SDK中内置了C#的编译器csc.exe,下面让我们使用这个微软提供的命令行编译器对程序welcome.cs进行编译。假设已经将welcome.cs文件保存在d:\Charp目录下,启动命令行提示符,在屏幕上输入一行命令:d:回车,cd Charp回车,键入命令:
    C:\WINNT\Microsoft.NET\Framework\v1.0.3705\csc welcome.cs
    如果一切正常welcome.cs文件将被编译,编译后生成可执行文件Welcome.exe。可以在命令提示符窗口运行可执行文件Welcome.exe,屏幕上出现一行字符提示您输入姓名:请键入你的姓名:输入任意字符并按下回车键,屏幕将打印出欢迎信息:欢迎!
    注意,和我们使用过的绝大多数编译器不同,在C#中编译器只执行编译这个过程,而在C和C++中要经过编译和链接两个阶段。换而言之C#源文件并不被编译为目标文件.obj,而是直接生成可执行文件.exe或动态链接库.dll,C#编译器中不需要包含链接器。
    使用Visual Studio.Net建立控制台程序
    (1) 运行Visual Studio.Net程序,出现如图1.2.2A界面。
    (2) 单击新建项目按钮,出现如图1.2.2B对话框。在项目类型(P)编辑框中选择Visual C#项目,在模板(T)编辑框中选择控制台应用程序,在名称(N)编辑框中键入e1,在位置(L)编辑框中键入D:\csarp,必须预先创建文件夹D:\csarp。也可以单击浏览按钮,在打开文件对话框中选择文件夹。单击确定按钮,创建项目。出现如图1.2.2C界面。编写一个应用程序,可能包含多个文件,才能生成可执行文件,所有这些文件的集合叫做一个项目。
    (3) 修改class1.cs文件如下,有阴影部分是新增加的语句,其余是集成环境自动生成的。
    using System;
    namespace e1
    {
    /// <summary>
    /// Class1 的摘要说明。
    /// </summary>
    class Class1
    {
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
    //
    // TODO: 在此处添加代码以启动应用程序
    //
    Console.WriteLine("请键入你的姓名:");
    Console.ReadLine();
    Console.WriteLine("欢迎!");
    }
    }
    }
    (4) 按CTRL+F5键,运行程序,如右图,和1.2.1节运行效果相同。屏幕上出现一行字符,提示您输入姓名:请键入你的姓名:输入任意字符并按下回车键,屏幕将打印出欢迎信息:欢迎!输入回车退出程序。



    图1.2.2A

    图1.2.2B


    1.3 类的基本概念
    C#语言是一种现代、面向对象的语言。面向对象程序设计方法提出了一个全新的概念:类,它的主要思想是将数据(数据成员)及处理这些数据的相应方法(函数成员)封装到类中,类的实例则称为对象。这就是我们常说的封装性。
    类的基本概念
    类可以认为是对结构的扩充,它和C中的结构最大的不同是:类中不但可以包括数据,还包括处理这些数据的函数。类是对数据和处理数据的方法(函数)的封装。类是对某一类具有相同特性和行为的事物的描述。例如,定义一个描述个人情况的类Person如下:
    using System;
    class Person//类的定义,class是保留字,表示定义一个类,Person是类名
    { private string name="张三";//类的数据成员声明
    private int age=12;//private表示私有数据成员
    public void Display()//类的方法(函数)声明,显示姓名和年龄
    { Console.WriteLine("姓名:{0},年龄:{1}",name,age);
    }
    public void SetName(string PersonName)//修改姓名的方法(函数)
    { name=PersonName;
    }
    public void SetAge(int PersonAge)
    { age=PersonAge;
    }
    }
    Console.WriteLine("姓名:{0},年龄:{1}",name,age)的意义是将第二个参数变量name变为字符串填到{0}位置,将第三个参数变量age变为字符串填到{1}位置,将第一个参数表示的字符串在显示器上输出。
    大家注意,这里我们实际定义了一个新的数据类型,为用户自己定义的数据类型,是对个人的特性和行为的描述,他的类型名为Person,和int,char等一样为一种数据类型。用定义新数据类型Person类的方法把数据和处理数据的函数封装起来。类的声明格式如下:
    属性 类修饰符 class 类名{类体}
    其中,关键字class、类名和类体是必须的,其它项是可选项。类修饰符包括new、public、protected、internal、private、abstract和sealed,这些类修饰符以后介绍。类体用于定义类的成员。
    1.3.2 类成员的存取控制
    一般希望类中一些数据不被随意修改,只能按指定方法修改,既隐蔽一些数据。同样一些函数也不希望被其它类程序调用,只能在类内部使用。如何解决这个问题呢?可用访问权限控制字,常用的访问权限控制字如下:private(私有),public(公有)。在数据成员或函数成员前增加访问权限控制字,可以指定该数据成员或函数成员的访问权限。
    私有数据成员只能被类内部的函数使用和修改,私有函数成员只能被类内部的其它函数调用。类的公有函数成员可以被类的外部程序调用,类的公有数据成员可以被类的外部程序直接使用修改。公有函数实际是一个类和外部通讯的接口,外部函数通过调用公有函数,按照预先设定好的方法修改类的私有成员。对于上述例子,name和age是私有数据成员,只能通过公有函数SetName()和SetAge()修改,既它们只能按指定方法修改。
    这里再一次解释一下封装,它有两个意义,第一是把数据和处理数据的方法同时定义在类中。第二是用访问权限控制字使数据隐蔽。
    1.3.3 类的对象
    Person类仅是一个用户新定义的数据类型,由它可以生成Person类的实例,C#语言叫对象。用如下方法声明类的对象:Person OnePerson=new Person();此语句的意义是建立Person类对象,返回对象地址赋值给Person类变量OnePerson。也可以分两步创建Person类的对象:Person OnePerson;OnePerson=new Person();OnePerson虽然存储的是Person类对象地址,但不是C中的指针,不能象指针那样可以进行加减运算,也不能转换为其它类型地址,它是引用型变量,只能引用(代表)Person对象,具体意义参见以后章节。和C、C++不同,C#只能用此种方法生成类对象。
    在程序中,可以用OnePerson.方法名或OnePerson.数据成员名访问对象的成员。例如:OnePerson.Display(),公用数据成员也可以这样访问。注意,C#语言中不包括C++语言中的->符号。
    1.3.4 类的构造函数和析构函数
    在建立类的对象时,需做一些初始化工作,例如对数据成员初始化。这些可以用构造函数来完成。每当用new生成类的对象时,自动调用类的构造函数。 因此,可以把初始化的工作放到构造函数中完成。构造函数和类名相同,没有返回值。例如可以定义Person类的构造函数如下:
    public Person(string Name,int Age)//类的构造函数,函数名和类同名,无返回值。
    { name=Name;
    age=Age;
    }
    当用Person OnePerson=new Person(“张五”,20)语句生成Person类对象时,将自动调用以上构造函数。请注意如何把参数传递给构造函数。
    变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销。类的对象被撤销时,将自动调用析构函数。一些善后工作可放在析构函数中完成。析构函数的名字为~类名,无返回类型,也无参数。Person类的析构函数为~ Person()。C#中类析构函数不能显示地被调用,它是被垃圾收集器撤销不被使用的对象时自动调用的。
    1.3.5 类的构造函数的重载
    在C#语言中,同一个类中的函数,如果函数名相同,而参数类型或个数不同,认为是不同的函数,这叫函数重载。仅返回值不同,不能看作不同的函数。这样,可以在类定义中,定义多个构造函数,名字相同,参数类型或个数不同。根据生成类的对象方法不同,调用不同的构造函数。例如可以定义Person类没有参数的构造函数如下:
    public Person()//类的构造函数,函数名和类同名,无返回值。
    { name="张三";
    age=12;
    }
    用语句Person OnePerson=new Person("李四",30)生成对象时,将调用有参数的构造函数,而用语句Person OnePerson=new Person()生成对象时,调用无参数的构造函数。由于析构函数无参数,因此,析构函数不能重载。
    1.3.6 使用Person类的完整的例子
    下边用一个完整的例子说明Person类的使用:(VisualStudio.Net编译通过)
    using System;
    namespace e1//定义以下代码所属命名空间,意义见以后章节
    { class Person
    { private String name="张三";//类的数据成员声明
    private int age=12;
    public void Display()//类的方法(函数)声明,显示姓名和年龄
    { Console.WriteLine("姓名:{0},年龄:{1}",name,age);
    }
    public void SetName(string PersonName)//指定修改姓名的方法(函数)
    { name=PersonName;
    }
    public void SetAge(int PersonAge)//指定修改年龄的方法(函数)
    { age=PersonAge;
    }
    public Person(string Name,int Age)//构造函数,函数名和类同名,无返回值
    { name=Name;
    age=Age;
    }
    public Person()//类的构造函数重载
    { name="田七";
    age=12;
    }
    }
    class Class1
    { static void Main(string[] args)
    { Person OnePerson=new Person("李四",30);//生成类的对象
    OnePerson.Display();
    //下句错误,在其它类(Class1类)中,不能直接修改Person类中的私有成员。
    //OnePerson.name="王五";
    //只能通过Person类中公有方法SetName修改Person类中的私有成员name。
    OnePerson.SetName("王五");
    OnePerson.SetAge(40);
    OnePerson.Display();
    OnePerson=new Person();
    OnePerson.Display();
    }
    }
    }
    键入CTRL+F5运行后,显示的效果是:
    姓名: 李四,年龄:30
    姓名: 王五,年龄:40
    姓名: 田七,年龄:12
    1.4 C#的数据类型
    从大的方面来分,C#语言的数据类型可以分为三种:值类型,引用类型,指针类型,指针类型仅用于非安全代码中。本节重点讨论值类型和引用类型。
    1.4.1 值类型和引用类型区别
    在C#语言中,值类型变量存储的是数据类型所代表的实际数据,值类型变量的值(或实例)存储在栈(Stack)中,赋值语句是传递变量的值。引用类型(例如类就是引用类型)的实例,也叫对象,不存在栈中,而存储在可管理堆(Managed Heap)中,堆实际上是计算机系统中的空闲内存。引用类型变量的值存储在栈(Stack)中,但存储的不是引用类型对象,而是存储引用类型对象的引用,即地址,和指针所代表的地址不同,引用所代表的地址不能被修改,也不能转换为其它类型地址,它是引用型变量,只能引用指定类对象,引用类型变量赋值语句是传递对象的地址。见下例:
    using System;
    class MyClass//类为引用类型
    { public int a=0;
    }
    class Test
    { static void Main()
    { f1();
    }
    static public void f1()
    { int v1=1;//值类型变量v1,其值1存储在栈(Stack)中
    int v2=v1;//将v1的值(为1)传递给v2,v2=1,v1值不变。
    v2=2;//v2=2,v1值不变。
    MyClass r1=new MyClass();//引用变量r1存储MyClass类对象的地址
    MyClass r2=r1;//r1和r2都代表是同一个MyClass类对象
    r2.a=2;//和语句r1.a=2等价
    }
    }
    存储在栈中的变量,当其生命周期结束,自动被撤销,例如,v1存储在栈中,v1和函数f1同生命周期,退出函数f1,v1不存在了。但在堆中的对象不能自动被撤销。因此C和C++语言,在堆中建立的对象,不使用时必须用语句释放对象占用的存储空间。.NET系统CLR内建垃圾收集器,当对象的引用变量被撤销,表示对象的生命周期结束,垃圾收集器负责收回不被使用的对象占用的存储空间。例如,上例中引用变量r1及r2是MyClass类对象的引用,存储在栈中,退出函数f1,r1和r2都不存在了,在堆中的MyClass类对象也就被垃圾收集器撤销。也就是说,CLR具有自动内存管理功能。
    1.4.2 值类型变量分类
    C#语言值类型可以分为以下几种:
     简单类型(Simple types)
    简单类型中包括:数值类型和布尔类型(bool)。数值类型又细分为:整数类型、字符类型(char)、浮点数类型和十进制类型(decimal)。
     结构类型(Struct types)
     枚举类型(Enumeration types)
    C#语言值类型变量无论如何定义,总是值类型变量,不会变为引用类型变量。
    1.4.3 结构类型
    结构类型和类一样,可以声明构造函数、数据成员、方法、属性等。结构和类的最根本的区别是结构是值类型,类是引用类型。和类不同,结构不能从另外一个结构或者类派生,本身也不能被继承,因此不能定义抽象结构,结构成员也不能被访问权限控制字protected修饰,也不能用virtual和abstract修饰结构方法。在结构中不能定义析构函数。虽然结构不能从类和结构派生,可是结构能够继承接口,结构继承接口的方法和类继承接口的方法基本一致。下面例子定义一个点结构point:
    using System;
    struct point//结构定义
    { public int x,y;//结构中也可以声明构造函数和方法,变量不能赋初值
    }
    class Test
    { static void Main()
    { point P1;
    P1.x=166;
    P1.y=111;
    point P2;
    P2=P1;//值传递,使P2.x=166,P2.y=111
    point P3=new point();//用new生成结构变量P3,P3仍为值类型变量
    }//用new生成结构变量P3仅表示调用默认构造函数,使x=y==0。
    }
    1.4.4 简单类型
    简单类型也是结构类型,因此有构造函数、数据成员、方法、属性等,因此下列语句int i=int.MaxValue;string s=i.ToString()是正确的。即使一个常量,C#也会生成结构类型的实例,因此也可以使用结构类型的方法,例如:string s=13.ToString()是正确的。简单类型包括:整数类型、字符类型、布尔类型、浮点数类型、十进制类型。见下表:
    保留字 System命名空间中的名字 字节数 取值范围
    sbyte System.Sbyte 1 -128~127
    byte System.Byte 1 0~255
    short System.Int16 2 -32768~32767
    ushort System.UInt16 2 0~65535
    int System.Int32 4 -2147483648~2147483647
    uint System.UInt32 4 0~4292967295
    long System.Int64 8 -9223372036854775808~9223372036854775808
    ulong System.UInt64 8 0~18446744073709551615
    char System.Char 2 0~65535
    float System.Single 4 3.4E-38~3.4E+38
    double System.Double 8 1.7E-308~1.7E+308
    bool System.Boolean (true,false)
    decimal System.Decimal 16 正负1.0到7.9之间
    C#简单类型使用方法和C、C++中相应的数据类型基本一致。需要注意的是:
     和C语言不同,无论在何种系统中,C#每种数据类型所占字节数是一定的。
     字符类型采用Unicode字符集,一个Unicode标准字符长度为16位。
     整数类型不能隐式被转换为字符类型(char),例如char c1=10是错误的,必须写成:char c1=(char)10,char c='A',char c='\x0032';char c='\u0032'。
     布尔类型有两个值:false,true。不能认为整数0是false,其它值是true。bool x=1是错误的,不存在这种写法,只能写成x=true 或x=false。
     十进制类型(decimal)也是浮点数类型,只是精度比较高,一般用于财政金融计算。
    1.4.5 枚举类型
    C#枚举类型使用方法和C、C++中的枚举类型基本一致。见下例:
    using System;
    class Class1
    { enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
    //使用Visual Studio.Net,enum语句添加在[STAThread]前边
    static void Main(string[] args)
    { Days day=Days.Tue;
    int x=(int)Days.Tue;//x=2
    Console.WriteLine("day={0},x={1}",day,x);//显示结果为:day=Tue,x=4
    }
    }
    在此枚举类型Days中,每个元素的默认类型为int,其中Sun=0,Mon=1,Tue=2,依此类推。也可以直接给枚举元素赋值。例如:
    enum Days{Sat=1,Sun,Mon,Tue,Wed,Thu,Fri,Sat};
    在此枚举中,Sun=1,Mon=2,Tue=3,Wed=4,等等。和C、C++中不同,C#枚举元素类型可以是byte、sbyte、short、ushort、int、uint、long和ulong类型,但不能是char类型。见下例:
    enum Days:byte{Sun,Mon,Tue,Wed,Thu,Fri,Sat};//元素为字节类型
    1.4.6 值类型的初值和默认构造函数
    所有变量都要求必须有初值,如没有赋值,采用默认值。对于简单类型,sbyte、byte、short、ushort、int、uint、long和ulong默认值为0,char类型默认值是(char)0,float为0.0f,double为0.0d,decimal为0.0m,bool为false,枚举类型为0,在结构类型和类中,数据成员的数值类型变量设置为默认值,引用类型变量设置为null。
    可以显示的赋值,例如int i=0。而对于复杂结构类型,其中的每个数据成员都按此种方法赋值,显得过于麻烦。由于数值类型都是结构类型,可用new语句调用其构造函数初始化数值类型变量,例如:int j=new int()。请注意,用new语句并不是把int变量变为引用变量,j仍是值类型变量,这里new仅仅是调用其构造函数。所有的数值类型都有默认的无参数的构造函数,其功能就是为该数值类型赋初值为默认值。对于自定义结构类型,由于已有默认的无参数的构造函数,不能再定义无参数的构造函数,但可以定义有参数的构造函数。
    1.4.7 引用类型分类
    C#语言中引用类型可以分为以下几种:
     类:C#语言中预定义了一些类:对象类(object类)、数组类、字符串类等。当然,程序员可以定义其它类。
     接口。
     代表。
    C#语言引用类型变量无论如何定义,总是引用类型变量,不会变为值类型变量。C#语言引用类型对象一般用运算符new建立,用引用类型变量引用该对象。本节仅介绍对象类型(object类型)、字符串类型、数组。其它类型在其它节中介绍。
    1.4.8 对象类(object类)
    C#中的所有类型(包括数值类型)都直接或间接地以object类为基类。对象类(object类)是所有其它类的基类。任何一个类定义,如果不指定基类,默认object为基类。继承和基类的概念见以后章节。C#语言规定,基类的引用变量可以引用派生类的对象(注意,派生类的引用变量不可以引用基类的对象),因此,对一个object的变量可以赋予任何类型的值:
    int x =25;
    object obj1;
    obj1=x;
    object obj2= 'A';
    object关键字是在命名空间System中定义的,是类System.Object的别名。
    1.4.9 数组类
    在进行批量处理数据的时候,要用到数组。数组是一组类型相同的有序数据。数组按照数组名、数据元素的类型和维数来进行描述。C#语言中数组是类System.Array类对象,比如声明一个整型数数组:int[] arr=new int[5];实际上生成了一个数组类对象,arr是这个对象的引用(地址)。
    在C#中数组可以是一维的也可以是多维的,同样也支持数组的数组,即数组的元素还是数组。一维数组最为普遍,用的也最多。我们先看一个一维数组的例子:
    using System;
    class Test
    { static void Main()
    { int[] arr=new int[3];//用new运算符建立一个3个元素的一维数组
    for(int i=0;i<arr.Length;i++)//arr.Length是数组类变量,表示数组元素个数
    arr[i]=i*i;//数组元素赋初值,arr[i]表示第i个元素的值
    for (int i=0;i<arr.Length;i++)//数组第一个元素的下标为0
    Console.WriteLine("arr[{0}]={1}",i,arr[i]);
    }
    }
    这个程序创建了一个int类型3个元素的一维数组,初始化后逐项输出。其中arr.Length表示数组元素的个数。注意数组定义不能写为C语言格式:int arr[]。程序的输出为:
    arr[0] = 0
    arr[1] = 1
    arr[2] = 4
    上面的例子中使用的是一维数组,下面介绍多维数组:
    string[] a1;//一维string数组类引用变量a1
    string[,] a2;//二维string数组类引用变量a2
    a2=new string[2,3];
    a2[1,2]="abc";
    string[,,] a3;//三维string数组类引用变量a3
    string[][] j2;//数组的数组,即数组的元素还是数组
    string[][][][] j3;
    在数组声明的时候,可以对数组元素进行赋值。看下面的例子:
    int[] a1=new int[]{1,2,3};//一维数组,有3个元素。
    int[] a2=new int[3]{1,2,3};//此格式也正确
    int[] a3={1,2,3};//相当于int[] a3=new int[]{1,2,3};
    int[,] a4=new int[,]{{1,2,3},{4,5,6}};//二维数组,a4[1,1]=5
    int[][] j2=new int[3][];//定义数组j2,有三个元素,每个元素都是一个数组
    j2[0]=new int[]{1,2,3};//定义第一个元素,是一个数组
    j2[1]=new int[]{1, 2, 3, 4, 5, 6};//每个元素的数组可以不等长
    j2[2]=new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
    1.4.10 字符串类(string类)
    C#还定义了一个基本的类string,专门用于对字符串的操作。这个类也是在名字空间System中定义的,是类System.String的别名。字符串应用非常广泛,在string类的定义中封装了许多方法,下面的一些语句展示了string类的一些典型用法:
     字符串定义
    string s;//定义一个字符串引用类型变量s
    s="Zhang";//字符串引用类型变量s指向字符串"Zhang"
    string FirstName="Ming";
    string LastName="Zhang";
    string Name=FirstName+" "+LastName;//运算符+已被重载
    string SameName=Name;
    char[] s2={'计','算','机','科','学'};
    string s3=new String(s2);
     字符串搜索
    string s="ABC科学";
    int i=s.IndexOf("科");
    搜索"科"在字符串中的位置,因第一个字符索引为0,所以"A"索引为0,"科"索引为3,因此这里i=3,如没有此字符串i=-1。注意C#中,ASCII和汉字都用2字节表示。
     字符串比较函数
    string s1="abc";
    string s2="abc";
    int n=string.Compare(s1,s2);//n=0
    n=0表示两个字符串相同,n小于零,s1<s2,n大于零,s1>s2。此方法区分大小写。也可用如下办法比较字符串:
    string s1="abc";
    string s="abc";
    string s2="不相同";
    if(s==s1)//还可用!=。虽然String是引用类型,但这里比较两个字符串的值
    s2="相同";
     判断是否为空字符串
    string s="";
    string s1="不空";
    if(s.Length==0)
    s1="空";
     得到子字符串或字符
    string s="取子字符串";
    string sb=s.Substring(2,2);//从索引为2开始取2个字符,Sb="字符",s内容不变
    char sb1=s[0];//sb1='取'
    Console.WriteLine(sb1);//显示:取
     字符串删除函数
    string s="取子字符串";
    string sb=s.Remove(0,2);//从索引为0开始删除2个字符,Sb="字符串",s内容不变
     插入字符串
    string s="计算机科学";
    string s1=s.Insert(3,"软件");//s1="计算机软件科学",s内容不变
     字符串替换函数
    string s="计算机科学";
    string s1=s.Replace("计算机","软件");//s1="软件科学",s内容不变
     把String转换为字符数组
    string S="计算机科学";
    char[] s2=S.ToCharArray(0,S.Length);//属性Length为字符类对象的长度
     其它数据类型转换为字符串
    int i=9;
    string s8=i.ToString();//s8="9"
    float n=1.9f;
    string s9=n.ToString();//s8="1.9"
    其它数据类型都可用此方法转换为字符类对象
     大小写转换
    string s="AaBbCc";
    string s1=s.ToLower();//把字符转换为小写,s内容不变
    string s2=s.ToUpper();//把字符转换为大写,s内容不变
     删除所有的空格
    string s="A bc ";
    s.Trim();//删除所有的空格
    string类其它方法的使用请用帮助系统查看,方法是打开Visual Studio.Net的代码编辑器,键入string,将光标移到键入的字符串string上,然后按F1键。
    1.4.11 类型转换
    在编写C#语言程序中,经常会碰到类型转换问题。例如整型数和浮点数相加,C#会进行隐式转换。详细记住那些类型数据可以转换为其它类型数据,是不可能的,也是不必要的。程序员应记住类型转换的一些基本原则,编译器在转换发生问题时,会给出提示。C#语言中类型转换分为:隐式转换、显示转换、加框(boxing)和消框(unboxing)等三种。
    一. 隐式转换
    隐式转换就是系统默认的、不需要加以声明就可以进行的转换。例如从int类型转换到long类型就是一种隐式转换。在隐式转换过程中,转换一般不会失败,转换过程中也不会导致信息丢失。例如:
    int i=10;
    long l=i;
    二. 显示转换
    显式类型转换,又叫强制类型转换。与隐式转换正好相反,显式转换需要明确地指定转换类型,显示转换可能导致信息丢失。下面的例子把长整形变量显式转换为整型:
    long l=5000;
    int i=(int)l;//如果超过int取值范围,将产生异常
    三. 加框(boxing)和消框(unboxing)
    加框(boxing)和消框(unboxing)是C#语言类型系统提出的核心概念,加框是值类型转换为object(对象)类型,消框是object(对象)类型转换为值类型。有了加框和消框的概念,对任何类型的变量来说最终我们都可以看作是object类型。
    1 加框操作
    把一个值类型变量加框也就是创建一个object对象,并将这个值类型变量的值复制给这个object对象。例如:
    int i=10;
    object obj=i;//隐式加框操作,obj为创建的object对象的引用。
    我们也可以用显式的方法来进行加框操作,例如:
    int i =10;
    object obj=object(i);//显式加框操作
    值类型的值加框后,值类型变量的值不变,仅将这个值类型变量的值复制给这个object对象。我们看一下下面的程序:
    using System
    class Test
    { public static void Main()
    { int n=200;
    object o=n;
    o=201;//不能改变n
    Console.WriteLine("{0},{1}",n,o);
    }
    }
    输出结果为:200,201。这就证明了值类型变量n和object类对象o都独立存在着。
    2. 消框操作
    和加框操作正好相反,消框操作是指将一个对象类型显式地转换成一个值类型。消框的过程分为两步:首先检查这个object对象,看它是否为给定的值类型的加框值,如是,把这个对象的值拷贝给值类型的变量。我们举个例子来看看一个对象消框的过程:
    int i=10;
    object obj=i;
    int j=(int)obj;//消框操作
    可以看出消框过程正好是加框过程的逆过程,必须注意加框操作和消框操作必须遵循类型兼容的原则。
    3. 加框和消框的使用
    定义如下函数:
    void Display(Object o)//注意,o为Object类型
    { int x=(int)o;//消框
    System.Console.WriteLine("{0},{1}",x,o);
    }
    调用此函数:int y=20;Display(y);在此利用了加框概念,虚参被实参替换:Object o=y,也就是说,函数的参数是Object类型,可以将任意类型实参传递给函数。
    1.5 运算符
    C#语言和C语言的运算符用法基本一致。以下重点讲解二者之间不一致部分。
    1.5.1 运算符分类
    与C语言一样,如果按照运算符所作用的操作数个数来分,C#语言的运算符可以分为以下几种类型:
     一元运算符:一元运算符作用于一个操作数,例如:-X、++X、X--等。
     二元运算符:二元运算符对两个操作数进行运算,例如:x+y。
     三元运算符:三元运算符只有一个:x? y:z。
    C#语言运算符的详细分类及操作符从高到低的优先级顺序见下表。
    类别 操作符
    初级操作符 (x) x.y f(x) a[x] x++ x-- new type of sizeof checked unchecked
    一元操作符 + - ! ~ ++x –x (T)x
    乘除操作符 * / %
    加减操作符 + -
    移位操作符 << >>
    关系操作符 < > <= >= is as
    等式操作符 == !=
    逻辑与操作符 &
    逻辑异或操作符 ^
    逻辑或操作符 |
    条件与操作符 &&
    条件或操作符 ||
    条件操作符 ?:
    赋值操作符 = *= /= %= += -= <<= >>= &= ^= |=
    1.5.2 测试运算符is
    is操作符用于动态地检查表达式是否为指定类型。使用格式为:e is T,其中e是一个表达式,T是一个类型,该式判断e是否为T类型,返回值是一个布尔值。例子:
    using System;
    class Test
    { public static void Main()
    { Console.WriteLine(1 is int);
    Console.WriteLine(1 is float);
    Console.WriteLine(1.0f is float);
    Console.WriteLine(1.0d is double);
    }
    }
    输出为:
    True
    False
    True
    True
    1.5.3 typeof运算符
    typeof操作符用于获得指定类型在system名字空间中定义的类型名字,例如:
    using System;
    class Test
    { static void Main()
    { Console.WriteLine(typeof(int));
    Console.WriteLine(typeof(System.Int32));
    Console.WriteLine(typeof(string));
    Console.WriteLine(typeof(double[]));
    }
    }
    产生如下输出,由输出可知int和System.int32是同一类型。
    System.Int32
    System.Int32
    System.String
    System.Double[]
    1.5.4 溢出检查操作符checked和unchecked
    在进行整型算术运算(如+、-、*、/等)或从一种整型显式转换到另一种整型时,有可能出现运算结果超出这个结果所属类型值域的情况,这种情况称之为溢出。整型算术运算表达式可以用checked或unchecked溢出检查操作符,决定在编译和运行时是否对表达式溢出进行检查。如果表达式不使用溢出检查操作符或使用了checked操作符,常量表达式溢出,在编译时将产生错误,表达式中包含变量,程序运行时执行该表达式产生溢出,将产生异常提示信息。而使用了unchecked操作符的表达式语句,即使表达式产生溢出,编译和运行时都不会产生错误提示。但这往往会出现一些不可预期的结果,所以使用unchecked操作符要小心。下面的例子说明了checked和unchecked操作符的用法:
    using System;
    class Class1
    { static void Main(string[] args)
    { const int x=int.MaxValue;
    unchecked//不检查溢出
    { int z=x*2;//编译时不产生编译错误,z=-2
    Console.WriteLine("z={0}",z);//显示-2
    }
    checked//检查溢出
    { int z1=(x*2);//编译时会产生编译错误
    Console.WriteLine("z={0}",z1);
    }
    }
    }
    1.5.5 new运算符
    new操作符可以创建值类型变量、引用类型对象,同时自动调用构造函数。例如:
    int x=new int();//用new创建整型变量x,调用默认构造函数
    Person C1=new Person ();//用new建立的Person类对象。Person 变量C1对象的引用
    int[] arr=new int[2];//数组也是类,创建数组类对象,arr是数组对象的引用
    需注意的是,int x=new int()语句将自动调用int结构不带参数的构造函数,给x赋初值0,x仍是值类型变量,不会变为引用类型变量。
    1.5.6 运算符的优先级
    当一个表达式包含多种操作符时,操作符的优先级控制着操作符求值的顺序。例如,表达式x+y*z按照x+(y*z)顺序求值,因为*操作符比+操作符有更高的优先级。这和数学运算中的先乘除后加减是一致的。1.5.1节中的表总结了所有操作符从高到低的优先级顺序。
    当两个有相同优先级的操作符对操作数进行运算时,例如x+y-z,操作符按照出现的顺序由左至右执行,x+y-z按(x+y)-z进行求值。赋值操作符按照右接合的原则,即操作按照从右向左的顺序执行。如x=y=z按照x=(y=z)进行求值。建议在写表达式的时候,如果无法确定操作符的实际顺序,则尽量采用括号来保证运算的顺序,这样也使得程序一目了然,而且自己在编程时能够思路清晰。
    1.6 程序控制语句
    C#语言控制语句和C基本相同,使用方法基本一致。C#语言控制语句包括:if语句、swith语句、while语句、do…while语句、for语句、foreach语句、break语句、continue语句、goto语句、return语句、异常处理语句等,其中foreach语句和异常语句是C#语言新增加控制语句。本节首先介绍一下这些语句和C语言的不同点,然后介绍C#语言新增的控制语句。
    1.6.1 和C语言的不同点
     与C不同,if语句、while语句、do…while语句、for语句中的判断语句,一定要用布尔表达式,不能认为0为false,其它数为true。
     switch语句不再支持遍历,C和C++语言允许switch语句中case标签后不出现break语句,但C#不允许这样,它要求每个case标签项后使用break语句或goto跳转语句,即不允许从一个case自动遍历到其它case,否则编译时将报错。switch语句的控制类型,即其中控制表达式的数据类型可以是sbyte、byte、short、ushort、uint、long、ulong、char、string或枚举类型。每个case标签中的常量表达式必须属于或能隐式转换成控制类型。如果有两个或两个以上case标签中的常量表达式值相同,编译时将会报错。执行switch语句,首先计算switch表达式,然后与case后的常量表达式的值进行比较,执行第一个与之匹配的case分支下的语句。如果没有case常量表达式的值与之匹配,则执行dafault分支下的语句,如果没有dafault语句,则退出switch语句。switch语句中可以没有dafault语句,但最多只能有一个dafault语句。见下例:
    using System;
    class class1
    { static void Main()
    { System.Console.WriteLine("请输入要计算天数的月份");
    string s=System.Console.ReadLine();
    string s1="";
    switch(s)
    { case "1": case "3": case "5":
    case "7": case "8": case "10":
    case "12"://共用一条语句
    s1="31";break;
    case "2":
    s1="28";break;
    case "4": case "6": case "9":
    goto case "11";//goto语句仅为说明问题,无此必要
    case "11":
    s1="30";break;
    default:
    s1="输入错误";break;
    }
    System.Console.WriteLine(s1);
    }
    }
    1.6.2 foreach语句
    foreach语句是C#语言新引入的语句,C和C++中没有这个语句,它借用Visual Basic中的foreach语句。语句的格式为:
    foreach(类型 变量名 in 表达式) 循环语句
    其中表达式必须是一个数组或其它集合类型,每一次循环从数组或其它集合中逐一取出数据,赋值给指定类型的变量,该变量可以在循环语句中使用、处理,但不允许修改变量,该变量的指定类型必须和表达式所代表的数组或其它集合中的数据类型一致。例子:
    using System;
    class Test()
    { public static void Main()
    { int[] list={10,20,30,40};//数组
    foreach(int m in list)
    Console.WriteLine("{0}",m);
    }
    }
    对于一维数组,foreach语句循环顺序是从下标为0的元素开始一直到数组的最后一个元素。对于多维数组,元素下标的递增是从最右边那一维开始的。同样break和continue可以出现在foreach语句中,功能不变。
    1.6.3 异常语句
    在编写程序时,不仅要关心程序的正常操作,还应该考虑到程序运行时可能发生的各类不可预期的事件,比如用户输入错误、内存不够、磁盘出错、网络资源不可用、数据库无法使用等,所有这些错误被称作异常,不能因为这些异常使程序运行产生问题。各种程序设计语言经常采用异常处理语句来解决这类异常问题。
    C#提供了一种处理系统级错误和应用程序级错误的结构化的、统一的、类型安全的方法。C#异常语句包含try子句、catch子句和finally子句。try子句中包含可能产生异常的语句,该子句自动捕捉执行这些语句过程中发生的异常。catch子句中包含了对不同异常的处理代码,可以包含多个catch子句,每个catch子句中包含了一个异常类型,这个异常类型必须是System.Exception类或它的派生类引用变量,该语句只扑捉该类型的异常。可以有一个通用异常类型的catch子句,该catch子句一般在事先不能确定会发生什么样的异常的情况下使用,也就是可以扑捉任意类型的异常。一个异常语句中只能有一个通用异常类型的catch子句,而且如果有的话,该catch子句必须排在其它catch子句的后面。无论是否产生异常,子句finally一定被执行,在finally子句中可以增加一些必须执行的语句。
    异常语句捕捉和处理异常的机理是:当try子句中的代码产生异常时,按照catch子句的顺序查找异常类型。如果找到,执行该catch子句中的异常处理语句。如果没有找到,执行通用异常类型的catch子句中的异常处理语句。由于异常的处理是按照catch子句出现的顺序逐一检查catch子句,因此catch子句出现的顺序是很重要的。无论是否产生异常,一定执行finally子句中的语句。异常语句中不必一定包含所有三个子句,因此异常语句可以有以下三种可能的形式:
     try –catch语句,可以有多个catch语句
     try -finally语句
     try -catch-finally语句,可以有多个catch语句
    请看下边的例子:
    1. try–catch-finally语句
    using System
    using System.IO//使用文件必须引用的名字空间
    public class Example
    { public static void Main()
    { StreamReader sr=null;//必须赋初值null,否则编译不能通过
    try
    { sr=File.OpenText("d:\\csarp\\test.txt");//可能产生异常
    string s;
    while(sr.Peek()!=-1)
    { s=sr.ReadLine();//可能产生异常
    Console.WriteLine(s);
    }
    }
    catch(DirectoryNotFoundException e)//无指定目录异常
    { Console.WriteLine(e.Message);
    }
    catch(FileNotFoundException e)//无指定文件异常
    { Console.WriteLine("文件"+e.FileName+"未被发现");
    }
    catch(Exception e)//其它所有异常
    { Console.WriteLine("处理失败:{0}",e.Message);
    }
    finally
    { if(sr!=null)
    sr.Close();
    }
    }
    }
    2. try -finally语句
    上例中,其实可以不用catch语句,在finally子句中把文件关闭,提示用户是否正确打开了文件,请读者自己完成。
    3. try -catch语句
    请读者把上例修改为使用try-catch结构,注意在每个catch语句中都要关闭文件。
    1.7 类的继承
    在1.3节,定义了一个描述个人情况的类Person,如果我们需要定义一个雇员类,当然可以从头开始定义雇员类Employee。但这样不能利用Person类中已定义的函数和数据。比较好的方法是,以Person类为基类,派生出一个雇员类Employee,雇员类Employee继承了Person类的数据成员和函数成员,既Person类的数据成员和函数成员成为Employee类的成员。这个Employee类叫以Person类为基类的派生类,这是C#给我们提出的方法。C#用继承的方法,实现代码的重用。
    1.7.1 派生类的声明格式
    派生类的声明格式如下:
    属性 类修饰符 class 派生类名:基类名 {类体}
    雇员类Employee定义如下:
    class Employee:Person//Person类是基类
    { private string department;//部门,新增数据成员
    private decimal salary;//薪金,新增数据成员
    public Employee(string Name,int Age,string D,decimal S):base(Name,Age)
    {//注意base的第一种用法,根据参数调用指定基类构造函数,注意参数的传递
    department=D;
    salary=S;
    }
    public new void Display()//覆盖基类Display()方法,注意new,不可用override
    { base.Display();//访问基类被覆盖的方法,base的第二种用法
    Console.WriteLine("部门:{0} 薪金:{1}",department,salary);
    }
    }
    修改主函数如下:
    class Class1
    { static void Main(string[] args)
    { Employee OneEmployee=new Employee("李四",30,"计算机系",2000);
    OneEmployee.Display();
    }
    }
    Employee类继承了基类Person的方法SetName()、SetAge(),数据成员name和age,即认为基类Person的这些成员也是Employee类的成员,但不能继承构造函数和析构函数。添加了新的数据成员department和salary。覆盖了方法Display()。请注意,虽然Employee类继承了基类Person的name和age,但由于它们是基类的私有成员,Employee类中新增或覆盖的方法不能直接修改name和age,只能通过基类原有的公有方法SetName()和SetAge()修改。如果希望在Employee类中能直接修改name和age,必须在基类中修改它们的属性为protected。
    1.7.2 base 关键字
    base关键字用于从派生类中访问基类成员,它有两种基本用法:
     在定义派生类的构造函数中,指明要调用的基类构造函数,由于基类可能有多个构造函数,根据base后的参数类型和个数,指明要调用哪一个基类构造函数。参见上节雇员类Employee构造函数定义中的base的第一种用法。
     在派生类的方法中调用基类中被派生类覆盖的方法。参见上节雇员类Employee的Display()方法定义中的base的第二种用法。
    1.7.3 覆盖基类成员
    在派生类中,通过声明与基类完全相同新成员,可以覆盖基类的同名成员,完全相同是指函数类型、函数名、参数类型和个数都相同。如上例中的方法Display()。派生类覆盖基类成员不算错误,但会导致编译器发出警告。如果增加new修饰符,表示认可覆盖,编译器不再发出警告。请注意,覆盖基类的同名成员,并不是移走基类成员,只是必须用如下格式访问基类中被派生类覆盖的方法:base.Display()。
    1.7.4 C#语言类继承特点
    C#语言类继承有如下特点:
     C#语言只允许单继承,即派生类只能有一个基类。
     C#语言继承是可以传递的,如果C从B派生,B从A派生,那么C不但继承B的成员,还要继承A中的成员。
     派生类可以添加新成员,但不能删除基类中的成员。
     派生类不能继承基类的构造函数、析构函数和事件。但能继承基类的属性。
     派生类可以覆盖基类的同名成员,如果在派生类中覆盖了基类同名成员,基类该成员在派生类中就不能被直接访问,只能通过base.基类方法名访问。
     派生类对象也是其基类的对象,但基类对象却不是其派生类的对象。例如,前边定义的雇员类Employee是Person类的派生类,所有雇员都是人类,但很多人并不是雇员,可能是学生,自由职业者,儿童等。因此C#语言规定,基类的引用变量可以引用其派生类对象,但派生类的引用变量不可以引用其基类对象。
    1.8 类的成员
    由于C#程序中每个变量或函数都必须属于一个类或结构,不能象C或C++那样建立全局变量,因此所有的变量或函数都是类或结构的成员。类的成员可以分为两大类:类本身所声明的以及从基类中继承来的。
    1.8.1 类的成员类型
    类的成员包括以下类型:
     局部变量:在for、switch等语句中和类方法中定义的变量,只在指定范围内有效。
     字段:即类中的变量或常量,包括静态字段、实例字段、常量和只读字段。
     方法成员:包括静态方法和实例方法。
     属性:按属性指定的get方法和Set方法对字段进行读写。属性本质上是方法。
     事件:代表事件本身,同时联系事件和事件处理函数。
     索引指示器:允许象使用数组那样访问类中的数据成员。
     操作符重载:采用重载操作符的方法定义类中特有的操作。
     构造函数和析构函数。
    包含有可执行代码的成员被认为是类中的函数成员,这些函数成员有方法、属性、索引指示器、操作符重载、构造函数和析构函数。
    1.8.2 类成员访问修饰符
    访问修饰符用于指定类成员的可访问性,C#访问修饰符有private、protected、public和internal4种。Private声明私有成员,私有数据成员只能被类内部的函数使用和修改,私有函数成员只能被类内部的函数调用。派生类虽然继承了基类私有成员,但不能直接访问它们,只能通过基类的公有成员访问。protected声明保护成员,保护数据成员只能被类内部和派生类的函数使用和修改,保护函数成员只能被类内部和派生类的函数调用。public声明公有成员,类的公用函数成员可以被类的外部程序所调用,类的公用数据成员可以被类的外部程序直接使用。公有函数实际是一个类和外部通讯的接口,外部函数通过调用公有函数,按照预先设定好的方法修改类的私有成员和保护成员。internal声明内部成员,内部成员只能在同一程序集中的文件中才是可以访问的,一般是同一个应用(Application)或库(Library)。
    1.9 类的字段和属性
    一般把类或结构中定义的变量和常量叫字段。属性不是字段,本质上是定义修改字段的方法,由于属性和字段的紧密关系,把它们放到一起叙述。
    1.9.1 静态字段、实例字段、常量和只读字段
    用修饰符static声明的字段为静态字段。不管包含该静态字段的类生成多少个对象或根本无对象,该字段都只有一个实例,静态字段不能被撤销。必须采用如下方法引用静态字段:类名.静态字段名。如果类中定义的字段不使用修饰符static,该字段为实例字段,每创建该类的一个对象,在对象内创建一个该字段实例,创建它的对象被撤销,该字段对象也被撤销,实例字段采用如下方法引用:实例名.实例字段名。用const修饰符声明的字段为常量,常量只能在声明中初始化,以后不能再修改。用readonly修饰符声明的字段为只读字段,只读字段是特殊的实例字段,它只能在字段声明中或构造函数中重新赋值,在其它任何地方都不能改变只读字段的值。例子:
    public class Test
    { public const int intMax=int.MaxValue;//常量,必须赋初值
    public int x=0;//实例字段
    public readonly int y=0;//只读字段
    public static int cnt=0;//静态字段
    public Test(int x1,int y1)//构造函数
    { //intMax=0;//错误,不能修改常量
    x=x1;//在构造函数允许修改实例字段
    y=y1;//在构造函数允许修改只读字段
    cnt++;//每创建一个对象都调用构造函数,用此语句可以记录对象的个数
    }
    public void Modify(int x1,int y1)
    { //intMax=0;//错误,不能修改常量
    x=x1;
    cnt=y1;
    //y=10;//不允许修改只读字段
    }
    }
    class Class1
    { static void Main(string[] args)
    { Test T1=new Test(100,200);
    T1.x=40;//引用实例字段采用方法:实例名.实例字段名
    Test.cnt=0;//引用静态字段采用方法:类名.静态字段名
    int z=T1.y;//引用只读字段
    z=Test.intMax;//引用常量
    }
    }
    1.9.2 属性
    C#语言支持组件编程,组件也是类,组件用属性、方法、事件描述。属性不是字段,但必然和类中的某个或某些字段相联系,属性定义了得到和修改相联系的字段的方法。C#中的属性更充分地体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,借助于get和set方法对属性的值进行读写。访问属性值的语法形式和访问一个变量基本一样,使访问属性就象访问变量一样方便,符合习惯。
    在类的基本概念一节中,定义一个描述个人情况的类Person,其中字段name和age是私有字段,记录姓名和年龄,外部通过公有方法SetName和SetAge修改这两个私有字段。现在用属性来描述姓名和年龄。例子如下:
    using System;
    public class Person
    { private string P_name="张三";//P_name是私有字段
    private int P_age=12;//P_age是私有字段
    public void Display()//类的方法声明,显示姓名和年龄
    { Console.WriteLine("姓名:{0},年龄:{1}",P_name,P_age);
    }
    public string Name//定义属性Name
    { get
    { return P_name;}
    set
    { P_name=value;}
    }
    public int Age//定义属性Age
    { get
    { return P_age;}
    set
    { P_age=value;}
    }
    }
    public class Test
    { public static void Main()
    { Person OnePerson= new Person();
    OnePerson.Name="田七";//value="田七",通过set方法修改变量P_Name
    string s=OnePerson.Name;//通过get方法得到变量P_Name值
    OnePerson.Age=20;//通过定义属性,既保证了姓名和年龄按指定方法修改
    int x=OnePerson.Age;//语法形式和修改、得到一个变量基本一致,符合习惯
    OnePerson.Display();
    }
    }
    在属性的访问声明中,只有set访问器表明属性的值只能进行设置而不能读出,只有get访问器表明属性的值是只读的不能改写,同时具有set访问器和get访问器表明属性的值的读写都是允许的。
    虽然属性和字段的语法比较类似,但由于属性本质上是方法,因此不能把属性当做变量那样使用,也不能把属性作为引用型参数或输出参数来进行传递。
    1.10 类的方法
    方法是类中用于执行计算或其它行为的成员。所有方法都必须定义在类或结构中。
    1.10.1 方法的声明
    方法的声明格式如下:
    属性 方法修饰符 返回类型 方法名(形参列表){方法体}
    方法修饰符包括new、public、protected、internal、private、static、virtual、sealed、override、abstract和extern。这些修饰符有些已经介绍过,其它修饰符将逐一介绍。返回类型可以是任何合法的C#数据类型,也可以是void,即无返回值。形参列表的格式为:(形参类型 形参1,形参类型 形参2,...),可以有多个形参。不能使用C语言的形参格式。
    1.10.2 方法参数的种类
    C#语言的方法可以使用如下四种参数(请注意和参数类型的区别):
     值参数,不含任何修饰符。
     引用参数,以ref修饰符声明。
     输出参数,以out修饰符声明。
     数组参数,以params修饰符声明。
    1. 值参数
    当用值参数向方法传递参数时,程序给实参的值做一份拷贝,并且将此拷贝传递给该方法,被调用的方法不会修改实参的值,所以使用值参数时,可以保证实参的值是安全的。如果参数类型是引用类型,例如是类的引用变量,则拷贝中存储的也是对象的引用,所以拷贝和实参引用同一个对象,通过这个拷贝,可以修改实参所引用的对象中的数据成员。
    2. 引用参数
    有时在方法中,需要修改或得到方法外部的变量值,C语言用向方法传递实参指针来达到目的,C#语言用引用参数。当用引用参数向方法传递实参时,程序将把实参的引用,即实参在内存中的地址传递给方法,方法通过实参的引用,修改或得到方法外部的变量值。引用参数以ref修饰符声明。注意在使用前,实参变量要求必须被设置初始值。
    3. 输出参数
    为了把方法的运算结果保存到外部变量,因此需要知道外部变量的引用(地址)。输出参数用于向方法传递外部变量引用(地址),所以输出参数也是引用参数,与引用参数的差别在于调用方法前无需对变量进行初始化。在方法返回后,传递的变量被认为经过了初始化。值参数、引用参数和输出参数的使用见下例:
    using System;
    class g{public int a=0;}//类定义
    class Class1
    { public static void F1(ref char i)//引用参数
    { i='b';}
    public static void F2(char i)//值参数,参数类型为值类型
    { i='d';}
    public static void F3(out char i)//输出参数
    { i='e';}
    public static void F4(string s)//值参数,参数类型为字符串
    { s="xyz";}
    public static void F5(g gg)//值参数,参数类型为引用类型
    { gg.a=20;}
    public static void F6(ref string s)//引用参数,参数类型为字符串
    { s="xyz";}
    static void Main(string[] args)
    { char a='c';
    string s1="abc";
    F2(a);//值参数,不能修改外部的a
    Console.WriteLine(a);//因a未被修改,显示c
    F1(ref a);//引用参数,函数修改外部的a的值
    Console.WriteLine(a);//a被修改为b,显示b
    Char j;
    F3(out j);//输出参数,结果输出到外部变量j
    Console.WriteLine(j);//显示e
    F4(s1);//值参数,参数类型是字符串,s1为字符串引用变量
    Console.WriteLine(s1);//显示:abc,字符串s1不被修改
    g g1=new g();
    F5(g1);//值参数,但实参是一个类引用类型变量
    Console.WriteLine(g1.a.ToString());//显示:20,修改对象数据
    F6(ref s1);//引用参数,参数类型是字符串,s1为字符串引用变量
    Console.WriteLine(s1);//显示:xyz,字符串s1被修改
    }
    }
    4. 数组参数
    数组参数使用params说明,如果形参表中包含了数组参数,那么它必须是参数表中最后一个参数,数组参数只允许是一维数组。比如string[]和string[][]类型都可以作为数组型参数。最后,数组型参数不能再有ref和out修饰符。见下例:
    using System;
    class Class1
    { static void F(params int[] args)//数组参数,有params说明
    { Console.Write("Array contains {0} elements:",args.Length);
    foreach (int i in args)
    Console.Write(" {0}",i);
    Console.WriteLine();
    }
    static void Main(string[] args)
    { int[] a = {1,2,3};
    F(a);//实参为数组类引用变量a
    F(10, 20, 30, 40);//等价于F(new int[] {60,70,80,90});
    F(new int[] {60,70,80,90});//实参为数组类引用
    F();//等价于F(new int[] {});
    F(new int[] {});//实参为数组类引用,数组无元素
    }
    }
    程序输出
    Array contains 3 elements: 1 2 3
    Array contains 4 elements: 10 20 30 40
    Array contains 4 elements: 60,70,80,90
    Array contains 0 elements:
    Array contains 0 elements:
    方法的参数为数组时也可以不使用params,此种方法可以使用一维或多维数组,见下例:
    using System;
    class Class1
    { static void F(int[,] args)//值参数,参数类型为数组类引用变量,无params说明
    { Console.Write("Array contains {0} elements:",args.Length);
    foreach (int i in args)
    Console.Write(" {0}",i);
    Console.WriteLine();
    }
    static void Main(string[] args)
    { int[,] a = {{1,2,3},{4,5,6}};
    F(a);//实参为数组类引用变量a
    //F(10, 20, 30, 40);//此格式不能使用
    F(new int[,] {{60,70},{80,90}});//实参为数组类引用
    //F();//此格式不能使用
    //F(new int[,] {});//此格式不能使用
    }
    }
    程序输出
    Array contains 3 elements: 1 2 3 4 5 6
    Array contains 4 elements: 60,70,80,90
    1.10.3 静态方法和实例方法
    用修饰符static声明的方法为静态方法,不用修饰符static声明的方法为实例方法。不管类生成或未生成对象,类的静态方法都可以被使用,使用格式为:类名.静态方法名。静态方法只能使用该静态方法所在类的静态数据成员和静态方法。这是因为使用静态方法时,该静态方法所在类可能还没有对象,即使有对象,由于用类名.静态方法名方式调用静态方法,静态方法没有this指针来存放对象的地址,无法判定应访问哪个对象的数据成员。在类创建对象后,实例方法才能被使用,使用格式为:对象名.实例方法名。实例方法可以使用该方法所在类的所有静态成员和实例成员。例子如下:
    using System;
    public class UseMethod
    { private static int x=0;//静态字段
    private int y=1;//实例字段
    public static void StaticMethod()//静态方法
    { x=10;//正确,静态方法访问静态数据成员
    //y=20;//错误,静态方法不能访问实例数据成员
    }
    public void NoStaticMethod()//实例方法
    { x=10;//正确,实例方法访问静态数据成员
    y=20;//正确,实例方法访问实例数据成员
    }
    }
    public class Class1
    { public static void Main()
    { UseMethod m=new UseMethod();
    UseMethod.StaticMethod();//使用静态方法格式为:类名.静态方法名
    m.NoStaticMethod();//使用实例方法格式为:对象名.实例方法名
    }
    }
    1.10.4 方法的重载
    在C#语言中,如果在同一个类中定义的函数名相同,而参数类型或参数个数不同,认为是不相同的函数,仅返回值不同,不能看作不同函数,这叫做函数的重载。前边Person类中定义了多个构造函数就是重载的例子。在C语言中,若计算一个数据的绝对值,则需要对不同数据类型求绝对值方法使用不同的方法名,如用abc()求整型数绝对值,labs()求长整型数绝对值,fabs()求浮点数绝对值。而在C#语言中,可以使用函数重载特性,对这三个函数定义同样的函数名,但使用不同的参数类型。下面是实现方法:
    using System;
    public class UseAbs
    { public int abs(int x)//整型数求绝对值
    { return(x<0 ? -x:x);}
    public long abs(long x)//长整型数求绝对值
    {return(x<0 ? -x:x);}
    public double abs(double x)//浮点数求绝对值
    {return(x<0 ? -x:x);}
    }
    class Class1
    { static void Main(string[] args)
    { UseAbs m=new UseAbs();
    int x=-10;
    long y=-123;
    double z=-23.98d;
    x=m.abs(x);
    y=m.abs(y);
    z=m.abs(z);
    Console.WriteLine("x={0},y={1},z={2}",x,y,z);
    }
    }
    类的对象调用这些同名方法,在编译时,根据调用方法的实参类型决定调用那个同名方法,计算不同类型数据的绝对值。这给编程提供了极大方便。
    1.10.5 操作符重载
    操作符重载是将C#语言中的已有操作符赋予新的功能,但与该操作符的本来含义不冲突,使用时只需根据操作符出现的位置来判别其具体执行哪一种运算。操作符重载,实际是定义了一个操作符函数,操作符函数声明的格式如下:
    static public 函数返回类型 operator 重新定义的操作符(形参表)
    C#语言中有一些操作符是可以重载的,例如:+ - ! ~ ++ -- true false * / % & | ^ << >> == != > < >= <=等等。但也有一些操作符是不允许进行重载的,例如:=, &&, ||, ?:, new, typeof, sizeof, is等。
    下边的例子,定义一个复数类,并且希望复数的加减乘除用符号+,-.*,/来表示。
    using System;
    class Complex//复数类定义
    { private double Real;//复数实部
    private double Imag;//复数虚部
    public Complex(double x,double y)//构造函数
    { Real=x;
    Imag=y;
    }
    static public Complex operator - (Complex a)//重载一元操作符负号,注意1个参数
    { return (new Complex(-a.Real,-a.Imag));}
    static public Complex operator +(Complex a,Complex b)//重载二元操作符加号
    { return (new Complex(a.Real+b.Real,a.Imag+b.Imag));}
    public void Display()
    { Console.WriteLine("{0}+({1})j",Real,Imag);}
    }
    class Class1
    { static void Main(string[] args)
    { Complex x=new Complex(1.0,2.0);
    Complex y=new Complex(3.0,4.0);
    Complex z=new Complex(5.0,7.0);
    x.Display();//显示:1+(2)j
    y.Display();//显示:3+(4)j
    z.Display();//显示:5+(7)j
    z=-x;//等价于z=opeator-(x)
    z.Display();//显示:-1+(-2)j
    z=x+y;//即z=opeator+(x,y)
    z.Display();//显示:4+(6)j
    }
    }
    1.10.6 this关键字
    每个类都可以有多个对象,例如定义Person类的两个对象:
    Person P1=new Person("李四",30);
    Person P2=new Person("张三",40);
    因此P1.Display()应显示李四信息,P2.Display()应显示张三信息,但无论创建多少个对象,只有一个方法Display(),该方法是如何知道显示那个对象的信息的呢?C#语言用引用变量this记录调用方法Display()的对象,当某个对象调用方法Display()时,this便引用该对象(记录该对象的地址)。因此,不同的对象调用同一方法时,方法便根据this所引用的不同对象来确定应该引用哪一个对象的数据成员。this是类中隐含的引用变量,它是被自动被赋值的,可以使用但不能被修改。例如:P1.Display(),this引用对象P1,显示李四信息。P2.Display(),this引用对象P2,显示张三信息。
    1.11 类的多态性
    在面向对象的系统中,多态性是一个非常重要的概念。C#支持两种类型的多态性,第一种是编译时的多态性,一个类的对象调用若干同名方法,系统在编译时,根据调用方法的实参类型及实参的个数决定调用那个同名方法,实现何种操作。编译时的多态性是通过方法重载来实现的。C#语言的方法重载以及操作符重载和C++语言的基本一致。
    第二种是运行时的多态性,是在系统运行时,不同对象调用一个名字相同,参数的类型及个数完全一样的方法,会完成不同的操作。C#运行时的多态性通过虚方法实现。在类的方法声明前加上了virtual修饰符,被称之为虚方法,反之为非虚方法。C#语言的虚方法和C++语言的基本一致。下面的例子说明了虚方法与非虚方法的区别:
    using System;
    class A
    { public void F()//非虚方法
    { Console.Write(" A.F");}
    public virtual void G()//虚方法
    { Console.Write(" A.G");}
    }
    class B:A//A类为B类的基类
    { new public void F()//覆盖基类的同名非虚方法F(),注意使用new
    { Console.Write(" B.F");}
    public override void G()//覆盖基类的同名虚方法G(),注意使用override
    { Console.Write(" B.G");}
    }
    class Test
    { static void F2(A aA)//注意,参数为A类引用变量
    { aA.G();}
    static void Main()
    { B b=new B();
    A a1=new A();
    A a2=b;//允许基类引用变量引用派生类对象,a2引用派生类B对象b
    a1.F();//调用基类A的非虚方法F(),显示A.F
    a2.F();//F()为非虚方法,调用基类A的F(),显示A.F
    b.F();//F()为非虚方法,调用派生类B的F(),显示B.F
    a1.G();//G()为虚方法,因a1引用基类A对象,调用基类A的G(),显示A.G
    a2.G();//G()为虚方法,因a2引用派生类B对象,调用派生类B的G(),显示B.G
    F2(b);//实参为派生类B对象,由于A aA=b,调用派生类B的函数G(),显示B.G
    F2(a1);//实参为基类A对象,调用A类的函数G(),显示A.G
    }
    }
    那么输出应该是:
    A.F A.F B.F A.G B.G B.G A.G
    注意例子中,不同对象调用同名非虚方法F()和同名虚方法G()的区别。a2虽然是基类引用变量,但它引用派生类对象b。由于G()是虚方法,因此a2.G()调用派生类B的G(),显示G.F。但由于F()是非虚方法,a2.F()仍然调用基类A的F(),显示A.F。或者说,如果将基类引用变量引用不同对象,或者是基类对象,或者是派生类对象,用这个基类引用变量分别调用同名虚方法,根据对象不同,会完成不同的操作。而非虚方法则不具备此功能。
    方法F2(A aA)中,参数是A类类型,F2(b)中形参和实参的关系是:A aA=b,即基类引用变量aA引用派生类对象b,aA.G()调用派生类B的函数G(),显示B.G。同理,F2(a1)实参为基类A对象,调用A类的函数G(),显示A.G。
    在类的基本概念一节中,定义一个描述个人情况的类Person,其中公有方法Display()用来显示个人信息。在派生雇员类Employee中,覆盖了基类的公有方法Display(),以显示雇员新增加的信息。我们希望隐藏这些细节,希望无论基类还是派生类,都调用同一个显示方法,根据对象不同,自动显示不同的信息。可以用虚方法来实现,这是一个典型的多态性例子。例子
    using System;
    public class Person
    { private String name="张三";//类的数据成员声明
    private int age=12;
    protected virtual void Display()//类的虚方法
    { Console.WriteLine("姓名:{0},年龄:{1}",name,age);
    }
    public Person(string Name,int Age)//构造函数,函数名和类同名,无返回值
    { name=Name;
    age=Age;
    }
    static public void DisplayData(Person aPerson)//静态方法
    { aPerson.Display();//不是静态方法调用实例方法,如写为Display()错误
    }
    }
    public class Employee:Person//Person类是基类
    { private string department;
    private decimal salary;
    public Employee(string Name,int Age,string D,decimal S):base(Name,Age)
    { department=D;
    salary=S;
    }
    protected override void Display()//重载虚方法,注意用override
    { base.Display();//访问基类同名方法
    Console.WriteLine("部门:{0} 薪金:{1} ", department,salary);
    }
    }
    class Class1
    { static void Main(string[] args)
    { Person OnePerson=new Person("李四",30);
    Person.DisplayData(OnePerson);//显示基类数据
    Employee OneEmployee=new Employee("王五",40,"财务部",2000);
    Person.DisplayData(OneEmployee); //显示派生类数据
    }
    }
    运行后,显示的效果是:
    姓名: 李四,年龄:30
    姓名: 王五,年龄:40
    部门:财务部 薪金:2000
    1.12 抽象类和抽象方法
    抽象类表示一种抽象的概念,只是希望以它为基类的派生类有共同的函数成员和数据成员。抽象类使用abstract修饰符,对抽象类的使用有以下几点规定:
     抽象类只能作为其它类的基类,它不能直接被实例化。
     抽象类允许包含抽象成员,虽然这不是必须的。抽象成员用abstract修饰符修饰。
     抽象类不能同时又是密封的。
     抽象类的基类也可以是抽象类。如果一个非抽象类的基类是抽象类,则该类必须通过覆盖来实现所有继承而来的抽象方法,包括其抽象基类中的抽象方法,如果该抽象基类从其它抽象类派生,还应包括其它抽象类中的所有抽象方法。
    请看下面的示例:
    abstract class Figure//抽象类定义
    { protected double x=0,y=0;
    public Figure(double a,double b)
    { x=a;
    y=b;
    }
    public abstract void Area();//抽象方法,无实现代码
    }
    class Square:Figure///类Square定义
    { public Square(double a,double b):base(a,b)
    {}
    public override void Area()//不能使用new,必须用override
    { Console.WriteLine("矩形面积是:{0}",x*y);}
    }
    class Circle:Figure///类Square定义
    { public Circle(double a):base(a,a)
    {}
    public override void Area()
    { Console.WriteLine("园面积是:{0}",3.14*x*y);}
    }
    class Class1
    { static void Main(string[] args)
    { Square s=new Square(20,30);
    Circle c=new Circle(10);
    s.Area();
    c.Area();
    }
    }
    程序输出结果为:
    矩形面积是:600
    园面积是:314
    抽象类Figure提供了一个抽象方法Area(),并没有实现它,类Square和Circle从抽象类Figure中继承方法Area(),分别具体实现计算矩形和园的面积。
    在类的基本概念一节中,定义一个描述个人情况的类Person,它只是描述了一个人最一般的属性和行为,因此不希望生成它的对象,可以定义它为抽象类。
    注意:C++程序员在这里最容易犯错误。C++中没有对抽象类进行直接声明的方法,而认为只要在类中定义了纯虚函数,这个类就是一个抽象类。纯虚函数的概念比较晦涩,直观上不容易为人们接受和掌握,因此C#抛弃了这一概念。
    1.13 密封类和密封方法
    有时候,我们并不希望自己编写的类被继承。或者有的类已经没有再被继承的必要。C#提出了一个密封类(sealed class)的概念,帮助开发人员来解决这一问题。
    密封类在声明中使用sealed修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,C#编译器将提示出错。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。
    C#还提出了密封方法(sealed method)的概念。方法使用sealed修饰符,称该方法是一个密封方法。在派生类中,不能覆盖基类中的密封方法。
    1.14 接口
    与类一样,在接口中可以定义一个和多个方法、属性、索引指示器和事件。但与类不同的是,接口中仅仅是它们的声明,并不提供实现。因此接口是函数成员声明的集合。如果类或结构从一个接口派生,则这个类或结构负责实现该接口中所声明的所有成员。一个接口可以从多个接口继承,而一个类或结构可以实现多个接口。由于C#语言不支持多继承,因此,如果某个类需要继承多个类的行为时,只能使用多个接口加以说明。
    1.14.1 接口声明
    接口声明是一种类型声明,它定义了一种新的接口类型。接口声明格式如下:
    属性 接口修饰符 interface 接口名:基接口{接口体}
    其中,关键字interface、接口名和接口体时必须的,其它项是可选的。接口修饰符可以是new、public、protected、internal和private。例子:
    public interface IExample
    {//所有接口成员都不能包括实现
    string this[int index] {get;set;}//索引指示器声明
    event EventHandler E;//事件声明
    void F(int value);//方法声明
    string P { get; set;}//属性声明
    }
    声明接口时,需注意以下内容:
     接口成员只能是方法、属性、索引指示器和事件,不能是常量、域、操作符、构造函数或析构函数,不能包含任何静态成员。
     接口成员声明不能包含任何修饰符,接口成员默认访问方式是public。
    1.14.2 接口的继承
    类似于类的继承性,接口也有继承性。派生接口继承了基接口中的函数成员说明。接口允许多继承,一个派生接口可以没有基接口,也可以有多个基接口。在接口声明的冒号后列出被继承的接口名字,多个接口名之间用分号分割。例子如下:
    using System;
    interface IControl
    { void Paint();
    }
    interface ITextBox:IControl//继承了接口Icontrol的方法Paint()
    { void SetText(string text);
    }
    interface IListBox:IControl//继承了接口Icontrol的方法Paint()
    { void SetItems(string[] items);
    }
    interface IComboBox:ITextBox,IListBox
    {//可以声明新方法
    }
    上面的例子中,接口ITextBox和IListBox都从接口IControl中继承,也就继承了接口IControl的Paint方法。接口IComboBox从接口ITextBox和IListBox中继承,因此它应该继承了接口ITextBox的SetText方法和IListBox的SetItems方法,还有IControl的Paint方法。
    1.14.3 类对接口的实现
    前面已经说过,接口定义不包括函数成员的实现部分。继承该接口的类或结构应实现这些函数成员。这里主要讲述通过类来实现接口。类实现接口的本质是,用接口规定类应实现那些函数成员。用类来实现接口时,接口的名称必须包含在类声明中的基类列表中。
    在类的基本概念一节中,定义一个描述个人情况的类Person,从类Person可以派生出其它类,例如:工人类、公务员类、医生类等。这些类有一些共有的方法和属性,例如工资属性。一般希望所有派生类访问工资属性时用同样变量名。该属性定义在类Person中不合适,因为有些人无工资,如小孩。如定义一个类作为基类,包含工资属性,但C#不支持多继承。可行的办法是使用接口,在接口中声明工资属性。工人类、公务员类、医生类等都必须实现该接口,也就保证了它们访问工资属性时用同样变量名。例子如下:
    using System;
    public interface I_Salary//接口
    { decimal Salary//属性声明
    { get;
    set;
    }
    }
    public class Person
    {…//见1.9.2属性节Person类定义,这里不重复了。
    }
    public class Employee:Person,I_Salary//Person类是基类,I_Salary是接口
    {//不同程序员完成工人类、医生类等,定义工资变量名称可能不同
    private decimal salary;
    public new void Display()
    { base.Display();
    Console.WriteLine("薪金:{0} ",salary);
    }
    //工人类、医生类等都要实现属性Salary,保证使用的工资属性同名
    public decimal Salary
    { get
    { return salary;}
    set
    { salary=value;}
    }
    }
    public class Test
    { public static void Main()
    { Employee S=new Employee();
    S.Name="田七";//修改属性Name
    S.Age=20;//修改属性Age
    S.Salary=2000;//修改属性Salary
    S.Display();
    }
    }
    如果类实现了某个接口,类也隐式地继承了该接口的所有基接口,不管这些基接口有没有在类声明的基类表中列出。因此,如果类从一个接口派生,则这个类负责实现该接口及该接口的所有基接口中所声明的所有成员。
    1.15 代表
    在这里要介绍的是C#的一个引用类型----代表(delegate),也翻译为委托。它实际上相当于C语言的函数指针。与指针不同的是C#中的代表是类型安全的。代表类声明格式如下:
    属性集 修饰符 delegate 函数返回类型 定义的代表标识符(函数形参列表);
    修饰符包括new、public、protected、internal和private。例如我们可以声明一个返回类型为int,无参数的函数的代表MyDelegate:
    public delegate int MyDelegate();//只能代表返回类型为int,无参数的函数
    声明了代表类MyDelegate,可以创建代表类MyDelegate的对象,用这个对象去代表一个静态方法或非静态的方法,所代表的方法必须为int类型,无参数。看下面的例子:
    using System;
    delegate int MyDelegate();//声明一个代表,注意声明的位置
    public class MyClass
    { public int InstanceMethod()//非静态的方法,注意方法为int类型,无参数
    { Console.WriteLine("调用了非静态的方法。");
    return 0;
    }
    static public int StaticMethod()//静态方法,注意方法为int类型,无参数
    { Console.WriteLine("调用了静态的方法。");
    return 0;
    }
    }
    public class Test
    { static public void Main ()
    { MyClass p = new MyClass();
    //用new建立代表类MyDelegate对象,d中存储非静态的方法InstanceMethod的地址
    MyDelegate d=new MyDelegate(p.InstanceMethod);//参数是被代表的方法
    d();//调用非静态方法
    //用new建立代表类MyDelegate对象,d中存储静态的方法StaticMethod的地址
    d=new MyDelegate(MyClass.StaticMethod);//参数是被代表的方法
    d();//调用静态方法
    }
    }
    程序的输出结果是:
    调用了非静态的方法。
    调用了静态的方法。
    1.16 事件
    事件是C#语言内置的语法,可以定义和处理事件,为使用组件编程提供了良好的基础。
    1.16.1 事件驱动
    Windows操作系统把用户的动作都看作消息,C#中称作事件,例如用鼠标左键单击按钮,发出鼠标单击按钮事件。Windows操作系统负责统一管理所有的事件,把事件发送到各个运行程序。各个程序用事件函数响应事件,这种方法也叫事件驱动。
    C#语言使用组件编制Windows应用程序。组件本质上是类。在组件类中,预先定义了该组件能够响应的事件,以及对应的事件函数,该事件发生,将自动调用自己的事件函数。例如,按钮类中定义了单击事件Click和单击事件函数。一个组件中定义了多个事件,应用程序中不必也没必要响应所有的事件,而只需响应其中很少事件,程序员编制相应的事件处理函数,用来完成需要响应的事件所应完成的功能。现在的问题是,第一,如何把程序员编制的事件处理函数和组件类中预先定义的事件函数联系起来。第二,如何使不需响应的事件无动作。这是本节要节的解决问题。
    1.16.2 事件的声明
    在C#中,事件首先代表事件本身,例如按钮类的单击事件,同时,事件还是代表类引用变量,可以代表程序员编制的事件处理函数,把事件和事件处理函数联系在一起。下面的例子定义了一个Button组件,这个例子不完整,只是说明问题。实际在C#语言类库中已预定义了Button组件,这里的代码只是想说明Button组件中是如何定义事件的。例子如下:
    public delegate void EventHandler(object sender,EventArgs e);//代表声明
    //EventHandler可以代表没有返回值,参数为(object sender,EventArgs e)的函数
    public class Button:Control//定义一个按钮类Button组件
    {…//按钮类Button其它成员定义
    public event EventHandler Click;//声明一个事件Click,是代表类引用变量
    protected void OnClick(EventArgs e)//Click事件发生,自动触发OnClick方法
    { if(Click!=null)//如果Click已代表了事件处理函数,执行这个函数
    Click(this,e);
    }
    public void Reset()
    { Click=null;}
    }
    在这个例子中,Click事件发生,应有代码保证(未列出)自动触发OnClick方法。Click是类Button的一个事件,同时也是代表EventHandler类的引用变量,如令Click代表事件处理函数,该函数完成Click事件应完成的功能,Click事件发生时,执行事件处理函数。
    1.16.3 事件的预订和撤消
    在随后的例子中,我们声明了一个使用Button类的登录对话框类,对话框类含有两个按钮:OK和Cancel按钮。
    public class LoginDialog: Form//登录对话框类声明
    { Button OkButton;
    Button CancelButton;
    public LoginDialog()//构造函数
    { OkButton=new Button();//建立按钮对象OkButton
    //Click代表OkButtonClick方法,注意+=的使用
    OkButton.Click+=new EventHandler(OkButtonClick);
    CancelButton=new Button();//建立按钮对象OkButton
    CancelButton.Click += new EventHandler(CancelButtonClick);
    }
    void OkButtonClick(object sender, EventArgs e)
    {…//处理OkButton.Click事件的方法
    }
    void CancelButtonClick(object sender, EventArgs e)
    {…//处理CancelButton.Click事件的方法
    }
    }
    在例子中建立了Button类的两个实例,单击按钮事件Click通过如下语句和事件处理方法联系在一起:OkButton.Click+=new EventHandler(OkButtonClick),该语句的意义是使OkButton.Click代表事件处理方法OkButtonClick,这样只要Click事件被触发,事件处理方法OkButtonClick就会被自动调用。撤消事件和事件处理方法OkButtonClick的联系采用如下语句实现:OkButton.Click-=new EventHandler(OkButtonClick),这时,OkButton.Click就不再代表事件处理方法,Click事件被触发,方法OkButtonClick就不会被调用了。务必理解这两条语句的用法。使用Visual Studio.Net集成环境可以自动建立这种联系,在自动生成的代码中包括这两条语句。
    1.17 索引指示器
    在C#语言中,数组也是类,比如我们声明一个整型数数组:int[] arr=new int[5],实际上生成了一个数组类对象,arr是这个对象的引用(地址),访问这个数组元素的方法是:arr[下标],在数组类中,使用索引访问元素是如何实现的呢?是否可以定义自己的类,用索引访问类中的数据成员?索引指示器(indexer)为我们提供了通过索引方式方便地访问类的数据成员的方法。
    首先看下面的例子,用于打印出小组人员的名单:
    using System
    class Team
    { string[] s_name = new string[2];//定义字符串数组,记录小组人员姓名
    public string this[int nIndex]//索引指示器声明,this为类Team类的对象
    { get//用对象名[索引]得到记录小组人员姓名时,调用get函数
    { return s_name[nIndex];
    }
    set//用对象名[索引]修改记录小组人员姓名时,调用set函数
    { s_name[nIndex] =value;//value为被修改值
    }
    }
    }
    class Test
    { public static void Main()
    { Team t1 = new Team();
    t1[0]="张三";
    t1[1]="李斯";
    Console.WriteLine("{0},{1}",t1[0], t1[1]);
    }
    }
    显示结果如下:张三,李斯
    1.18 名字空间
    一个应用程序可能包含许多不同的部分,除了自己编制的程序之外,还要使用操作系统或开发环境提供的函数库、类库或组件库,软件开发商处购买的函数库、类库或组件库,开发团队中其它人编制的程序,等等。为了组织这些程序代码,使应用程序可以方便地使用这些程序代码,C#语言提出了名字空间的概念。名字空间是函数、类或组件的容器,把它们按类别放入不同的名字空间中,名字空间提供了一个逻辑上的层次结构体系,使应用程序能方便的找到所需代码。这和C语言中的include语句的功能有些相似,但实现方法完全不同。
    1.18.1 名字空间的声明
    用关键字namespace声明一个名字空间,名字空间的声明要么是源文件using语句后的第一条语句,要么作为成员出现在其它名字空间的声明之中,也就是说,在一个名字空间内部还可以定义名字空间成员。全局名字空间应是源文件using语句后的第一条语句。在同一名字空间中,不允许出现同名名字空间成员或同名的类。在声明时不允许使用任何访问修饰符,名字空间隐式地使用public修饰符。例子如下:
    using System;
    namespace N1//N1为全局名字空间的名称,应是using语句后的第一条语句
    { namespace N2//名字空间N1的成员N2
    { class A//在N2名字空间定义的类不应重名
    { void f1(){};}
    class B
    { void f2(){};}
    }
    }
    也可以采用非嵌套的语法来实现以上名字空间:
    namespace N1.N2//类A、B在名字空间N1.N2中
    { class A
    { void f1(){};}
    class B
    { void f2(){};}
    }
    也可以采用如下格式:
    namespace N1.N2//类A在名字空间N1.N2中
    { class A
    { void f1(){};}
    }
    namespace N1.N2//类B在名字空间N1.N2中
    { class B
    { void f2(){};}
    }
    1.18.2 名字空间使用
    如在程序中,需引用其它名字空间的类或函数等,可以使用语句using,例如需使用上节定义的方法f1()和f2(),可以采用如下代码:
    using N1.N2;
    class WelcomeApp
    { A a=new A();
    a.f1();
    }
    using N1.N2实际上是告诉应用程序到哪里可以找到类A。请读者重新看一下1.2.1节中的例子。
    1.19 非安全代码
    在C和C++的程序员看来,指针是最强有力的工具之一,同时又带来许多问题。因为指针指向的数据类型可能并不相同,比如你可以把int类型的指针指向一个float类型的变量,而这时程序并不会出错。如果你删除了一个不应该被删除的指针,比如Windows中指向主程序的指针,程序就有可能崩溃。因此滥用指针给程序带来不安全因素。正因为如此,在C#语言中取消了指针这个概念。虽然不使用指针可以完成绝大部分任务,但有时在程序中还不可避免的使用指针,例如调用Windows操作系统的API函数,其参数可能是指针,所以在C#中还允许使用指针,但必须声明这段程序是非安全(unsafe)的。可以指定一个方法是非安全的,例如:unsafe void F1(int * p){…}。可以指定一条语句是非安全的,例如:unsafe int* p2=p1;还可以指定一段代码是非安全的,例如:unsafe{ int* p2=p1;int* p3=p4;}。在编译时要采用如下格式:csc 要编译的C#源程序 /unsafe。
    习题
    1. 从键盘输入姓名,在显示器中显示对输入姓名的问候。(提示:string为字符串类型,用语句string s=Console.ReadLine()输入姓名)
    2. 构造函数和析购函数的主要作用是什么?它们各有什么特性?
    3. 定义点类,数据成员为私有成员,增加有参数和无参数构造函数,在主函数中生成点类对象,并用字符显示点类对象的坐标。
    4. 定义矩形类,数据成员为私有成员,增加有参数和无参数构造函数,在主函数中生成矩形类对象,并用字符显示矩形类对象的长、宽和矩形左上角的坐标。
    5. 设计一个计数器类,统计键入回车的次数,数据成员为私有成员,在主程序中使用此类统计键入回车的次数。
    6. 说明值类型和引用类型的区别,并和C语言相应类型比较。
    7. 定义点结构,在主函数中生成点结构变量,从键盘输入点的位置,并重新显示坐标。
    8. 定义整型一维数组,从键盘输入数组元素数值后,用循环语句显示所有元素的值。
    9. 输入字符串,将字符串第一个字母和每个空格后的字母变为大写,其余字母为小写后输出。
    10. 输入5个数,在每两个数之间增加3个空格后输出。
    11. 编一个猜数程序,程序设定一个1位十进制数,允许用户猜3次,错了告诉比设定数大还是小,用switch语句实现。
    12. C#语言for语句可以这样使用:for(int i;i<10;i++),请问,i的有效使用范围。
    13. 用字符*在CRT上显示一个矩形。
    14. 输入一个字符串,用foreach语句计算输入的字符串长度,并显示长度。
    15. 输入两个数相加,并显示和。用异常语句处理输入错误。
    16. 将1.6.3节中try–catch-finally语句例子改为try-finally和try–catch语句。
    17. 定义点类,从点类派生矩形类,数据成员为私有成员,增加有参数和无参数构造函数,在主函数中生成矩形类对象,并用字符显示矩形类对象的长、宽和矩形左上角的坐标。
    18. 重做12题,将数据成员用属性表示。
    19. 定义一个类,将类外部的char数组元素都变为大写。主程序输入一个字符串,将其变为char数组,变为大写后输出每一个char数组元素。分别用类对象和静态函数实现。
    20. 定义分数类,实现用符号+,-,*,/完成分数的加减乘除。在主函数中输入两个数,完成运算后输出运算结果。
    21. 建立一个sroot()函数,返回其参数的二次根。重载它,让它能够分别返回整数、长整数和双精度参数的二次根。
    22. 重新设计complex类,完成复数的+、-、*、/四则运算。
    23. 定义点类,从点类派生矩形类和园类,主程序实现用同一个方法显示矩形和园的面积。
    24. 重做19题,将点类定义为抽象类。
    25. 重做19题,改为接口实现,即将点类改为接口。


    第二章 Windows编程的基础知识
    2.1 窗口
    Windows应用程序一般都有一个窗口,窗口是运行程序与外界交换信息的界面。一个典型的窗口包括标题栏,最小化按钮,最大/还原按钮,关闭按钮,系统菜单图标,菜单,工具条,状态栏,滚动条,客户区等。程序员的工作之一是设计符合自己要求的窗口,C#用控件的方法设计界面。编程另一个工作是在用户区显示数据和图形。
    2.2 Windows的消息系统
    2.2.1 消息驱动(事件驱动)
    Windows应用程序和dos程序(控制台程序)的最大区别是事件驱动,也叫消息驱动。dos程序运行时如要读键盘,则要独占键盘等待用户输入,如用户不输入,则CPU一直执行键盘输入程序,等待用户输入,即dos程序独占外设和CPU。
    Windows操作系统是一个多任务的操作系统,允许同时运行多个程序,它不允许任何一个程序独占外设,如键盘,鼠标等,所有运行程序共享外设和CPU,各个运行程序都要随时从外设接受命令,执行命令。
    因此必须由Windows操作系统统一管理各种外设。Windows把用户对外设的动作都看作事件(消息),如单击鼠标左键,发送单击鼠标左键事件,用户按下键盘,发送键盘被按下的事件等。Windows操作系统统一负责管理所有的事件,把事件发送到各个运行程序,而各个运行程序用一个函数响应事件,这个函数叫事件响应函数。这种方法叫事件驱动。每个事件都有它自己的事件响应函数,当接到Windows事件后,自动执行此事件的事件响应函数。程序员编程的主要工作就是编制这些事件的处理函数,完成相应的工作。
    2.2.2 事件队列
    Windows把用户的动作都看作事件,Windows操作系统负责管理所有的事件,事件发生后,这些事件被放到系统事件队列中,Windows操作系统从系统事件队列中逐一取出事件,分析各个事件,分送事件到相应运行程序的事件队列中。而每个运行程序,则利用消息循环方法(既循环取得自己事件队列中的事件)得到事件,并把他们送到当前活动窗口,由窗口中的事件函数响应各个事件(消息)。因此,每个运行程序都有自己的事件队列。
    2.2.3 注视窗口
    Windows操作系统允许多个程序同时运行,每个程序可能拥有多个窗口,但其中只有一个窗口是活动的,我们能从窗口的标题栏的颜色来识别一个活动窗口,这个窗口接收Windows系统发来的大部分的事件。这个应用程序的窗口被称为注视(活动)窗口。
    2.3 Windows编程接口和类库
    操作系统为了方便应用程序设计,一般都要提供一个程序库,一些设计应用程序的共用代码都包含在这个程序库中。程序员可以调用这些代码,以简化编程。这节介绍一些常用程序库。
    2.3.1 Windows编程接口(API)
    API(Application Programming Interface)是Windows98、2000和XP操作系统中提供的一组函数,这些函数采用C语言调用格式,是为程序员编制Windows应用程序提供的编程接口。程序员用C语言直接调用API也可以编制Windows应用程序,但大量的程序代码必须由程序员自己编写,而API函数非常庞大,给编程者带来很大的困难。
    2.3.2 MFC类库
    由于API函数十分庞大复杂,看不到函数之间的关系,使程序员不易使用。用C语言使用API函数编写Windows应用程序是十分困难的。微软的VC++6.0用类对API函数进行了封装,为编程提供了MFC类库。使用MFC类库简化了Windows应用程序的编制。但是,MFC类库的使用还是比较复杂的,因此,VC++一直是一些专业人员的编程工具。
    2.3.3 组件库
    为了简化Windows应用程序的设计,提出了组件(控件)的概念,组件也是类,按钮、菜单、工具条等都可以封装为组件,组件采用属性、事件、方法来描述,其中属性描述组件的特性,如按钮的标题,标签字体的颜色和大小。方法是组件类提供的函数,通过调用这些方法,可以控制组件的行为。组件通过事件和外界联系,一个组件可以响应若干个事件,可以为事件增加事件处理函数,以后每当发生该事件,将自动调用该事件处理函数处理此事件。很多组件在设计阶段是可见的,支持可视化编程,这些组件又被叫做控件。用控件编制Windows应用程序很象搭积木,将控件放到窗体中,设置好属性,漂亮的界面就设计好了。组件编程的工具有很多,例如:VB6.0、VB.Net、C#、C++Builder、Java、Delphi等快速开发工具(RAD)。这些工具都有自己的组件库。
    2.3.4 .NET框架类库
    .NET系统为编制Windows应用程序、Web应用程序、Web服务,在.Net框架(.Net FrameWork)中提供了基础类库(Base Class Library)。它是一个统一的、面向对象的、层次化的、可扩展的类库,统一了微软当前各种不同的框架和开发模式,无论开发Windows应用程序,还是开发Web应用程序,采用相同的组件名称,组件具有相同的属性、方法和事件,开发模式也类似,方便程序员学习。.Net框架类库支持控件可视化编程,.Net中的VC++.Net、VB.Net、C#语言都使用这个类库,消除了各种语言开发模式的差别。该类库包括以下功能:基础类库(基本功能,象字符串、数组等)、网络、安全、远程化、诊断和调试、I/O、数据库、XML、Web服务、Web编程、Windows编程接口等等。
    Windows98、2000和XP操作系统并不包含.NET框架类库,为了运行C#程序,必须安装.Net FrameWork。
    2.4 Windows应用程序的基本结构
    Windows应用程序和控制台应用程序的基本结构基本一样,程序的执行总是从Main()方法开始,主函数Main()必须在一个类中。但Windows应用程序使用图形界面,一般有一个窗口(Form),采用事件驱动方式工作。本节介绍Windows应用程序的基本结构。
    2.4.1 最简单的Windows应用程序
    最简单的Windows应用程序如下:
    using System;//引入名字空间
    using System.Windows.Forms;
    public class Form1:Form//类定义
    { static void Main()//主函数
    { Application.Run(new Form1());
    }
    }
    自定义类Form1以Form类为基类。Form类是.Net系统中定义的窗体类,Form类对象具有Windows应用程序窗口的最基本功能,有标题栏、系统菜单、最大化按钮、最小化按钮和关闭按钮、用户区。Form类对象还是一个容器,在Form窗体中可以放置其它控件,例如菜单控件,工具条控件等等。System.Application类中的静态方法Run负责完成一个应用程序的初始化,运行,终止等功能,其参数是本程序使用的窗体Form1类对象,Run方法还负责从操作系统接受事件,并把事件送到窗体中响应。窗体关闭,方法Run退出,Windows应用程序结束。假设已经将文件保存在d:\Charp目录下,文件名为:e1.cs。启动命令行提示符,在屏幕上输入一行命令:d:回车,cd Charp回车,键入命令:
    C:\WINNT\Microsoft.NET\Framework\v1.0.3705\csc /t:winexe /r:system.dll,System.Windows.Forms.dll e1.cs
    命令中的/t:winexe表示要建立一个Windows应用程序,/r表示要引入的命名空间。也可以用记事本建立一个批处理文件g.bat,将以上命令内容拷贝到文件中,运行g.bat,和在命令行提示符键入命令效果相同。以上方法在FrameWork SDK 2000中实现。如果一切正常e1.cs文件将被编译,编译后生成可执行文件e1.exe。运行可执行文件e1.exe,CRT上出现一个窗口如右图。
    可以在Form1类中定义新的变量,由于主窗体关闭,程序也就结束了,因此定义在主窗体Form1中的变量的生命周期和程序的生命周期是相同的,从这个意义上说,这些变量是全局变量。可以为Form1类定义构造函数,在构造函数中做一些初始化的工作,例如修改Form1标题栏中的标题。还可以在Form1中定义控件类的对象,这些控件将在Form1的用户区显示出来,换句话讲,在Form1中生成控件对象,也就是把控件放到窗体中。如在窗体中增加了一个按钮(Button)控件,单击按钮,将产生单击按钮事件,完成一定功能,下例说明了如何在窗体中增加控件,如何修改控件属性,如何增加控键的事件处理函数。
    using System;
    using System.Windows.Forms;
    public class Form1:Form
    { Button button1;//生成Button类引用变量,和应用程序有相同生命周期
    public Form1()//构造函数
    {//下句修改主窗体标题,不指明属性(方法)所属对象,默认为Form1类的属性(方法)
    Text="我的第一个程序";//也可写为:this.Text="我的第一个程序";
    button1=new Button();//生成Button类对象
    button1.Location=new Point(25,25);//修改button1属性location即按钮位置
    button1.Text="确定";//修改button1属性Text,即按钮的标题
    //下句指定button1_Click函数是按钮单击事件的单击事件处理函数
    button1.Click+=new System.EventHandler(button1_Click);
    this.Controls.Add(button1);//按钮增加到窗体中,将在主窗体用户区显示出来
    }
    static void Main()
    { Application.Run(new Form1());
    }
    private void button1_Click(object sender, System.EventArgs e)
    {//事件处理函数
    this.button1.Text="单击了我";//单击按钮事件执行的语句
    }
    }
    请注意在窗体中增加控件类的对象的步骤,首先生成一个引用变量button1,和主窗体Form1有相同的生命周期,第二步在构造函数中用new生成Button类对象,第三步在构造函数中修改button1的属性,增加button1的事件函数。这些步骤对于定义任何一个控件都是相同的。编译运行结果如右图:

    2.4.2 用Visual Studio.Net建立Windows应用程序框架
    以上所做的工作,都是一些固定的工作,可以使用Visual Studio.Net自动建立,下面介绍使用Visual Studio.Net创建Windows应用程序的具体步骤。
    (1) 运行Visual Studio.Net程序,出现如图1.2.2A界面。
    (2) 单击新建项目按钮,出现如图1.2.2B对话框。在项目类型(P)编辑框中选择Visual C#项目,在模板(T)编辑框中选Windows应用程序,在名称(N)编辑框中键入e2,在位置(L)编辑框中键入D:\csarp。也可以单击浏览按钮,在打开文件对话框中选择文件夹。单击确定按钮,创建项目。出现如图2.4.2A界面。生成一个空白窗体(Form1)。

    图2.4.2A
    (3) 在e2文件夹中下有两个文件夹和8个文件,一般只修改Form1.cs文件。右击Form1窗体,在快捷菜单中选择菜单项查看代码(C),可打开Form1.cs文件。Visual Studio.Net生成的Foem1.cs文件如下,这是使用Visual Studio.Net创建Windows应用程序的最基本的形式。底色为黑色的字是作者增加的注解。
    using System;//引入名字空间
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    namespace e2//定义名字空间,///为解释
    { //此处可定义其它类
    /// <summary>
    /// Form1 的摘要说明。
    /// </summary>
    public class Form1 : System.Windows.Forms.Form//Forme1类定义
    { //此处可定义自己的变量,这些变量和运行程序同生命周期
    /// <summary>
    /// 必需的设计器变量。
    /// </summary>
    private System.ComponentModel.Container components = null;
    public Form1()//构造函数
    {
    //
    // Windows 窗体设计器支持所必需的
    //
    InitializeComponent();//此函数系统自动生成,不要修改,该函数做一些初始化工作
    //
    // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
    //在构造函数增加自己的初始化代码,必须放在InitializeComponent()之后
    }
    /// <summary>
    /// 清理所有正在使用的资源。
    /// </summary>
    protected override void Dispose( bool disposing )
    {
    if( disposing )
    {
    if (components != null)
    {
    components.Dispose();
    }
    }
    base.Dispose( disposing );
    }
    #region Windows Form Designer generated code
    /// <summary>
    /// 设计器支持所需的方法 - 不要使用代码编辑器修改
    /// 此方法的内容。
    /// </summary>
    private void InitializeComponent()
    { //此函数系统自动生成,不要修改函数内容,函数做一些初始化工作
    //
    // Form1
    //
    this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
    this.ClientSize = new System.Drawing.Size(292, 273);
    this.Name = "Form1";//this 是Form1窗体对象
    this.Text = "Form1";
    }
    #endregion
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main()//程序入口函数 ,一般不修改
    {
    Application.Run(new Form1());//主程序建立窗体运行
    }//程序入口函数之后可定义自己的方法、属性等
    }
    }
    (4) 下边在窗体中增加一个按钮,并为按钮增加单击事件函数。单击图2.4.2A中标题为Forms.cs[设计]的窗口标签,返回标题为Forms.cs[设计]的窗口。向项目中添加控件需要使用工具箱窗口,若看不到,可以用菜单命令视图/工具箱打开这个窗口(见图2.4.2B左图)。选中工具箱窗口中Windows窗体类型下的Button条目,然后在标题为Forms.cs[设计]的窗口的Form1窗体中按下鼠标左键,拖动鼠标画出放置Button控件的位置,抬起鼠标左键,就将Button控件放到Form1窗体中。选中按钮控件,属性窗口(见图2.4.2B中图)显示按钮属性,其中左侧为属性名称,右侧为属性值,用属性窗口修改Button的Text属性值为:确定。单击属性窗体上的第4个图标,打开事件窗口(见图2.4.2B右图),显示Button控件所能响应的所有事件,其中左侧为事件名称,右侧为事件处理函数名称,如果为空白,表示还没有事件处理函数,选中Click事件,双击右侧空白处,增加单击事件处理函数。
    图2.4.2B
    完成以上设计后,集成环境生成的Foem1.cs文件如下,底色为黑色的代码是新增代码。
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    namespace e2
    {
    /// <summary>
    /// Form1 的摘要说明。
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
    private System.Windows.Forms.Button button1;//定义Button类引用变量
    /// <summary>
    /// 必需的设计器变量。
    /// </summary>
    private System.ComponentModel.Container components = null;
    public Form1()
    {
    //
    // Windows 窗体设计器支持所必需的
    //
    InitializeComponent();
    //
    // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
    //
    }
    /// <summary>
    /// 清理所有正在使用的资源。
    /// </summary>
    protected override void Dispose( bool disposing )
    {
    if( disposing )
    {
    if (components != null)
    {
    components.Dispose();
    }
    }
    base.Dispose( disposing );
    }
    #region Windows Form Designer generated code
    /// <summary>
    /// 设计器支持所需的方法 - 不要使用代码编辑器修改
    /// 此方法的内容。
    /// </summary>
    private void InitializeComponent()
    {
    this.button1 = new System.Windows.Forms.Button();//生成对象
    this.SuspendLayout();
    //
    // button1
    //
    this.button1.Location = new System.Drawing.Point(96, 56);//修改属性
    this.button1.Name = "button1";
    this.button1.Size = new System.Drawing.Size(72, 32);
    this.button1.TabIndex = 0;
    this.button1.Text = "确定";
    this.button1.Click += new System.EventHandler(this.button1_Click);//增加事件
    //
    // Form1
    //
    this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
    this.ClientSize = new System.Drawing.Size(292, 273);
    this.Controls.AddRange(new System.Windows.Forms.Control[] {this.button1});
    this.Name = "Form1";
    this.Text = "Form1";
    }
    #endregion
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main()
    {
    Application.Run(new Form1());
    }
    private void button1_Click(object sender, System.EventArgs e)
    {//事件处理函数
    }
    }
    }
    请注意按钮放到窗体后,集成环境自动增加的语句。分析这些增加的语句,可知在窗体中增加Button类对象的步骤:首先定义Button类变量button1,这是Form1类的一个字段,由于主窗体关闭,程序也就结束了,因此定义在主窗体Form1中的变量的生命周期和程序的生命周期是相同的,从这个意义上说,这样的变量是全局变量。因此变量button1和主窗体Form1有相同的生命周期。第二步在构造函数中用new生成Button类对象,第三步在构造函数中修改button1的属性,第四步增加button1的事件函数,函数button1_Click()是事件处理函数,语句this.button1.Click += new System.EventHandler(this.button1_Click)把按钮Button1的事件Click和事件处理函数button1_Click()联系到一起。程序员应在事件处理函数button1_Click()中增加具体的事件处理语句。这些步骤对于增加任何控件都是相同的。可以比较一下2.4.1节中的步骤,它们基本是相同的。应熟悉以上操作步骤,学会在窗体中增加控件,修改控件属性,增加事件函数。
    2.4.3 方案(Solution)和项目(Project)
    一个应用(Application)可能包括一个或多个可执行程序,例如,学生信息管理系统,可能包括客户端程序和服务器端程序,所有这些可执行程序的集合叫做一个应用解决方案。为了生成一个可执行程序,可能需要有一个或多个文件,例如,一般需要一个窗体文件,有时还需要一个资源文件,若干图形或图像文件。所有这些文件的集合叫一个项目,因此项目是为了创建一个可执行程序所必需的所有的文件的集合。而一个方案中可能包括多个项目。为了方便管理项目和项目所在的方案,Visual Studio.Net为开发人员提供了解决方案资源管理器窗口(图2.4.3)。它可以为我们显示一个方案的树形结构,以及它所包含的项目及项目中的文件。
    一个项目一般要放在一个文件夹中,例如上边的例子,项目e2的所有文件都在文件夹e2中,共有两个文件夹和8个文件,它们的用途如下:
     bin文件夹:包含debug子文件夹,存储生成带调试信息的可执行C#程序。
     obj文件夹:包含编译过程中生成的中间代码。
     AssemblyInfo.cs:创建项目自动添加。包含各种属性设置,例如,项目最终创建的可执行文件或DLL文件中的信息,如标题、描述、公司名等。一般用工具修改该程序,不要直接修改。
     Form1.cs:窗体文件,程序员一般只修改该文件。
     Form1.resx:资源文件。程序员用集成环境提供的工具修改,不要直接修改。
     e2.suo:解决方案用户选项文件,记录用户关于解决方案的选项。
     e2.csproj:项目文件,记录用户关于项目的选项。
     e2.sln:解决方案文件。
    为了以后重新用Visual Studio.Net打开该解决方案,必须保存除了两个文件夹以外的所有文件,实际上,由于文件夹e2不太大,可以保存整个e2文件夹。如果重新开始一个解决方案,首先用菜单项文件/关闭解决方案,关闭当前项目,再新建一个项目。为了用Visual Studio.Net修改以前的程序,必须打开保存的项目文件(扩展名为csproj),或者使用菜单项文件/打开项目,打开保存的项目,同时打开项目所在的解决方案。


    图2.4.3
    习题
    (1) Windows应用程序和dos程序有那些不同。
    (2) 以键盘操作为例说明什么是事件驱动。
    (3) 那些Windows操作系统提供了.NET框架类库,那些提供了API。
    (4) 运行C#程序,应首先安装那些软件。
    (5) 定义一个和应用程序同生命周期的变量,该变量应定义在何处,说明该变量的使用范围。
    (6) 在窗体中增加一个控件,应如何操作,集成环境增加了那些代码。
    (7) 为控件增加事件函数,应如何操作,集成环境增加了那些代码。
    (8) 如何为窗体文件增加一个方法,说明该方法的使用范围。


    第三章 常用控件和类的使用
    Visual Studio.Net(简称VS.NET)使用控件(组件)设计Windows应用程序。将VS.NET工具箱窗口中的控件放到窗体中,使用属性窗口改变控件的属性,或在程序中用语句修改属性,为控件增加事件函数,完成指定的功能。
    3.1 控件通用属性
    大部分控件,例如Label、Button、TextBox等,都是Control类的派生类。Control类中定义了这些派生类控件通用的一组属性和方法,这些属性是:
     Name:控件的名称,区别控件类不同对象的唯一标志,例如建立一个Button控件类对象,可用如下语句,Button button1=new Button(),那么Name属性的值为button1。
     Location:表示控件对象在窗体中的位置。本属性是一个结构,结构中有两个变量,x和y,分别代表控件对象左上角顶点的x和y坐标,该坐标系以窗体左上角为原点,x轴向左为正方向,y轴向下为正方向,以像素为单位。修改Location,可以移动控件的位置,例如:button1.Location=new Point(100,200)语句移动按钮button1到新位置。
     Left和Top:属性值等效于控件的 Location 属性的 X 和Y。修改Left和Top,可以移动控件的位置,例如:button1.Left=100语句水平移动按钮button1。
     Size:本属性是一个结构,结构中有两个变量,Width和Height分别代表控件对象的宽和高,例如可用语句button1.Size.Width=100修改Button控件对象button1的宽。
     BackColor:控件背景颜色。
     Enabled:布尔变量,为true表示控件可以使用,为false表示不可用,控件变为灰色。
     Visible:布尔变量,为true控件正常显示,为false控件不可见。
     Modifier:定义控件的访问权限,可以是private、public、protected等。默认值为private。
     Cursor:鼠标移到控件上方时,鼠标显示的形状。默认值为Default,表示使用默认鼠标形状,即为箭头形状。
    3.2 Form类
    Form类是.Net系统中定义的窗体类(WinForm),它属于System.Windows.Forms名字空间。Form类对象具有Windows应用程序窗口的最基本功能。它可以是对话框、单文档或多文档应用程序窗口的基类。Form类对象还是一个容器,在Form窗体中可以放置其它控件,例如菜单控件,工具条控件等等,还可以放置子窗体。
    1. Form类常用属性
     AutoScroll:布尔变量,表示窗口是否在需要时自动添加滚动条。
     FormBorderStyle:窗体边界的风格,如有无边界、单线、3D、是否可调整等。
     Text:字符串类对象,窗体标题栏中显示的标题。
     AcceptButton:记录用户键入回车时,相当于单击窗体中的那个按钮对象。
     CanceButton:记录用户键入ESC键时,相当于单击窗体中的那个按钮对象。以上两个属性多用于对话框,例如打开文件对话框,用户键入回车,相当于单击确定按钮。
     MaxiMizeBox:窗体标题栏右侧最大化按钮是否可用,设置为false,按钮不可用。
     MiniMizeBox:窗体标题栏右侧最小化按钮是否可用,设置为false,按钮不可用。如果属性MaxiMizeBox和MiniMizeBox都设置为false,将只有关闭按钮。在不希望用户改变窗体大小时,例如对话框,将两者都设置为false。
    2. Form类常用方法
     Close():窗体关闭,释放所有资源。如窗体为主窗体,执行此方法,程序结束。
     Hide():隐藏窗体,但不破坏窗体,也不释放资源,可用方法Show()重新打开。
     Show():显示窗体。
    3. Form类常用事件
     Load:在窗体显示之前发生,可以在其事件处理函数中做一些初始化的工作。
    3.3 标签(Label)控件
    标签控件用来显示一行文本信息,但文本信息不能编辑,常用来输出标题、显示处理结果和标记窗体上的对象。标签一般不用于触发事件。
    1. Label控件常用属性
     Text:显示的字符串
     AutoSize:控件大小是否随字符串大小自动调整,默认值为false,不调整。
     ForeColor:Label显示的字符串颜色。
     Font:字符串所使用的字体,包括所使用的字体名,字体的大小,字体的风格等等,具体修改方法见下边的例子。
    2. 例子e3_3:我的第一个程序
    下面的例子在窗口中显示一行文本,该例虽然简单,但包括了用Visual Studio.Net建立C# Windows应用程序的基本步骤。具体实现步骤如下:
    (1) 建立一个新项目,生成一个空白窗体(Form1),见图2.4.2A。可以用属性窗口(图2.4.2B中图)修改窗体的属性,例如修改Form1的属性Text,可以修改窗体的标题。用鼠标拖动窗体的边界小正方形,可以修改窗体打开时的初始大小。
    (2) 双击工具箱窗口(图2.4.2B左图)中Windows窗体类型下的Label条目,在窗体Form1放置一个Label控件。该控件用来显示一行文本。可以用鼠标拖放Label到窗体的任意位置,并可拖动Label边界改变控件的大小。
    (3) 选中Label控件,在属性窗口中找到属性text,把它的值由“Label1”修改为“我的第一个程序”。接着在属性窗口中选中Font属性,单击Font属性右侧的标题为…的按钮,打开对话框,在对话框中可以修改Label控件显示字符串的字体名称和字号等,也可以单击Font属性左边的+号,在出现的子属性中编辑。编辑完成后,单击Font属性左边的-号,隐藏Font的子属性。修改ForeColor属性可以修改Label控件显示字符串的颜色。这是在设计阶段修改属性。
    (4) 编译,运行,可以看到窗口中按指定字体大小和颜色显示:我的第一个程序。运行效果如右图。
    (5) 保存项目。生成一个可执行程序需要多个文件,这些文件组成一个项目。一般把一个项目存到一个子目录中。单击文件/存所有文件菜单项,保存所有文件。
    (6) 关掉VS.NET,再启动。用文件/打开项目菜单项打开刚才关闭的项目文件(扩展名为sln)。应能看到刚才关闭的设计界面。必须打开项目,才能完成编译工作。
    3.4 按钮(Button)控件
    用户单击按钮,触发单击事件,在单击事件处理函数中完成相应的工作。
    1. Button 控件的常用属性和事件
     属性Text:按钮表面的标题
     事件Click:用户单击触发的事件,一般称作单击事件。
    2. 例子e3_4
    本例说明如何用程序修改属性,如何使用方法,增加事件函数。该例在窗口中显示一行文字,增加2个按纽,单击标题为红色的按纽把显示的文本颜色改为红色,单击标题为黑色的按纽把显示的文本颜色改为黑色。实现步骤如下:
    (1) 继续上例,放三个Button控件到窗体,修改属性Text,使标题分别为红色,黑色,退出。设计好的界面如右图。
    (2) 选中标题为红色的按纽,打开事件窗口(见图2.4.2B右图),显示该控件所能响应的所有事件,其中左侧为事件名称,右侧为事件处理函数名称,如果为空白,表示还没有事件处理函数,选中Click事件,双击右侧空白处,增加单击(Click)标题为红色的按钮的事件处理函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { label1.ForeColor=Color.Red;//运行阶段修改属性
    }//注意label1是控件的名字(label的Name属性),用它来区分不同的控件。
    (3) 单击(Click)标题为黑色的按纽的事件处理函数如下:
    private void button2_Click(object sender, System.EventArgs e)
    { label1.ForeColor=Color.Black;}
    (4) 单击(Click)标题为退出的按纽的事件处理函数如下:
    private void button3_Click(object sender, System.EventArgs e)
    { Close();}
    Close()为窗体(Form)的方法,作用是关闭注窗体。由于关闭了主窗体,程序也就结束了。注意,引用窗体的方法和属性时可不用指定对象名,换句话讲,如不指定属性或方法的对象名,默认为窗体的属性或方法。而使用其它组件的属性及方法要指明所属组件对象,例如label1.ForeColor=Color.Red;
    (5) 编译,运行,单击标题为红色的按纽,窗体显示字符串颜色变为红色,单击标题为黑色的按纽,窗体显示字符串颜色变为黑色,单击标题为退出的按纽,结束程序。
    3.5 事件处理函数的参数
    事件处理函数一般有两个参数,第一个参数(object sender)为产生该事件的对象的属性Name的值,例如上例单击标题为红色的按钮,第一个参数sender的值为button1。如上例标题为红色的按钮和标题为黑色的按钮使用同一个单击事件处理函数,其事件处理如下:
    private void button1_Click(object sender,System.EventArgs e)
    { if(sender==button1)
    label1.ForeColor=Color.Red;
    else
    label1.ForeColor=Color.Black;
    }
    事件处理函数第二个参数(System.EventArgs e)代表事件的一些附加信息,事件不同,所代表的信息也不相同,例如在后边的例子中可以看到,按下鼠标的事件处理函数中,e.X和e.Y分别为发生事件时鼠标位置的x坐标和y坐标,e.Button表示用户单击了鼠标那个键,如为MouseButtons.Left,表示单击了鼠标左键。
    为了使这两个按钮使用相同的单击事件处理函数,首先为标题为红色的按钮增加单击事件处理函数,即是上边的代码,事件函数名称为:button1_Click。选中标题为黑色的按钮,打开事件窗体(见图2.4.2B右图),选中Click事件,从其右侧下拉列表中选择事件处理函数为button1_Click,这样两个按钮就使用相同的单击事件处理函数了。
    3.6 文本框(TextBox)控件
    TextBox控件是用户输入文本的区域,也叫文本框。
    1. TextBox控件属性和事件
     属性Text:用户在文本框中键入的字符串
     属性MaxLength:单行文本框最大输入字符数。
     属性ReadOnly:布尔变量,为true,文本框不能编辑。
     属性PasswordChar:字符串类型,允许输入一个字符,如输入一个字符,用户在文本框中输入的所有字符都显示这个字符。一般用来输入密码。
     属性MultiLine:布尔变量,为true,多行文本框,为false,单行文本框。
     属性ScrollBars:MultiLine=true时有效,有4种选择:=0,无滚动条,=1,有水平滚动条,=2,有垂直滚动条,=3,有水平和垂直滚动条。
     属性SelLength:可选中文本框中的部分或全部字符,本属性为所选择的文本的字符数。
     属性SelStart:所选中文本的开始位置。
     属性SelText:所选中的文本
     属性AcceptsReturn:MultiLine=true时有效,布尔变量,为true,键入回车,换行,为false,键入回车键,相当于单击窗体中的默认按钮。
     事件TextChanged:文本框中的字符发生变化时,发出的事件。
    2. 例子e3_6
    本例要求用户在编辑框中输入两个乘数,单击按钮把相乘的结果在编辑框中显示出来。
    (1) 建立一个新的项目。放四个Label控件到窗体,Text属性分别为:被乘数,乘数,积,*,=。
    (2) 放三个textBox控件到窗体,属性Name从左到右分别为:textBox1、textBox2、textBox3,属性Text都为空。
    (3) 放三个Button控件到窗体,Text属性分别修改为求积,清空,退出。设计的界面如上图。
    (4) 标题为求积的按钮的单击事件处理函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { float ss,ee;
    ss=Convert.ToSingle(textBox1.Text);
    ee=Convert.ToSingle(textBox2.Text);
    textBox3.Text=Convert.ToString(ss*ee);
    }
    (5) 标题为清空的按钮的单击事件处理函数如下:
    private void button2_Click(object sender,System.EventArgs e)
    { textBox1.Text="";
    textBox2.Text="";
    textBox3.Text="";
    }
    (6) 标题为退出的按钮的单击事件处理函数如下:
    private void button3_Click(object sender, System.EventArgs e)
    { Close();}
    (7) 编译,运行,在文本框textBox1,textBox2分别输入2和3,单击标题为求积的按纽,textBox3中显示6,单击标题为清空的按钮,三个文本框被清空,单击标题为退出的按纽,结束程序。
    3.7 Convert类
    Convert类中提供了一些静态方法,用来把一种类型数据转换为另一种类型数据。例如,Convert.ToSingle(textBox1.Text)把字符串textBox1.Text转换为单浮点数。Convert.ToString(3.14)把单浮点数3.14转换为字符串。其它转换函数还有:ToInt、ToInt16等等。
    3.8 单选按钮(RadioButton)和GroupBox控件
    RadioButton是单选按钮控件,多个RadioButton控件可以为一组,这一组内的RadioButton控件只能有一个被选中。GroupBox控件是一个容器类控件,在其内部可放其它控件,表示其内部的所有控件为一组,其属性Text可用来表示此组控件的标题。例如把RadioButton控件放到GroupBox控件中,表示这些RadioButton控件是一组。有一些特性是互斥的,例如性别,选择这类特性可用RadioButton和GroupBox控件。
    1. GroupBox控件常用属性
    GroupBox控件常用属性只有一个,属性Text,指定GroupBox控件顶部的标题。
    2. RadioButton控件属性和事件
     属性Text:单选按钮控件旁边的标题。
     属性Checked:布尔变量,为true表示按钮被选中,为false表示不被选中。
     事件CheckedChanged:单选按钮选中或不被选中状态改变时产生的事件。
     事件Click:单击单选按钮控件时产生的事件。
    3. 例子e3_8
    该例用RadioButton控件修改Label控件字符串的字体为:宋体、黑体、楷体。具体实现步骤如下:
    (1) 建立一个新的项目。
    (2) 放Label控件到窗体,属性Text=“不同的字体”。字体为宋体。
    (3) 放GroupBox控件到窗体,其属性Text=“选择字体”。
    (4) 放三个RadioButton控件到GroupBox中,其属性Text分别为:宋体、黑体、楷体。宋体RadioButton控件的属性Checked=true。设计好的界面如右图。
    (5) 为三个RadioButton控件的CheckedChanged事件增加事件处理函数如下:
    private void radioButton1_CheckedChanged(object sender, System.EventArgs e)
    { if(radioButton1.Checked)
    label1.Font=new Font("宋体",label1.Font.Size);
    }//label1显示的字体变为宋体,字体大小不变
    private void radioButton2_CheckedChanged(object sender, System.EventArgs e)
    { if(radioButton2.Checked)
    label1.Font=new Font("黑体",label1.Font.Size);
    }
    private void radioButton3_CheckedChanged(object sender, System.EventArgs e)
    { if(radioButton3.Checked)
    label1.Font=new Font("楷体_GB2312",label1.Font.Size);
    }
    (6) 编译,运行,单击RadioGroup1中的三个RadioButton按钮,可以改变字体。注意三个按钮只能选一个,既只能选一种字体。考虑一下,是否可用Click事件。
    3.9 Font类
    Font类有两个构造函数:第一个是new Font(字体名称,字号),例如,label1.Font=new Font("黑体",9),用法还可参考例e3_8。第二个是new Font(字体名称,字号,字体风格),其中第三个参数是枚举类型,具体定义如下:
    enum FontStyle{
    Regular =0,//正常字体
    Bold =1,//黑体
    Italic =2,//斜体
    BoldItalic =3,//黑斜体
    Underline =4,//下划线,5=黑体下划线,6=斜体下划线,7=黑斜体下划线
    Strikeout =8}//删除线,9=黑体删除线,10=斜体删除线,依此类推。
    例如修改标签控件字体为斜体:
    label1.Font=new Font("黑体",9,label1.Font.Style|FontStyle.Italic);
    或者:label1.Font=new Font("黑体",9,label1.Font.Style|(FontStyle)2);
    修改标签控件字体不为斜体:
    label1.Font=new Font("黑体",9,label1.Font.Style&~FontStyle.Italic);
    或者:label1.Font=new Font("黑体",9,label1.Font.Style&(FontStyle)(~2));
    用法还可参考例e3_11。
    3.10 多选框(CheckBox)控件
    CheckBox是多选框控件,可将多个CheckBox控件放到GroupBox控件内形成一组,这一组内的CheckBox控件可以多选,不选或都选。可用来选择一些可共存的特性,例如一个人的爱好。
    1. CheckBox控件属性和事件
     属性Text:多选框控件旁边的标题。
     属性Checked:布尔变量,为true表示多选框被选中,为false不被选中。
     事件Click:单击多选框控件时产生的事件。
     事件CheckedChanged:多选框选中或不被选中状态改变时产生的事件。
    2. 例子e3_10A
    在窗口中增加2个CheckBox控件,分别用来选择是否爱好音乐和是否爱好文学,用鼠标单击CheckBox控件,改变爱好选择,用Label控件显示所选择的爱好。实现步骤如下:
    (1) 建立新项目。放Label控件到窗体,属性Text=“你的爱好是:”。
    (2) 放GroupBox控件到窗体,属性Text=“爱好”。放两个CheckBox控件到GroupBox中,属性Text分别为:音乐、文学。设计界面如下图。
    (3) 标题为音乐的多选框控件的CheckedChanged事件处理函数如下:
    private void checkBox1_CheckedChanged(object sender, System.EventArgs e)
    { String text1="你的爱好是:";
    if(checkBox1.Checked)
    text1=text1+checkBox1.Text;
    if(checkBox2.Checked)
    text1+=checkBox2.Text;
    label1.Text=text1;
    }
    (4) 将标题为文学的多选框控件的CheckedChanged事件处理函数,设置为标题为音乐的多选框控件的CheckedChanged事件处理函数,具体步骤见3.5节。
    (5) 编译,运行。选中音乐将在标签控件中显示:你的爱好是:音乐,再选中文学显示:你的爱好是:音乐文学,…。
    3. 例子e3_10B
    该例同上例,但按选中音乐和文学的顺序在标签中显示爱好,实现步骤如下:
    (1) 建立一个新项目。为Form1类增加私有变量String s="你的爱好是:"。
    (2) 放Label控件、GroupBox控件、两个CheckBox到窗体,属性设置同上例。
    (4)标题为音乐的多选框控件CheckBox1的CheckedChanged事件处理函数如下:
    private void checkBox1_CheckedChanged(object sender,System.EventArgs e)
    { int n=s.IndexOf("音乐");//s中有字符串"音乐"吗?n=-1表示没有
    if(n==-1)//n=-1,表示上次没选此项,此次选中,应增加"音乐"
    s+="音乐";
    else//否则,表示上次已选此项,此次不选中,应删除"音乐"
    s=s.Remove(n,2);
    label1.Text=s;
    }
    (5)标题为文学的多选框控件CheckBox2的CheckedChanged事件处理函数如下:
    private void checkBox2_CheckedChanged(object sender,System.EventArgs e)
    { int n=s.IndexOf("文学");//s中有字符串"文学"吗?=-1表示没有
    if(n==-1)//=-1,表示上次没选此项,此次选中,应增加"文学"
    s+="文学";
    else//否则,表示上次已选此项,此次不选中,应删除"文学"
    s=s.Remove(n,2);
    label1.Text=s;
    }
    (6)编译,运行。选中音乐在标签中显示:你的爱好是:音乐,再选中文学显示:你的爱好是:音乐文学,不选音乐显示:你的爱好是:文学,再选音乐显示:你的爱好是:文学音乐。
    3.11 列表选择控件(ListBox)
    列表选择控件列出所有供用户选择的选项,用户可从选项中选择一个或多个选项。
    1. 列表选择控件的常用属性、事件和方法
     属性Items:存储ListBox中的列表内容,是ArrayList类对象,元素是字符串。
     属性SelectedIndex:所选择的条目的索引号,第一个条目索引号为0。如允许多选,该属性返回任意一个选择的条目的索引号。如一个也没选,该值为-1。
     属性SelectedIndices:返回所有被选条目的索引号集合,是一个数组类对象。
     属性SelectedItem:返回所选择的条目的内容,即列表中选中的字符串。如允许多选,该属性返回选择的索引号最小的条目。如一个也没选,该值为空。
     属性SelectedItems:返回所有被选条目的内容,是一个字符串数组。
     属性SelectionMode:确定可选的条目数,以及选择多个条目的方法。属性值可以使:none(可以不选或选一个)、one(必须而且必选一个)、MultiSimple(多选)或MultiExtended(用组合键多选)。
     属性Sorted:表示条目是否以字母顺序排序,默认值为false,不允许。
     方法GetSelected():参数是索引号,如该索引号被选中,返回值为true。
     事件SelectedIndexChanged:当索引号(即选项)被改变时发生的事件。
    2. 例子e3_11
    根据列表框的选择,为字符串加下划线、删除线、变斜体、变粗体。具体步骤如下:
    (1) 建立一个新项目。放Label控件到窗体,其属性Text=“字体风格”。
    (2) 放置ListBox控件到窗体中,属性Name=listBox1。选中ListBox控件,在属性窗口中,单击Items属性右侧的三个小点,打开字符串集合编辑器对话框,在其中输入四项:粗体、斜体、下划线、删除线,注意每一项要换行。如上图。
    (3) 设置列表选择控件ListBox1属性SelectionMode为MultiExtended,允许多选。
    (4) 为列表选择控件的事件SelectedIndexChenged增加事件处理函数如下:
    private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
    { int Style=0,k=1;//Style=0正常字体,1=黑体,2=斜体,3=黑斜体等,参见3.9节
    for(int i=0;i<listBox1.Items.Count;i++)//此例Count=4,为什么?
    {if(listBox1.GetSelected(i))//例如此例GetSelected(0)=true表示粗体被选中
    Style=Style|k;//增加指定风格
    else
    Style=Style&(~k);//取消指定风格
    k=k*2;
    }
    FontStyle m=new FontStyle();
    m=(FontStyle)Style;
    label1.Font=new Font(label1.Font.Name,9,m);}
    (5) 编译,运行,单选或用Ctrl键多选,看一下效果。运行效果如上图。
    3.12 下拉列表组合框(ComboBox)控件
    控件ComboBox中有一个文本框,可以在文本框输入字符,其右侧有一个向下的箭头,单击此箭头可以打开一个列表框,可以从列表框选择希望输入的内容。现介绍该控件用法。
    1. ComboBox控件的常用属性、事件和方法
     属性DropDownStyle:确定下拉列表组合框类型。为Simple表示文本框可编辑,列表部分永远可见。为DropDown是默认值,表示文本框可编辑,必须单击箭头才能看到列表部分。为DropDownList表示文本框不可编辑,必须单击箭头才能看到列表部分。
     属性Items:存储ComboBox中的列表内容,是ArrayList类对象,元素是字符串。
     属性MaxDropDownItems:下拉列表能显示的最大条目数(1—100),如果实际条目数大于此数,将出现滚动条。
     属性Sorted:表示下拉列表框中条目是否以字母顺序排序,默认值为false,不允许。
     属性SelectedItem:所选择条目的内容,即下拉列表中选中的字符串。如一个也没选,该值为空。其实,属性Text也是所选择的条目的内容。
     属性SelectedIndex:编辑框所选列表条目的索引号,列表条目索引号从0开始。如果编辑框未从列表中选择条目,该值为-1。
     事件SelectedIndexChanged:被选索引号改变时发生的事件。
    2. 例子e3_12 选择Windows操作系统提供的所有字体
    增加一个ComboBox控件,用来选择字符串使用的字体名。本例提供方法使控件ComboBox的下拉列表中显示Windows操作系统中使用的所有字体名。运行效果如右图。实现步骤如下:
    (1) 建立新项目。放Label控件到窗体,其属性Text=“选择不同字体”。
    (2) 放ComboBox控件到窗体中,属性Name=comboBox1,属性DropDownStyle=DropDownList,不能在编辑框中输入字体名,只能从下拉列表中选取。
    (3) 为窗体Form1的事件Load增加事件处理函数如下:
    private void Form1_Load(object sender, System.EventArgs e)
    {//Families是类FontFamily的一个静态属性,得到操作系统中所使用的所有字体名
    FontFamily[] families=FontFamily.Families;//静态属性没有类的对象也可使用
    foreach (FontFamily family in families)
    comboBox1.Items.Add(family.Name);//注意Add方法的使用
    }
    (4) 为comboBox1的事件SelectedIndexChenged增加事件处理函数如下:
    private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
    {label1.Font=new Font(comboBox1.Text,9);}
    (5) 编译,运行,在下拉列表中选择不同字体名,标签的字体变为选择的字体。从下拉列表中可以看到操作系统中的所有字体名称已经在列表中。
    3.13 ToolTip控件
    在一些Windows应用程序中,例如Word程序,当鼠标在工具条的按钮上停留一段时间后,会在旁边出现提示,ToolTip控件就是为实现此功能的。可以用ToolTip控件为任何控件增加提示,本节介绍该控件的使用方法。
    例子e3_13 为Button控件增加提示
    (1) 建立一个新项目。放Button控件到窗体,Name属性为Button1。
    (2) 把toolTip控件放到窗体中,属性Name=ToolTip1。
    (3) 在Form1的构造函数中,增加语句如下:
    toolTip1.SetToolTip(button1,"这是一个按钮");
    (4) 编译,运行,当鼠标在Button上停留一段时间后,会在旁边出现提示:这是一个按钮。
    3.14 超级链接(LinkLable)控件
    控件LinkLable是控件Label的派生类,和控件Label不同的是显示的字符有下划线,可以为LinkLable控件的LinkClicked事件增加事件处理函数,当鼠标指向LinkLable控件,鼠标形状变为手形,单击该控件,调用这个事件处理函数,可以打开文件或网页。
    1. 超级链接控件的属性、方法和事件
     属性LinkColor:用户未访问过的链接的字符颜色,默认为蓝色。
     属性VisitedLinkColor:用户访问链接后的字符颜色。
     属性LinkVisited:如果已经访问过该链接,则为true;否则为false。
     属性LinkArea: 是一个结构,变量LinkArea.Start表示字符串中开始加下划线的字符位置,LinkArea.Length表示字符串中加下划线字符的个数。
     事件LinkClicked:单击控件LinkLable事件。
    2. 例子e3_14:用LinkLabel控件超级链接到微软网站。
    (1) 建立一个新工程。放LinkLabel控件到窗体,属性Text=“介绍微软的操作系统”。
    (2) 修改LinkLabel控件属性LinkArea.Length=2,LinkArea.Start=2。也可在构造函数用语句修改:linkLabel1.LinkArea=new LinkArea(2,2);
    (3) 为LinkLabel控件的事件LinkClicked增加事件处理函数:
    private void linkLabel1_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
    { linkLabel1.LinkVisited=true;
    System.Diagnostics.Process.Start("http://www.micosoft.com.cn");
    }
    (4) 运行,效果如右图,注意只有字符微软带下划线。单击微软,打开浏览器访问微软主页。
    (5) 如果要打开一个窗口,列出C盘根目录下的文件及文件夹,LinkLabel控件事件LinkClicked事件处理函数修改如下:
    linkLabel1.LinkVisited=true;
    System.Diagnostics.Process.Start("C:/");
    (6) 如果要打开指定程序,例如打开记事本程序,修改LinkClicked事件处理函数如下:
    linkLabel1.LinkVisited=true;
    System.Diagnostics.Process.Start("notepad");
    3.15 定时(Timer)控件
    定时控件(Timer)也叫定时器或计时器控件,是按一定时间间隔周期性地自动触发事件的控件。在程序运行时,定时控件是不可见的。
    3. 定时控件的属性、方法和事件
     属性Interval:周期性地自动触发事件的时间间隔,单位为毫秒。
     属性Enabled:为true,启动定时器。调用方法Start()也可启动定时器。
     方法Start()和Stop():启动和停止定时器。设置属性Enabled=false也可停止定时器。
     事件Tick:每间隔属性Interval指定的时间,产生事件Tick。
    4. 例子e3_15用标签控件显示当前日期和时间
    (1) 建立一个新项目。放Timer组件到窗体,Name属性为timer1。
    (2) 放Label控件到窗体,Name属性为label1。
    (3) 为窗体Form1的事件Load增加事件处理函数如下:
    private void Form1_Load(object sender, System.EventArgs e)
    { this.timer1.Interval=100;
    this.timer1.Enabled=true;
    label1.Text=DateTime.Now.ToString();
    }
    (4) 为Timer1的Tick事件增加事件处理函数如下:
    private void timer1_Tick(object sender, System.EventArgs e)
    { label1.Text=DateTime.Now.ToString();
    }
    (5) 编译,运行,标签控件位置显示日期和时间。运行效果如上图。
    3.16 DateTime类
    DateTime类中提供了一些静态方法,可以用来得到日期、星期和时间,下面是一些常用的方法。
     得到日期和时间,并转换为字符串。
    String s=DateTime.Now.ToString();//或DateTime.Today.ToString()
     得到年、月和日期
    int y=DateTime.Now.Year;//得到年
    int m=DateTime.Now.Month;//得到月
    int d=DateTime.Now.Day;//得到日期
    String s=DateTime.Now.DayOfWeek.ToString();//英文表示的星期
     得到小时、分和秒
    int h=DateTime.Now.Hour;//得到小时
    int m=DateTime.Now.Minute;//得到分
    int s=DateTime.Now.Second;//得到秒
     定义一个DateTime类对象,表示1999年1月13日3时57分32.11秒
    System.DateTime moment=new System.DateTime(1999,1,13,3,57,32,11);
     加法和减法(减法请读者自己完成)
    System.DateTime dTime=new System.DateTime(1980,8,5);//1980年8月5日
    //时间间隔,17天4小时2分1秒
    System.TimeSpan tSpan=new System.TimeSpan(17,4,2,1);
    System.DateTime result=dTime+tSpan;//结果是:1980年8月22日4:2:1 AM.
    3.17 菜单
    Windows应用程序一般都有一个菜单,通过选择菜单中的不同菜单项,完成指定的功能。使用主菜单控件MainMenu可以很容易建立windows应用程序的主菜单。
    1. 菜单的组成及功能
    放主菜单控件MainMenu到窗体中,可以为窗体增加一个主菜单。主菜单一般包括若干顶级菜单项,例如,文件、编辑、帮助等。单击顶级菜单项,可以出现弹出菜单,弹出菜单中包含若干菜单项,例如单击文件顶级菜单项,其弹出菜单一般包括打开文件、存文件、另存为等菜单项,用鼠标单击菜单项,可以执行菜单项命令。有的菜单项还包括子菜单。
    所有菜单项都可以有快捷键,即菜单项中带有下划线的英文字符,当按住ALT键后,再按顶级菜单项的快捷键字符,可以打开该顶级菜单项的弹出菜单。弹出菜单出现后,按菜单项的快捷键字符,可以执行菜单项命令。增加快捷键的方法是在菜单项的标题中,在要设定快捷键英文字符的前边增加一个字符&,例如,菜单项的标题为:打开文件(&0),菜单项的显示效果为:打开文件(0)。菜单项可以有加速键,一般在菜单项标题的后面显示,例如,菜单项打开文件的加速键一般是Ctrl+O,不打开菜单,按住Ctrl键后,再按O键,也可以执行打开文件命令。设定加速键的方法是修改菜单项的ShortCut属性。
    2. 用程序生成菜单
    放主菜单控件MainMenu到窗体中,可以为该窗体增加一个主菜单,Visual Studio.Net自动添加如下语句:
    MainMenu mainMenu1=new MainMenu();
    This.Menu=mainMenu1;//指定主窗口的主菜单是mainMenu1。
    可以建立多个MainMenu类对象,用第二条语句修改使主窗口使用不同的主菜单。有了主菜单对象,用如下语句为主菜单增加顶级菜单项:
    MenuItem myFile=mainMenu1.MenuItem.Add(“文件(&F)”);//顶级菜单项:文件
    有了顶级菜单项对象,用如下语句为顶级菜单项的弹出菜单增加菜单项:
    myFile.MenuItem.Add(“打开(&O)”);//文件顶级菜单项的弹出菜单的菜单项:打开
    实际上,这些都可以用Visual Studio.Net自动生成。
    3. 菜单项的属性和事件
     属性Checked:布尔变量,=true,表示菜单项被选中,其后有标记:∨。
     属性ShortCut:指定的加速键,可以从下拉列表中选择。
     属性ShowShortCut:布尔变量,true(默认值),表示显示加速键,false,不显示。
     属性Text:菜单项标题。如为字符-,为分隔线。如指定字符前加&,例如:颜色(&c),增加快捷键,即用Alt+c访问颜色菜单。
     常用事件Click:单击菜单项事件。
    4. 例子e3_17 增加菜单
    本例在窗体中建立主菜单,主菜单包括一个顶级菜单项:颜色,其弹出菜单包括两个菜单项:红色、黑色,单击标题为红色的菜单项,把窗体中显示的字符串变为红色,单击标题为黑色的菜单项,把窗体中显示的字符串变为黑色。实现步骤如下:
    (1) 建立一个新项目。放Label控件到窗体。
    (2) 双击工具箱中Mainmenu控件,在窗体中增加主菜单。右下角有一主菜单图标,在左上角有一方框,其中有文字:请在此处输入,在此方框中输入菜单标题。
    (3) 在方框内输入字符”颜色”,在其下部方框内输入字符”红色”为一菜单项,在”红色”下输入字符”黑色”为另一菜单项,再输入”退出”菜单项。如希望在选中某一菜单项后出现下一级子菜单,可在菜单项右侧方框中输入子菜单项名。如果菜单项属性Text的值为-,则菜单项为分隔符。可以用鼠标拖动菜单项移动菜单项的位置。集成环境设计界面如下图。

    (4) 标题为红色的菜单项的单击(Click)事件处理函数如下:
    private void menuItem2_Click(object sender,System.EventArgs e)
    {label1.ForeColor=Color.Red;}//改变字体颜色为红色
    (5) 标题为黑色的菜单项的单击(Click)事件处理函数如下:
    private void menuItem3_Click(object sender, System.EventArgs e)
    {label1.ForeColor=Color.Black;}//改变字体颜色为黑色
    (6) 标题为退出的菜单项的单击(Click)事件处理函数如下:
    private void menuItem4_Click(object sender, System.EventArgs e)
    { Close();}//退出程序
    (7) 编译,运行,单击红色和黑色菜单项,能改变字符串的颜色。效果如上图。
    3.18 工具条
    一般Windows应用程序都有一个工具条,可以认为工具条上的按钮为菜单的某一菜单项的快捷按钮,单击工具条按钮相当于单击相应菜单项,完成同样的功能。
    1. 工具条的组成及功能
    放工具条控件ToolBar到窗体中,可以为该窗体增加一个工具条。在工具条中可以增加Button按钮和其它控件,例如象Word程序的工具条中用下拉列表控件(ComboBox)选择字号、字体等。一般工具条按钮上都有一个图标,提示用户该按钮的使用功能。按钮的所有图标存放到ImageList类对象中。单击任何一个按钮,都产生工具条控件的ButtonClick事件,在这个事件处理事件函数中,要用语句区分用户单击了那一个按钮,以完成相应的功能。
    2. 控件ToolBar的属性、事件和方法
     属性BorderStyle:边界风格,=None(默认值),无边界;=FixedSingle,单线边界;=Fixed3D,立体风格边界。
     属性Button:集合属性,存储ToolBar的按钮对象。单击其后的按钮,可以打开ToolBarButton集合编辑器对话框(见下图),增加或删除按钮,修改按钮属性。

     属性ImageList:指定一个ImageList类对象,该对象中可以存储若干图标,这些图标作为ToolBar控件按钮的图标。
     属性Wrappable:布尔变量,=true(默认值),当窗体Form水平尺寸小于工具条的水平尺寸时,一行不能显示所有按钮,允许下一行显示;=false,不允许。
     事件ButtonClick:ToolBar控件的单击事件。在ButtonClick事件处理事件函数中,要用语句区分用户单击了那一个按钮,以完成相应的功能。
     属性ShowToolTips:布尔变量,=true,允许显示提示信息。
     方法IndexOF():参数为ToolBar控件中按钮的属性Name,返回其索引值。
    3. ToolBar控件中ToolBarButton按钮的属性
    ToolBar控件中ToolBarButton按钮可以看作独立的控件,它有自己独立的属性。下面介绍ToolBar控件中ToolBarButton按钮的属性。
     属性ImageIndex:ToolBar控件属性ImageList指定一个ImageList类对象,该对象中的图标作为ToolBar控件按钮的图标。这个属性指定本按钮使用ImageList类对象中存储的第几个图标。
     属性Style:有4个值,=PushButton,为普通按钮;=Separator,为一分割符,再左边和右边的两个按钮中间增加一个间隙;=ToggleButton,开关按钮,单击该按钮,按钮被按下,不抬起,再单击,抬起。=DropDownButton,下拉按钮,按钮右侧有一个下拉箭头,单击下拉箭头,可以弹出下拉列表。
     属性Text:ToolBar控件中按钮除了有图标外,还可以有属性Text指定的文字。
     属性ToolTipText:当鼠标在工具条按钮上停留一段时间后,将在工具条按钮旁边出现此属性指定的提示。
    4. 例子e3_18
    现为上例的菜单增加工具条,有两个按钮,单击按钮分别使字体变红、变黑。步骤如下:
    (1) 继续菜单的例子,放ImageList控件到窗体。
    (2) 放ToolBar控件到窗体。修改属性ImageList=ImageList1。
    (3) 单击ImageList属性Images后按钮,打开Image集合编辑器,单击添加按钮,打开选择文件对话框。按指定路径选择图标的文件后,单击确定按钮,增加图标到ImageList对象中。在C:\Program Files\Microsoft Office\Office\forms\2052文件夹和C:\program files\Microsoft Visual Studio.Net\Common7\Graphics\Icon\Misc文件夹中有若干图标。也可用画笔程序自己设计图标,图标的宽和高应比工具条按钮的宽和高略小,存为.ico文件。也可以用抓图软件抓其它程序的图标。任选以上方法,为ImageList对象增加两个图标。
    (4) 单击ToolBar控件属性Buttons后按钮,打开ToolBarButton集合编辑器(见上图),单击添加按钮,增加一个按钮,从其属性ImageIndex后的下拉列表中选择按钮使用的图标,设置按钮的ToolTipText属性为:改变字体为红色,为工具按钮增加提示。同样方法增加第二个按钮,按钮的ToolTipText属性为:改变字体为黑色。
    (5) 设定ToolBar控件属性ShowToolTips为true。
    (6) 为ToolBar控件的ButtonClick事件增加事件函数如下:
    private void toolBar1_ButtonClick(object sender,
    System.Windows.Forms.ToolBarButtonClickEventArgs e)
    { int n=toolBar1.Buttons.IndexOf(e.Button);//n为工具条中被单击按钮的序号
    switch(n)
    { case 0://第一个按钮,调用相应的菜单项的事件处理函数。
    this.menuItem3_Click(sender,e);
    break;
    case 1://第二个按钮
    this.menuItem2_Click(sender,e);
    break;
    }
    }
    (7) 编译,运行,单击两个工具条按钮,可以分别使字体变为红色或黑色。见上图。
    3.19 状态栏(StatusBar)控件
    Windows应用程序的状态栏一般用来显示一些信息,如时间,鼠标位置等。
    1. 状态栏控件的属性
     属性Panels:集合属性,存储状态栏中的各个分栏对象。单击其后标题为…的按钮,可以打开StatusBarPanels集合编辑器对话框,增加或删除分栏,修改分栏属性。
     属性ShowPanel:布尔变量,=true,允许显示多栏;=false,不允许。
    2. 状态栏(StatusBar)控件分栏的属性
    状态条可以为单栏,也可以为多栏。属性Text,表示在状态栏中显示的内容。如为单栏,在单栏中显示字符串的语句是:statusBar1.Text=”在单栏中显示的文本”,如为多栏,在第2栏中显示字符串的语句是:statusBar1.Panels[1].Text=”在第2栏中显示的文本”。
     属性Alignment:对齐方式,可以为左对齐、右对齐和中间对齐。
     属性Text:表示在状态栏中显示的内容。
     属性Width:栏的宽度。
     属性BorderStyle:指定状态栏控件上 每个分栏的边框外观。边界风格,=None(默认值),不显示边框;=Raised,三维凸起边框;=Sunken,三维凹陷边框显示。
    3. 例子e3_19 为窗体增加状态条,在状态条内显示时间和鼠标位置。
    (1) 建立新项目。放StatusBar控件到窗体。单击StatusBar控件属性Panels后按钮,打开StatusBarPanels集合编辑器(如下图),单击添加按钮,增加若2栏。其序号为0、1。

    (2) 修改StatusBar控件属性ShowPanel=true。
    (3) 放Timer组件到窗体,Name=Timer1,属性Interval=1000,Enabled=true。
    (4) 为Timer1的Tick事件增加事件处理函数如下:
    private void timer1_Tick(object sender, System.EventArgs e)
    { statusBar1.Panels[0].Text=DateTime.Now.ToString();
    }
    (5) 为Form1的MouseMove事件增加事件处理函数如下:
    private void Form1_MouseMove(object sender,System.Windows.Forms.MouseEventArgs e)
    {statusBar1.Panels[1].Text="X:"+e.X.ToString()+",Y:"+e.Y.ToString();
    }
    (6) 编译,运行,如右图,在第1栏中可以看到当前时间,在窗口中移动鼠标,在第2栏中可以看到鼠标的位置不断变化。
    3.20 鼠标事件
    从类System.Windows.Forms.Control派生的控件都有鼠标事件,控件的Click事件本质上也是鼠标事件。一些控件还有单独的鼠标事件,例如Form。鼠标事件有:
     MouseDown:如果鼠标位于控件区域,按下鼠标按键时产生该事件。
     MouseUp:如果鼠标位于控件区域,抬起鼠标按键时产生该事件。
     MouseMove:如果鼠标在控件区域移动,产生该事件。
     MouseEnter:鼠标进入控件区域,产生该事件。
     MouseLeave:鼠标离开控件区域,产生该事件。
    鼠标事件处理函数一般有两个参数,第一个参数(object sender)是产生该事件的对象的属性Name的值,例如,为Form1的MouseDown事件增加事件函数,单击Form1,第一个参数sender代表Form1对象。(System.Windows.Forms.MouseEventArgs e)是事件处理函数第二个参数,代表事件的一些信息,事件不同,所代表的信息也不相同,鼠标按下事件处理函数中,e.X为发生事件时鼠标位置的x坐标,e.Y为发生事件时鼠标位置的y坐标,e.Button为MouseButtons.Left,表示单击了鼠标左键等等,Right和Middle则分别代表右键和中间键。e.Clicks为鼠标单击的次数,如果大于2次,则为双击。
    例子e3_20:在窗体中的指定区域,双击鼠标左键,用Label控件显示双击鼠标的位置。指定区域的左上角坐标为(20,20),宽为200,高为200。
    (1) 建立一个新项目。放Label控件到窗体。属性Name=label1。
    (2) Panel控件可以将窗体分为多个区域。放Panel控件到窗体,属性Location.X=20,Location.Y=20,属性Width=200,Height=200,属性Name=p1。
    (3) 为Panel的MouseDown事件增加事件函数如下:
    private void p1_MouseDown(object sender,System.Windows.Forms.MouseEventArgs e)
    { if(e.Button==MouseButtons.Left&&e.Clicks>1)//如果是双击左键
    label1.Text="X:"+e.X.ToString()+",Y:"+e.Y.ToString();
    }
    (4) 编译,运行,分别在指定区域和区域外双击鼠标左键,看一下效果。分别在指定区域和区域外双击鼠标右键,看一下效果。
    3.21 快捷菜单(ContextMenu)
    使用过Word程序的人都知道,在其程序窗口的不同位置单击右键,会出现不同弹出菜单,这个弹出菜单叫快捷菜单,这节介绍如何在应用程序中增加快捷菜单。快捷菜单和主菜单的属性、事件和方法基本一致,只是快捷菜单没有顶级菜单项,因此这里就不多介绍了。
    例子e3.21
    例子在窗口中显示一行字符串,加入两个按纽,单击按纽button1把字符串变为红色,单击按纽button2把字符串变为黑色。为两个按钮建立快捷菜单,快捷菜单中有2个菜单项,单击菜单项把字符串变为红色或黑色。为窗体建立快捷菜单,菜单中仅有1个退出菜单项,单击退出菜单项,退出程序。具体实现步骤如下:
    (1) 建立一个新项目。放Label控件到窗体。
    (2) 放2个Button控件到窗体,标题(属性Text)分别为红色,黑色。
    (3) 标题为红色的按钮的单击事件处理函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { label1.ForeColor=Color.Red;}
    (4) 标题为黑色的按钮的单击事件处理函数如下:
    private void button2_Click(object sender, System.EventArgs e)
    { label1.ForeColor=Color.Black;}
    (5) 放2个ContextMenu控件到窗体,属性Name分别为contextMenu1,contextMenu2。
    (6) 选中contextMenu1控件,在菜单编辑器中增加两个标题分别为红色和黑色的菜单项,它们的单击事件处理函数分别是单击红色按钮和单击黑色按钮的事件处理函数。
    (7) 选中contextMenu2控件,在菜单编辑器中增加标题为退出的菜单项,并为其增加单击事件处理函数,为事件处理函数增加语句:Close();
    (8) 将红色按钮和黑色按钮的属性ContextMenu指定为contextMenu1。Form的属性ContextMenu指定为contextMenu2。
    (9) 编译,运行,右击标题为红色的按钮,快捷菜单contextMenu1打开,单击快捷菜单中标题为红色的菜单项,将使窗体显示的字符串颜色变为红色,右击标题为黑色的按钮,快捷菜单contextMenu1打开,单击快捷菜单中标题为黑色的菜单项,将使窗体显示的字符串颜色变为黑色,右击窗体,快捷菜单contextMenu2打开,单击快捷菜单中标题为退出的菜单项,将退出应用程序。运行效果如上图。
    3.22 综合例子:计算器
    具体步骤如下:
    (1) 建立一个新项目。Form属性MaxiMizeBox=false,属性MiniMizeBox=false。属性FormBorderStyle=FixedDialog,窗口不能修改大小。
    (2) 放textBox控件到窗体,属性Name=textBox1,属性Text="0",属性ReadOnly=true。
    (3) 增加10个Button控件,前9个按钮属性Name分别为:Button1-Button9,最后一个为Button0,属性Text分别为:1、2、3、4、5、6、7、8、9、0。
    (4) 增加7个Button控件,属性Name分别为:btn_dot、btn_equ、btn_add、btn_sub、btn_mul、btn_div、btn_C,属性Text分别为:.、=、+、-、*、/、C。设计界面如下图。
    (5) 控件Button0单击事件处理函数如下:
    private void button0_Click(object sender, System.EventArgs e)
    { if(sender==button0) append_num(0);
    if(sender==button1) append_num(1);
    if(sender==button2) append_num(2);
    if(sender==button3) append_num(3);
    if(sender==button4) append_num(4);
    if(sender==button5) append_num(5);
    if(sender==button6) append_num(6);
    if(sender==button7) append_num(7);
    if(sender==button8) append_num(8);
    if(sender==button9) append_num(9);
    }
    (6) 为Form1类增加方法如下:
    public void append_num(int i)
    { if(textBox1.Text!="0")
    textBox1.Text+=Convert.ToString(i);
    else
    textBox1.Text=Convert.ToString(i);
    }
    (7) 将Button1-Button9的单击事件处理函数设定为Button0单击事件处理函数。
    (8) 为标题为.按钮增加事件处理函数如下:
    private void btn_dot_Click(object sender, System.EventArgs e)
    { int n=textBox1.Text.IndexOf(".");
    if(n==-1)//如果没有小数点,增加小数点,否则不增加
    textBox1.Text=textBox1.Text+".";
    }
    (9) 编译,单击数字按钮,在textBox1可以看到输入的数字,也可以输入小数。
    (10) 先实现加法,必须定义一个浮点类型变量sum,初始值为0,记录部分和。
    (11) 输入了第一个加数,然后输入任一运算符(+、-、*、\或=),应首先清除编辑框中显示的第一个加数,才能输入第二个加数。为实现此功能,必须定义一个布尔变量blnClear,初始值为false,表示输入数字或小数点前不清除编辑框中显示,输入运算符(+、-、*、\或=)后,blnClear=true,表示再输入数字或小数点先清除编辑框中显示。修改前边程序,输入数字或小数点前,要判断变量blnClear,如为true,清除编辑框中显示的内容后,再显示新输入的数字或小数点,同时修改blnClear=false。为此修改append_num方法如下:
    public void append_num(int i)
    { if(blnClear)//如果准备输入下一个加数,应先清除textBox1显示内容
    { textBox1.Text="0";//阴影部分为新增语句
    blnClear=false;
    }
    if(textBox1.Text!="0")
    textBox1.Text+=Convert.ToString(i);
    else
    textBox1.Text=Convert.ToString(i);
    }
    (12) 修改btn_dot_Click方法如下:
    private void btn_dot_Click(object sender, System.EventArgs e)
    { if(blnClear) //如果准备输入下一个数,应先清除textBox1显示内容
    { textBox1.Text="0";//阴影部分为新增语句
    blnClear=false;
    }
    int n=textBox1.Text.IndexOf(".");
    if(n==-1)//如果没有小数点,增加小数点,防止多次输入小数点
    textBox1.Text=textBox1.Text+".";
    }
    (13) 如果计算1+2-3的运算结果,先单击按钮1,编辑框中显示1,再单击按钮+,执行运算sum=sum+1(注意此时sum=0),显示sum到编辑框中(实际显示不变),记住此次输入的运算符,这里为+号。单击按钮2,编辑框中显示2,再单击按钮-,按记录的运算符(这里是+)计算sum=sum+2,显示sum到编辑框中,记住此次输入的运算符,这里为-号,依此类推。为实现此功能,必须定义一个字符串变量strOper,记录输入的运算符,初始值为"+",保证输入第一个运算符后,执行运算sum=sum+第一个加数,由于初始sum=0,也就是sum=第一个加数。标题为+的按钮的单击事件处理函数如下:
    private void btn_add_Click(object sender, System.EventArgs e)
    { double dbSecond=Convert.ToDouble(textBox1.Text);
    if(!blnClear)//如果未输入第二个操作数,不运算
    switch(strOper)//按记录的运算符号运算
    { case "+":
    sum+=dbSecond;
    break;
    //在此增加其它运算符-、*、\代码
    }
    if(sender==btn_add)
    strOper="+";
    //在此增加运算符-、*、\、=代码
    textBox1.Text=Convert.ToString(sum);
    blnClear=true;
    }
    (14) =号处理语句和+号处理基本一致,修改标题为+按钮的事件函数如下:
    private void btn_add_Click(object sender, System.EventArgs e)
    { double dbSecond=Convert.ToDouble(textBox1.Text);
    if(!blnClear)//如果未输入第二个操作数,不运算
    switch(strOper)//按记录的运算符号运算
    { case "+":
    sum+=dbSecond;
    break;
    //在此增加运算符-、*、\代码
    }
    if(sender==btn_add)
    strOper="+";
    if(sender==btn_equ)//为=号处理增加的语句
    strOper="=";
    textBox1.Text=Convert.ToString(sum);
    blnClear=true;
    }
    将btn_equ按钮的单击事件函数设定为+按钮的单击事件函数。
    (15) 为标题为C按钮增加事件函数如下:
    private void btn_C_Click(object sender, System.EventArgs e)
    { textBox1.Text="0";
    sum=0;
    blnClear=false;
    strOper="+";
    }
    (16) 请读者自己补上减法,乘法,除法运算的语句。
    习题:
    (1) 在窗口中显示一行字符串,加入两个按纽,单击按纽1把字符串改为红色,单击按纽2把字符串改为黑色。使字符串为红色时红色按纽不能使用,字符串为黑色时黑色按纽不能使用。(提示:可以修改按钮的属性Enabled为false使其不能使用。)
    (2) 将上题改为用按扭修改字体的大小,分别为大字体和小字体。(参见3.9节)
    (3) 加一文本框控件和一按纽,单击按纽将文本框控件输入内容显示标签控件上。(提示:单击按钮事件处理函数中加语句label1.Text=textBox1.Text)。
    (4) 修改上题,使文本框控件和标签控件文本同步显示(提示:文本框控件的TextChanged事件处理函数中加语句label1.Text=textBox1.Text)。
    (5) 加一文本框控件和一按纽,单击按纽将文本框控件输入的文本中选中的内容显示在标签控件上(提示:单击按钮事件处理函数中加语句label1.Text=textBox1.SelText。)
    (6) 加一文本框控件和一按纽,单击按纽将文本框控件输入的文本的字符、选中的内容的字符数和选中的内容的开始位置显示在标签控件上。
    (7) 用控件RadioButton选择性别,把选择的结果用Label控件显示出来。
    (8) 例子e3_8中如改为响应单击事件Click,可能出现什么问题?
    (9) 用控件ComboBox修改标签控件字体的大小。(用属性Item在下拉列表中输入大小)。
    (10) 放ListBox控件到窗体中,属性Name=listBox1。列表框有三项分别为:苹果,梨子,香蕉。允许多选。标签控件同步显示ListBox控件所做的选择。提示:为ListBox控件的SelectedIndexChenged事件增加事件函数,
    label1.Text="所选择的是:";
    for(int i=0;i<listBox1.SelectedIndices.Count;i++)
    label1.Text+=listBox1.SelectedItems[i].ToString()+",";
    (11) 放ListBox、TextBox和3个Button控件到窗体中,属性Name分别为listBox1、textBox1、Button1、Button2、Button3。Button控件属性Text分别为:增加、删除、清空。单击增加按钮,把textBox中输入的内容作为一个条目增加到listBox1中,单击删除按钮,删除listBox1中所选择的条目,单击清空按钮,清除listBox1所有条目。提示:增加用语句:listBox1.Items.Add(textBooooox1.Text)。删除所选择的条目用语句:listBox1.Items.RemoveAt(listBox1.SelectedIndex)。清除listBox1所有条目用语句:listBox1.Items.Clear()。
    (12) 在窗体中显示字符,每隔1秒字体变大些,变到一定尺寸后,每隔1秒字体变小些,如此循环。增加一个按钮,可以启动和停止字符串字体大小变化,按钮标题给出正确提示。
    (13) 在窗体中显示字符,每隔1秒字符移动一定距离,先右移,移到右边界,再左移,移到左边界,又一次右移,如此循环。(提示:修改Label的Left属性值。)
    (14) 修改例子e3_17,使显示字符串为红色时,标题为红色的菜单项无效;使显示字符串黑色时,标题为黑色的菜单项无效。
    (15) 修改例子e3_17,使显示字符串为红色时,标题为红色的菜单项前增加选中标志;使显示字符串黑色时,标题为黑色的菜单项前增加选中标志。
    (16) 为例e3_17的菜单项增加加速键,键入Alt+c打开顶级菜单项颜色的弹出菜单,弹出菜单打开后,键入B执行标题为黑色的菜单项命令,键入R执行标题为红色的菜单项命令。
    (17) 为例子e3_17的菜单项定义加速键(属性Shortcut),键入ctrl+r使显示字符串为红色,键入ctrl+b使显示字符串黑色。
    (18) 为例子e3_17顶级菜单项颜色增加单击事件处理函数,在事件处理函数中判断显示的字符串的颜色,决定是否为相应的菜单项增加选中标志。
    (19) 拖动鼠标左键时,在状态栏中显示鼠标的位置。
    (20) 模拟画笔程序,在左侧增加工具按钮,在下部增加颜色按钮。
    (21) 在工具栏中加三个按钮,单击按钮时,按钮保持按下状态,再单击按钮,按钮抬起。在按下状态,使标签控件中字符串加下画线、斜体或加粗,抬起则取消。
    (21) 工具栏中按钮的属性Style设置为ToolBarButtonStyle.DropDownButton,按钮可有一个下拉菜单。首先创建一个ContextMenu菜单,指定工具栏中按钮的属性DropDownMenu的值为创建的ContextMenu菜单对象,将在按下按钮时显示这个菜单。请用工具栏中按钮的下拉菜单实现使标签控件字符的颜色变为红色、黑色。(提示:工具栏中按钮的属性Style设置为ToggleButton。属性Pushed是一个布尔变量,表示工具栏按钮当前是否处于按下状态)
    (22) 用工具栏中按钮的下拉菜单实现使标签控件字符的颜色变为红色、黑色。(提示:如工具栏中按钮的属性Style设置为DropDownButton,按钮可有一个下拉菜单。首先创建一个ContextMenu菜单,指定工具栏中按钮的属性DropDownMenu的值为创建的ContextMenu菜单对象,将在按下按钮时显示这个菜单。)
    (23) 完成计算器的减法和乘除程序。增加求平方,对数等功能。(例如Math.Sqrt())


    第四章 文本编辑器的实现
    本章的目的是建立一个文本编辑器,同时继续介绍控件的用法。有两类文本编辑器:单文档文本编辑器和多文档文本编辑器,单文档文本编辑器一次只允许打开一个文件,如果要打开另一个文件,必须关闭当前打开的文件,微软的写字板程序就是一个典型的单文档字处理程序。多文档文本编辑器同时允许打开多个文件,每个文件占用一个子窗口,微软的Word程序就是一个典型的多文档字处理程序。本章首先介绍建立一个单文档文本编辑器的方法,然后介绍建立多文档文本编辑器的方法。
    4.1 用RichTextBox控件实现文本编辑器
    RichTextBox控件可以用来输入和编辑文本,该控件和TextBox控件有许多相同的属性、事件和方法,但比TextBox控件的功能多,除了TextBox控件的功能外,还可以设定文字的颜色、字体和段落格式,支持字符串查找功能,支持rtf格式等。这里只介绍在TextBox控件中没有介绍的属性、事件和方法,相同部分就不介绍了,可参见TextBox控件。RichTextBox控件的属性、事件和方法如下:
     属性Dock:很多控件都有此属性,它设定控件在窗体中的位置,可以是枚举类型DockStyle的成员None、Left、Right、Top、Bottom或Fill,分别表示在窗体的任意位置、左侧、右侧、顶部、底部或充满客户区。在属性窗口中,属性DOCK的值用周边5个矩形,中间一个矩形的图形来表示。
     属性SelectedText:获取或设置RichTextBox控件内的选定文本。
     属性SelectionLength:获取或设置RichTextBox控件中选定文本的字符数。
     属性SelectionStart:获取或设置RichTextBox控件中选定的文本起始点。
     属性SelectionFont:如果已选定文本,获取或设置选定文本字体,如果未选定文本,获取当前输入字符采用字体或设置以后输入字符采用字体。
     属性SelectionColor:如果已选定文本,获取或设置选定文本的颜色,如果未选定文本,获取当前输入字符采用的颜色或设置以后输入字符采用的颜色。
     属性Lines:记录RichTextBox控件中所有文本的字符串数组,每两个回车之间字符串是数组的一个元素。
     属性Modified:指示用户是否已修改控件的内容。为true,表示已修改。
     事件SelectionChange:RichTextBox控件内的选定文本更改时发生的事件。
     事件TextChanged:RichTextBox控件内的文本内容改变时发生的事件。
     方法Clear():清除RichTextBox控件中用户输入的所有内容,即清空属性Lines。
     方法Copy()、Cut()、Paste():实现RichTextBox控件的拷贝、剪贴、粘贴功能。
     方法SelectAll():选择RichTextBox控件内的所有文本。
     方法Find():实现查找功能。从第二个参数指定的位置,查找第一个参数指定的字符串,并返回找到的第一个匹配字符串的位置。返回负值,表示未找到匹配字符串。第三个参数指定查找的一些附加条件,可以是枚举类型RichTextBoxFinds的成员:MatchCase(区分大小写)、Reverse(反向查找)等。允许有1个、2个或3个参数。
     方法SaveFile():存文件,它有2个参数,第一个参数为要存文件的全路径和文件名,第二个参数是文件类型,可以是:纯文本,RichTextBoxStreamType.PlainText;Rtf格式流,RichTextBoxStreamType.RichText;采用Unicode编码的文本流,RichTextBoxStreamType.UnicodePlainText。
     方法LoadFile():读文件,参数同方法SaveFile(),注意存取文件的类型必须一致。
     方法Undo():撤消RichTextBox控件中的上一个编辑操作。
     方法Redo():重新应用RichTextBox控件中上次撤消的操作。
    4.2 实现文本编辑器的剪贴板功能
    许多程序都支持剪贴板功能。通过剪贴板可以完成数据的剪贴(Cut),复制(Copy),粘贴(Paste)等功能。剪贴板可以理解为一块存储数据的公共区域,用户可以把数据复制或剪贴到剪贴板中,本任务或其它任务要用剪贴板中的数据时,可以用粘贴功能从剪贴板中把数据取出。存入剪贴板中的数据,可以是字符,位图,或者其它格式数据。实现文本编辑器的编辑和剪贴板功能的具体步骤如下:
    (1) 新建项目。放RichTextBox控件到窗体。属性Name=richTextBox1,Dock=Fill,Text=””。
    (2) 放Mainmenu控件到窗体中。增加顶级菜单项:编辑,为其弹出菜单增加菜单项:剪切、复制、粘贴、撤销和恢复,属性Name分别为:mainMenuEdit、menuItemEditCut、menuItemEditCopy、menuItemEditPaste、menuItemEditUndo、menuItemEditRedo。为各个菜单项增加事件处理函数如下:
    private void menuItemEditCut_Click(object sender, System.EventArgs e)
    { richTextBox1.Cut();} //剪切
    private void menuItemEditCopy_Click(object sender, System.EventArgs e)
    { richTextBox1.Copy();} //拷贝
    private void menuItemEditPaste_Click(object sender, System.EventArgs e)
    { richTextBox1.Paste();} //粘贴
    private void menuItemEditUndo_Click(object sender, System.EventArgs e)
    { richTextBox1.Undo();} //撤销
    private void menuItemEditRedo_Click(object sender, System.EventArgs e)
    { richTextBox1.Redo();} //恢复
    (3) 编译,运行,输入一些字符后,选中一些字符,试验一下剪切、复制、粘贴等功能,并查看一下在剪贴板中字符是否能粘贴到其它字处理软件中,例如写字板。查看一下撤销和恢复功能是否可用。
    4.3 实现文本编辑器的存取文件功能
    文本编辑器都具有文件存取功能,顶级菜单项文件的弹出菜单中一般包括如下菜单项:新建、打开、关闭、保存和另存为等。本节实现以上菜单项。
    4.3.1 OpenFileDialog和SaveFileDialog控件
    OpenFileDialog对话框用来选择要打开的文件路径及文件名,SaveFileDialog对话框用来选择要存储文件的路径及文件名。两个对话框的外观如下图,它们的属性和方法基本相同,这里在一起介绍。

    图4.3.1A 打开文件对话框

    图4.3.1B 文件另存为对话框
     属性Filter:字符串类型,选择在对话框中显示的文件类型。属性Filter有多项,中间用|分开,每两项是一组,每组的第一项将出现在对话框保存类型(T)下拉列表编辑框的下拉列表中(见图4.3.1A),供用户选择,第二项表示如第一项被选中,对话框实际列出的文件。例如Filter="纯文本文件(*.txt)|*.txt|所有文件(*.*)|*.*",表示打开对话框,对话框的文件类型(T)下拉列表编辑框的下拉列表有两项:纯文本文件(*.txt)和所有文件(*.*),供用户选择。如果从文件类型下拉列表编辑框的下拉列表中选中"纯文本文件(*.txt)",表示打开对话框,只列出所有扩展名为.txt的文件,如果选中"所有文件(*.*)",表示打开对话框,将列出所有文件。
     属性FilterIndex:表示打开对话框后,对话框的文件类型(T)下拉列表编辑框的下拉列表中首先被选中的项的索引号。可以在设计阶段在属性窗口修改属性FilterIndex和Filter,也可在程序中用下列语句修改:openFileDialog1.Filter="纯文本文件(*.txt)|*.txt|所有文件(*.*)|*.*",openFileDialog1.FilterIndex=1。
     属性FileName:用户选取的文件的路径和文件名。
     属性InitialDirectory:打开对话框首先显示该属性指定的文件夹中的文件。
     属性DefaultExt:如果用户未指定扩展名,自动增加属性指定的文件扩展名。
     方法ShowDialog():打开对话框,根据方法的返回值确定用户单击了那个按钮,如返回DialogResult.Cancle,用户单击了忽略按钮,如返回DialogResult.OK,用户单击了打开或保存按钮。
    4.3.2 存取文件功能的实现
    (4) 把OpenFileDialog和SaveFileDialog控件放到窗体中。属性Name分别是openFileDialog1和saveFileDialog1。
    (5) 增加顶级菜单项:文件,为其弹出菜单增加菜单项:新建、打开...、保存...、另存为...、退出。修改Name属性分别为:mainMenuFile、menuItemFileNew、menuItemFileOpen、menuItemFileSave、menuItemFileSaveAs、menuItemFileExit。
    (6) 为Form1类增加string类型变量记录当前编辑的文件名:string s_FileName="",如果为空,表示还未记录文件名,即编辑的文件还没有名字。当单击菜单项保存,保存文件时,必须请用户输入文件名。
    (7) 为新建菜单项增加事件处理函数如下:
    private void menuItemFileNew_Click(object sender, System.EventArgs e)
    { richTextBox1.Text="";//或richTextBox1.Clear();
    s_FileName="";//新建文件没有文件名。
    }
    (8) 为打开文件菜单项增加事件处理函数如下:
    private void menuItemFileOpen_Click(object sender, System.EventArgs e)
    { if(openFileDialog1.ShowDialog()==DialogResult.OK)
    { s_FileName=openFileDialog1.FileName;
    richTextBox1.LoadFile(openFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }
    }
    (9) 为另存为菜单项增加事件处理函数如下:
    private void menuItemFileSaveAs_Click(object sender, System.EventArgs e)
    { if(saveFileDialog1.ShowDialog()==DialogResult.OK)
    { s_FileName=saveFileDialog1.FileName;
    richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }//注意存取文件类型应一致。
    }
    (10) 为保存文件菜单项增加事件处理处理函数如下:
    private void menuItemSaveFile_Click(object sender, System.EventArgs e)
    { if(s_FileName.Length!=0)
    richTextBox1.SaveFile(s_FileName,RichTextBoxStreamType.PlainText);
    else
    menuItemFileSaveAs_Click(sender,e);//调用另存为菜单项事件处理函数
    }
    (11) 把SaveFileDialog控件放到窗体中,将自动创建控件对象,其生命周期等于窗体生命周期,将长期占用存储空间。实际上SaveFileDialog控件对象只在存文件菜单项事件处理函数中有用,其它时间无用。为了节约存储空间,可以在存文件菜单项事件处理函数中建立SaveFileDialog控件对象,退出该事件处理函数时,自动释放该对象。修改另存为菜单项事件处理函数如下(首先删除增加的控件SaveFileDialog):
    private void menuItemFileSaveAs_Click(object sender, System.EventArgs e)
    { SaveFileDialog saveFileDialog1=new SaveFileDialog();
    saveFileDialog1.Filter="纯文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
    saveFileDialog1.FilterIndex=1;
    if(saveFileDialog1.ShowDialog()==DialogResult.OK)
    { s_FileName=saveFileDialog1.FileName;
    richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }//也可以用此方法修改打开文件菜单项事件处理函数。
    }
    (12) 为退出菜单项增加事件处理函数如下:
    private void menuItemExit_Click(object sender, System.EventArgs e)
    { Close();}
    (13) 编译,运行,可以存取文件。
    4.4 修改字体属性
    为修改字体属性,首先打开字体对话框FontDialog,选择指定字体。可以按两种方式修改字体,如果未选中字符,表示以后键入的字符将按选定字体输入。如果选中字符,则仅修改选定字符的字体。修改字符颜色也根据同样原则。
    4.4.1 FontDialog控件属性和方法
    用户可以用FontDialog对话框选定指定字体,FontDialog控件和OpenDialog控件的属性和方法基本相同,这里只介绍不同部分。属性Font:用户用FontDialog对话框选定的字体。FontDialog对话框显示效果如图4.3.1。
    4.4.2 修改字体属性的实现方法
    (14) 放FontDialog控件到窗体,属性Name=fontDialog1。增加顶级菜单项:格式,为格式顶级菜单项的弹出菜单增加菜单项:字体,属性Name分别为mainMenuModel和menuItemModelFont,为字体菜单项增加事件处理函数如下:
    private void menuItemModelFont_Click(object sender, System.EventArgs e)
    { if(fontDialog1.ShowDialog()==DialogResult.OK)
    richTextBox1.SelectionFont=fontDialog1.Font;
    }
    (15) 编译,运行,在选中字符和不选中字符两种情况下,用字体菜单项修改字体,看是否能实现写字板中同样的功能。

    图4.3.1字体对话框
    4.5 实现About对话框
    前边介绍的SaveDialog、OpenDialog和FontDialog都是类库中预先定义的对话框,本节介绍如何创建满足一定要求的自制对话框。对话框其实就是窗体,其基类和主窗体一样,是System.Windows.Forms.Form。只是一般对话框只有关闭按钮,没有最大化和最小化按钮,对话框的边界是固定的,不能改变。设计自己的对话框是经常遇到的工作。
    (16) 选择菜单项项目/添加Windows窗体,弹出对话框(见图4.5),在模板(T)编辑框中选择Windows窗体,在名称栏(N)编辑框中输入窗体文件名称:formAbout.cs,单击打开按钮,可以见到一个新窗体。从文件formAbout.cs可以看到新建窗体类名也为formAbout。
    (17) 修改formAbout属性StartPosition=CenterParent,表示打开对话框时,对话框在父窗口的中间。修改属性MaximizeBox=False,MinimizeBox=False,表示没有最大化和最小化按钮,既不能最大化和最小化。属性FormBorderStyle=FixedDialog,窗口不能修改大小。属性Text="关于记事本"。可以在窗体中增加各种控件,例如,小图标,Label控件等。本例仅增加Label控件表示版权信息,其属性Text="版权所有"。一个按钮,属性Text="确定",按钮单击事件处理函数如下:
    private void button1_Click(object sender,System.EventArgs e)
    {Close();}
    (18) 为Form1窗体增加顶级菜单项:帮助,为帮助顶级菜单项弹出菜单增加菜单项:关于…,属性Name为menuItemAbout。关于…菜单项单击事件处理函数如下:
    private void menuItemAbout_Click(object sender, System.EventArgs e)
    { formAbout AboutDialog=new formAbout();
    AboutDialog.ShowDialog(this);
    }//注意不能使用Show()函数
    (19) 编译,运行,单击关于…菜单项,将出现一个formAbout对话框(如右图),并且不关闭此对话框,不能回到主窗口,一般把这样的对话框叫做模式对话框。


    图4.5
    4.6 实现文本编辑器查找替换功能
    本节首先介绍模式对话框和非模式对话框的概念。并用非模式对话框实现文本编辑器程序的查找和替换功能。
    4.6.1 模式对话框和非模式对话框
    模式对话框和非模式对话框的区别是:打开模式对话框后,只有关闭该模式对话框,才能转到其他窗口,例如前边讲到的SaveDialog和OpenDialog都是典型的模式对话框。而打开非模式对话框后,不必退出该模式对话框,就可以转到其他窗口,例如查找和替换对话框都是典型的非模式对话框。两类对话框本质上都是窗体,是System.Windows.Forms.Form类的派生类,只是打开时使用的方法不一样,打开模式对话框,使用方法ShowDialog(),而打开非模式对话框,使用方法Show()。文本编辑器程序中,查找和替换对话框一般是非模式对话框。
    4.6.2 写字板查找替换功能的实现
    (20) 建立查找替换对话框。对话框其实就是窗体,其基类是System.Windows.Forms.Form。选择菜单项项目/添加Windows窗体,弹出对话框(如图4.5),选择Windows窗体,在名称栏输入窗体文件名称:formFindReplace.cs,单击打开按钮,可以见到一个新窗体。其属性Name=formFindReplace。
    (21) 修改formFindReplace窗体属性StartPosition=CenterParent,表示打开对话框时,对话框在父窗口的中间。修改属性MaximizeBox=False,MinimizeBox=False,表示没有最大化和最小化按钮,既不能最大化和最小化。FormBorderStyle=FixedDialog,窗口不能修改大小。属性Text="查找和替换"。在窗体中增加两个Label控件,属性Text分别为"查找字符串"和"替换字符串"。两个TextBox控件,属性Text=""。两个按钮,属性Text分别为"查找下一个"和"替换查到字符"。修改属性TopMost=true,使该窗口打开时总在其它窗体的前边。对话框界面如右图。
    (22) 为formFindReplace窗体增加变量:Form1 MainForm1;
    (23) 修改formFindReplace类构造函数如下(阴影部分是所做的修改):
    public formAbout(Form1 form1)//增加参数
    {
    //Windows窗体设计器支持所必需的
    InitializeComponent();
    //TODO:在InitializeComponent调用后添加任何构造函数代码
    MainForm1=form1;//新增语句,这里Form1是主窗体的属性Name的值
    }//有了Form1,可以在formFindReplace窗体中调用主窗体的公有方法
    (24) 为主窗体Form1增加方法如下,该方法将被formFindReplace窗体类调用。
    public void FindRichTextBoxString(string FindString)
    {} //以后步骤将在此方法中增加查找语句
    (25) formFindReplace窗体中查找下一个按钮单击事件处理函数如下:
    private void buttonFind_Click(object sender, System.EventArgs e)
    { if(textBox1.Text.Length!=0)//如果查找字符串不为空,调用主窗体查找方法
    MainForm1.FindRichTextBoxString(textBox1.Text);//上步增加的方法
    else
    MessageBox.Show("查找字符串不能为空","提示",MessageBoxButtons.OK);
    }//MessageBox时对话框,使用方法见4.7.1节
    (26) 为主窗体Form1增加方法如下,该方法将被formFindReplace窗体类调用。
    public void ReplaceRichTextBoxString(string ReplaceString)
    {} //以后步骤将在此方法中增加替换语句
    (27) 为替换查到字符按钮单击事件增加事件处理函数如下:
    private void buttonReplace_Click(object sender, System.EventArgs e)
    { if(textBox2.Text.Length!=0)//如果查找字符串不为空,调用主窗体替换方法
    MainForm1.ReplaceRichTextBoxString(textBox1.Text,textBox2.Text);
    else//方法MainForm1.ReplaceRichTextBoxString见(26)中定义
    MessageBox.Show("替换字符串不能为空","提示", MessageBoxButtons.OK);
    }
    (28) 为Form1窗体增加变量:int FindPostion=0,记录查找位置。
    (29) 为Form1窗体顶级菜单项编辑的弹出菜单增加菜单项:查找和替换。为查找和替换菜单项单击事件增加事件处理函数如下:
    private void menuItemFindReplace_Click(object sender, System.EventArgs e)
    { FindPostion=0;
    formAbout FindReplaceDialog=new formAbout(this);//注意this
    FindReplaceDialog.Show();//打开非模式对话框使用Show()方法
    }
    (30) 为在前边定义的Form1主窗体的FindRichTextBoxString方法增加语句如下:
    public void FindRichTextBoxString(string FindString)
    { if(FindPostion>=richTextBox1.Text.Length)//已查到文本底部
    { MessageBox.Show("已到文本底部,再次查找将从文本开始处查找",
    "提示",MessageBoxButtons.OK);
    FindPostion=0;
    return;
    }//下边语句进行查找,返回找到的位置,返回-1,表示未找到,参数1是要找的字符串
    //参数2是查找的开始位置,参数3是查找的一些选项,如大小写是否匹配,查找方向等
    FindPostion=richTextBox1.Find(FindString,
    FindPostion,RichTextBoxFinds.MatchCase);
    if(FindPostion==-1)//如果未找到
    { MessageBox.Show("已到文本底部,再次查找将从文本开始处查找",
    "提示", MessageBoxButtons.OK);
    FindPostion=0;//下次查找的开始位置
    }
    else//已找到
    { richTextBox1.Focus();//主窗体成为注视窗口
    FindPostion+=FindString.Length;
    }//下次查找的开始位置在此次找到字符串之后
    }
    (31) 为在前边定义的Form1主窗体的ReplaceRichTextBoxString方法增加语句如下:
    public void ReplaceRichTextBoxString(string ReplaceString)
    { if(richTextBox1.SelectedText.Length!=0)//如果选取了字符串
    richTextBox1.SelectedText=ReplaceString;//替换被选的字符串
    }
    (32) 编译,运行,输入若干字符,选中菜单项:编辑/查找和替换,打开对话框,注意该对话框可以在不关闭的情况下,转到主窗体,并且总是在其它窗体的前边,因此它是一个典型的非模式对话框。在对话框中输入查找和替换的字符,单击标题为查找下一个的按钮,可以找到所选字符,并被选中,单击标题为替换所选字符按钮,可以看到查找到的字符被替换。运行效果如右图:
    4.7 提示用户保存修改的文件
    用户在新建文本,打开其他文本或者退出文本编辑器时,如果编辑内容发生了改变,应提示用户是否保存已修改的文本内容。因此就需要在用户关闭当前文件前,弹出提示对话框,提醒用户是否保存当前文件。本节实现此功能。
    4.7.1 对话框MessageBox
    使用MessageBox可以打开一个对话框,用法如下:
    MessageBox.Show(this,"要保存当前更改吗?","保存更改吗?",
    MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question);
    第一个参数是父窗口,第二个参数是提示信息,第三个参数是标题栏的内容,第四个参数是有那些按钮,此例有YES,NO,CANCEL按钮,还可以使用AbortRetryIgnore(中止、重试和忽略按钮)、OK(确定按钮)、OKCancel(确定和取消按钮)、RetryCance(重试和忽略按钮)、YesNo(是和否按钮)等选项。第五个参数是使用那一个图标,此例是一个问号图标,还可以是Asterisk、Error、Exclamation、Hand、Stop、Warning等图标,如为None则无图标。返回值是System.Windows.Forms.DialogResult变量,代表用户按了那一个按钮。如果返回值是System.Windows.Forms.DialogResult.Yes,则表示按了YES键,表示要存修改的文件。如果返回值是System.Windows.Forms.DialogResult.Cancel,按Cancel键,表示忽略此次操作。如果返回值是System.Windows.Forms.DialogResult.No,则表示按了No键,表示不存修改的文件。以上设计的对话框MessageBox如下图:

    4.7.2 提示用户保存修改的文件的实现
    (33) 为Form1类增加一个bool变量bSave=false作为标记,用来跟踪RichTextBox中文本内容改变的情况。在程序开始运行、建立和打开一个新文件时,bSave=false,表示不必保存当前文本。RichTextBox控件有一个TextChanged事件,当文本发生改变的时候,这个事件就会被激活,在该事件处理函数中,使bSave=true。
    (34) 首先增加一个函数,其功能是判断是否需要将已修改的文件存盘,之所以要增加这个函数是因为有三处要用到此函数。该函数返回true,表示继续操作,该函数返回false,表示忽略此次操作,该函数定义如下:
    public bool IfSaveOldFile()
    { bool ReturnValue=true;
    if(bSave)
    { System.Windows.Forms.DialogResult dr;
    dr=MessageBox.Show(this,"要保存当前更改吗?","保存更改吗?",
    MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question);
    switch(dr)
    { case System.Windows.Forms.DialogResult.Yes://单击了yes按钮,保存修改
    bSave=false;
    if(s_FileName.Length!=0)
    richTextBox1.SaveFile(s_FileName,RichTextBoxStreamType.PlainText);
    else
    { SaveFileDialog saveFileDialog1=new SaveFileDialog();
    saveFileDialog1.Filter="纯文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
    saveFileDialog1.FilterIndex=1;
    if(saveFileDialog1.ShowDialog()==DialogResult.OK)
    { s_FileName=saveFileDialog1.FileName;
    richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }
    }
    ReturnValue=true;
    break;
    case System.Windows.Forms.DialogResult.No://单击了no按钮,不保存
    bSave=false;
    ReturnValue=true;
    break;
    case System.Windows.Forms.DialogResult.Cancel://单击了Cancel按钮
    ReturnValue=false;
    break;
    }
    }
    return ReturnValue;
    }
    (35) 在新建和打开菜单项的事件处理函数的头部增加如下语句:
    if(!IfSaveOldFile())//如果忽略,退出。
    return;
    (36) 修改存文件菜单项单击事件处理函数如下:
    private void menuItemSaveFile_Click(object sender, System.EventArgs e)
    { if(s_FileName.Length!=0)
    { bSave=false;//阴影为增加的语句
    richTextBox1.SaveFile(s_FileName,RichTextBoxStreamType.PlainText);
    }
    else
    menuItemSaveAs_Click(sender,e);
    }
    (37) 修改另存为菜单项单击事件处理函数如下:
    private void menuItemSaveAs_Click(object sender, System.EventArgs e)
    { SaveFileDialog saveFileDialog1=new SaveFileDialog();
    saveFileDialog1.Filter="纯文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
    saveFileDialog1.FilterIndex=1;
    if(saveFileDialog1.ShowDialog()==DialogResult.OK)
    { s_FileName=saveFileDialog1.FileName;
    richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    bSave=false;//阴影为增加的语句
    }
    }
    (38) 为RichTextBox控件TextChanged事件增加事件处理函数如下:
    private void richTextBox1_TextChanged(object sender, System.EventArgs e)
    { bSave=true;}
    (39) 为Form1窗体Closing事件是在关闭窗口之前发送的事件,此时,窗体中的控件还存在,还可以保存修改的内容,也可以不退出。增加它的事件处理函数如下:
    private void Form1_Closing(object sender,System.ComponentModel.CancelEventArgs e)
    { if(!IfSaveOldFile())
    e.Cancel=true;//不退出
    }
    (40) 编译,运行,键入若干字符,选中菜单项新建或打开,或退出,将看到提示信息,问是否保存修改的文件。有三种选择:存文件,不存文件,忽略此次操作,试验单击不同按钮的效果。
    4.8 打印和打印预览
    打印和打印预览是一个编辑器必须具有的功能,本节介绍实现打印和打印预览的方法。一般要实现如下菜单项:打印、打印预览、页面设置。
    4.8.1 PrintDocument类
    PrintDocument组件是用于完成打印的类,其常用属性、方法和事件如下:
     属性DocumentName:字符串类型,记录打印文档时显示的文档名(例如,在打印状态对话框或打印机队列中显示)。
     方法Print:开始文档的打印。
     事件BeginPrint:在调用Print方法后,在打印文档的第一页之前发生。
     事件PrintPage:需要打印新的一页时发生。
     事件EndPrint:在文档的最后一页打印后发生。
    若要打印,首先创建PrintDocument组件的对象。然后使用页面设置对话框PageSetupDialog设置页面打印方式,这些设置作为要打印的所有页的默认设置。使用打印对话框PrintDialog设置对文档进行打印的打印机的参数。在打开两个对话框前,首先设置对话框的属性Document为指定的PrintDocument类对象,修改的设置将保存到PrintDocument组件对象中。第三步是调用PrintDocument.Print方法来实际打印文档。当调用该方法后,引发下列事件:BeginPrint、PrintPage、EndPrint。其中每打印一页都引发PrintPage事件,打印多页,要多次引发PrintPage事件。完成一次打印,可以引发一个或多个PrintPage事件。
    程序员应为这3个事件编写事件处理函数。BeginPrint事件处理函数进行打印初始化,一般设置在打印时所有页的相同属性或共用的资源,例如所有页共同使用的字体、建立要打印的文件流等。PrintPage事件处理函数负责打印一页数据。EndPrint事件处理函数进行打印善后工作。这些处理函数的第2个参数System.Drawing.Printing.PrintEventArgs e提供了一些附加信息,主要有:
     e.Cancel:布尔变量,设置为true,将取消这次打印作业。
     e.Graphics:所使用的打印机的设备环境,参见第五章。
     e.HasMorePages:布尔变量。PrintPage事件处理函数打印一页后,仍有数据未打印,退出事件处理函数前设置HasMorePages=true,退出PrintPage事件处理函数后,将再次引发PrintPage事件,打印下一页。
     e.MarginBounds:打印区域的大小,是Rectangle结构,元素包括左上角坐标:Left和Top,宽和高:Width和Height。单位为1/100英寸。
     e.MarginBounds:打印纸的大小,是Rectangle结构。单位为1/100英寸。
     e.PageSettings:PageSettings类对象,包含用对话框PageSetupDialog设置的页面打印方式的全部信息。可用帮助查看PageSettings类的属性。
    下边为这3个事件编写事件处理函数,具体步骤如下:
    (41) 在最后一个using语句之后增加语句:
    using System.IO;
    using System.Drawing.Printing;
    (42) 本例打印或预览RichTextBox中的内容,增加变量:StringReader streamToPrint=null。如果打印或预览文件,改为:StreamReader streamToPrint,流的概念参见第六章。增加打印使用的字体的变量:Font printFont。
    (43) 放PrintDocument控件到窗体,属性name为printDocument1。
    (44) 为printDocument1增加BeginPrint事件处理函数如下:
    private void printDocument1_BeginPrint(object sender,
    System.Drawing.Printing.PrintEventArgs e)
    { printFont=richTextBox1.Font;//打印使用的字体
    streamToPrint=new StringReader(richTextBox1.Text);//打印richTextBox1.Text
    }//如预览文件改为:streamToPrint=new StreamReader("文件的路径及文件名");
    (45) printDocument1的PrintPage事件处理函数如下。streamToPrint.ReadLine()读入一段数据,可能打印多行。本事件处理函数将此段数据打印在一行上,因此方法必须改进。
    private void printDocument1_PrintPage(object sender,
    System.Drawing.Printing.PrintPageEventArgs e)
    { float linesPerPage=0;//记录每页最大行数
    float yPos=0;//记录将要打印的一行数据在垂直方向的位置
    int count=0;//记录每页已打印行数
    float leftMargin=e.MarginBounds.Left;//左边距
    float topMargin=e.MarginBounds.Top;//顶边距
    string line=null;//从RichTextBox中读取一段字符将存到line中
    //每页最大行数=一页纸打印区域的高度/一行字符的高度
    linesPerPage=e.MarginBounds.Height/printFont.GetHeight(e.Graphics);
    //如果当前页已打印行数小于每页最大行数而且读出数据不为null,继续打印
    while(count<linesPerPage&&((line=streamToPrint.ReadLine())!=null))
    { //yPos为要打印的当前行在垂直方向上的位置
    yPos=topMargin+(count*printFont.GetHeight(e.Graphics));
    e.Graphics.DrawString(line,printFont,Brushes.Black,
    leftMargin,yPos,new StringFormat());//打印,参见第五章
    count++;//已打印行数加1
    }
    if(line!=null)//是否需要打印下一页
    e.HasMorePages=true;//需要打印下一页
    else
    e.HasMorePages=false;//不需要打印下一页
    }
    (46) 为printDocument1增加EndPrint事件处理函数如下:
    private void printDocument1_EndPrint (object sender,
    System.Drawing.Printing.PrintEventArgs e)
    { if(streamToPrint!=null)
    streamToPrint.Close();//释放不用的资源
    }
    4.8.2 打印设置对话框控件PageSetupDialog
    Windows窗体的PageSetupDialog控件是一个页面设置对话框,用于在Windows应用程序中设置打印页面的详细信息,对话框的外观如图4.8.2。

    图4.8.2
    用户使用此对话框能够设置纸张大小(类型)、纸张来源、纵向与横向打印、上下左右的页边距等。在打开对话框前,首先设置其属性Document为指定的PrintDocument类对象,用来把页面设置保存到PrintDocument类对象中。为文本编辑器增加页面设置功能的具体步骤如下:
    (47) 为文件顶级菜单项的弹出菜单增加菜单项:页面设置。
    (48) 放PageSetupDialog控件到窗体,属性name为pageSetupDialog1。
    (49) 为页面设置菜单项增加单击事件处理函数如下:
    private void menuItem5_Click(object sender,System.EventArgs e)
    { pageSetupDialog1.Document=printDocument1;
    pageSetupDialog1.ShowDialog();
    }
    (50) 打开对话框pageSetupDialog1后,如果单击了确定按钮,PageSetupDialog对话框中所做的的页面设置被保存到PrintDocument类对象printDocument1中,如果单击了取消按钮,不保存这些修改,维持原来的值。当调用PrintDocument.Print方法来实际打印文档时,引发PrintPage事件,该事件处理函数的第二个参数e提供了这些设置信息。
    4.8.3 打印预览
    用PrintPreviewDialog类可以在屏幕上显示PrintDocument的打印效果,既打印预览。实现打印预览的具体步骤如下:
    (51) 为文件顶级菜单项的弹出菜单增加菜单项:打印预览。
    (52) 放PrintPreviewDialog控件到窗体,属性name为printPreviewDialog1。
    (53) 为打印预览菜单项增加单击事件处理函数如下:
    private void menuItemPrintView_Click(object sender,System.EventArgs e)
    { printPreviewDialog1.Document=printDocument1;
    printPreviewDialog1.ShowDialog();
    }
    (54) 编译,运行,输入若干字符,试验一下预览的效果,预览的效果如图4.8.3。

    图4.8.3
    4.8.4 用打印对话框PrintDialog实现打印
    PrintDialog组件是类库中预先定义的对话框,用来设置对文档进行打印的打印机的参数,包括打印机名称、要打印的页(全部打印或指定页的范围)、打印的份数以及是否打印到文件等。在打开对话框前,首先设置其属性Document为指定的PrintDocument类对象,打开PrintDialog对话框后,修改的设置将保存到PrintDocument类的对象中。PrintDialog对话框的外观如图4.8.4。

    图4.8.4
    增加打印功能的具体步骤如下:
    (55) 放PrintDialog控件到窗体属性Name=printDialog1。
    (56) 为文件顶级菜单项的弹出菜单增加菜单项:打印。
    (57) 为打印菜单项增加单击事件处理函数如下:(不能打印?)
    private void menuItemPrint_Click(object sender, System.EventArgs e)
    { printDialog1.Document=printDocument1;
    if(printDialog1.ShowDialog(this)==DialogResult.OK)
    printDocument1.Print();
    }
    (58) 编译,运行,输入若干字符,试验一下打印效果。
    4.9 编写多文档界面应用程序
    本节首先介绍如何建立类似Microsoft Word的文本编辑器,然后介绍如何建立类似Visualstudio.Net的编辑器那样的文本编辑器,有多个选项卡页。
    4.9.1 建立类似Microsoft Word的编辑器
    建立一个类似Microsoft Word的编辑器,可以有多页,每页处理一个文档。多文档界面(MDI)应用程序具有一个主窗体(父窗体),主窗体在其工作区内包含一组窗体(子窗体)。每个子窗体都是一个限制为只能在该父窗体内出现的窗体。这些子窗体通常共享父窗体界面的菜单栏、工具栏以及其他部分。创建多文当编辑器的具体步骤如下:
    (1) 新建项目。修改主窗体属性IsMdiContainer=true,表示主窗体是一个子窗体容器。
    (2) 放主菜单控件Mainmenu到主窗体。增加顶级菜单项:文件,属性Name=menuItemFile。为文件菜单增加菜单项:新建、打开、另存为、关闭当前窗口、退出,属性Name分别为menuItemNew、menuItemOpen、menuItemSaveAs、menuItemCloseChild、menuItemExit。增加顶级菜单项:窗口,属性Name=menuItemWindow,属性MdiList=true,该属性将在窗口菜单下增加子窗口列表。为窗口菜单增加菜单项:水平平铺、层叠、垂直平铺,属性Name分别为menuItemTileH、menuItemCascade、menuItemTileV。
    (3) 创建子窗体,选择菜单项:项目/添加Windows窗体,弹出对话框(见图4.5),选择Windows窗体,在名称栏输入窗体文件名称:FormChild.cs,单击打开按钮,可以见到一个新窗体。定义新窗体的类名也为FormChild。此窗体作为主窗体的子窗体。
    (4) 放RichTextBox1控件到子窗体。修改属性Dock=Fill,Text="",Modifiers=public,使RichTextBox1为公有成员,在主窗体可以访问RichTextBox1。
    (5) 为主窗体菜单项新文件增加单击事件处理函数如下:
    private void menuItemNew_Click(object sender,System.EventArgs e)
    { FormChild formChild=new FormChild();
    formChild.MdiParent=this;
    formChild.Show();
    }
    (6) 把OpenFileDialog控件放到窗体中。单击打开文件菜单项事件处理函数如下:
    private void menuItemOpen_Click(object sender, System.EventArgs e)
    { if(openFileDialog1.ShowDialog(this)==DialogResult.OK)
    { FormChild ChildForm=new FormChild();
    ChildForm.MdiParent=this;
    ChildForm.richTextBox1.LoadFile(openFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    ChildForm.Show();
    }
    }
    (7) 把SaveFileDialog控件放到子窗体中。另存为菜单项事件处理函数如下:
    private void menuItemChildSaveAs_Click(object sender, System.EventArgs e)
    { if(saveFileDialog1.ShowDialog(this)==DialogResult.OK)
    { FormChild ChildForm=(FormChild)this.ActiveMdiChild;
    ChildForm.richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }
    }
    (8) 为主窗体菜单项关闭当前窗口增加单击事件函数如下:
    private void menuItemCloseChild_Click(object sender, System.EventArgs e)
    { this.ActiveMdiChild.Close();}
    (9) 为主窗体菜单项退出增加单击事件函数如下:
    private void menuItemExit_Click(object sender, System.EventArgs e)
    { Close();}
    (10) 为主窗体菜单项水平平铺增加单击事件函数如下:
    private void menuItemTileH_Click(object sender, System.EventArgs e)
    { this.LayoutMdi(MdiLayout.TileHorizontal);}
    (11) 为主窗体菜单项层叠增加单击事件函数如下:
    private void menuItemCascade_Click_1(object sender, System.EventArgs e)
    { this.LayoutMdi(MdiLayout.Cascade);}
    (12) 为主窗体菜单项垂直平铺增加单击事件函数如下:
    private void menuItemTileV_Click(object sender, System.EventArgs e)
    { this.LayoutMdi(MdiLayout. TileVertical);}
    (13) 运行,运行效果如下,子窗体为层叠排列。

    4.9.2 主窗口和子窗口的菜单的融合
    在许多多文档编辑器应用程序中,在没有子窗体打开时,菜单比较简单,而有子窗体打开后,菜单增多。实现这种功能一般是在主窗体中创建一个简单菜单,子窗体没打开时,只显示这个简单菜单。在子窗体中也创建一个菜单,包含主窗体菜单中没有的菜单项。打开子窗体后,子窗体的菜单和主窗体菜单合并成为一个菜单,这个功能叫做主窗口和子窗口的菜单的融合。创建具有这种功能的多文档编辑器应用程序可以按下列步骤:
    (1) 新建项目。修改主窗口属性IsMdiContainer为true。
    (2) 把Mainmenu控件放到主窗体中。增加顶级菜单项:文件。其属性MergeType=MergeItems,表示打开子窗体后,主窗体和子窗体中属性MergeOrder相同的顶级菜单项的弹出菜单中的菜单项合并为一个弹出菜单。属性MergeOrder=0。子窗体的顶级菜单项文件的属性MergeType也应为MergeItems,MergeOrder属性也应为0,这样打开子窗口后,才能合并为一个弹出菜单。属性Name=menuItemFile。为文件菜单增加菜单项:新建、打开、退出,属性Name分别为menuItemNew、menuItemOpen、menuItemExit,属性MergeType都为Add,属性MergeOrder依次为1、2、6,目的是打开子窗口后,在新建和打开菜单项后加入子窗口菜单栏中的文件菜单的另存为菜单项。增加菜单:帮助,其属性MergeType=Add,属性MergeOrder=7,属性Name=menuItemHelp。为帮助菜单增加菜单项:关于…,属性Name=menuItemAbout。其余菜单在子窗口中实现。注意属性MergeOrder分别为0、7,打开子窗口后,子窗口中的菜单将按顺序插入到主窗口的菜单中,例如,子窗口有菜单:编辑,其属性MergeOrder=3,合并后,菜单排列顺序为:文件、编辑、帮助。
    (3) 创建子窗体,选择菜单项:项目/添加Windows窗体,弹出对话框,选择Windows窗体,在名称栏输入窗体文件名称:formChild.cs,单击打开按钮,可以见到一个新窗体。定义新窗体的类名也为formChild。
    (4) 为formChild窗体增加变量:Form1 MainForm1;
    (5) 修改formChild类构造函数如下(阴影部分是所做的修改):
    public formChild(Form1 form1)//增加参数
    {
    //Windows窗体设计器支持所必需的
    InitializeComponent();
    //TODO:在InitializeComponent调用后添加任何构造函数代码
    MainForm1=form1;//新增语句,这里Form1是主窗体的属性Name的值
    }//有了Form1,可以在formChild窗体中调用主窗体的公有方法
    (6) 把Mainmenu控件放到子窗体中。增加顶级菜单项:文件,其属性MergeType=MergeItems,属性MergeOrder=0。为文件顶级菜单项弹出菜单增加菜单项:另存为…,属性MergeType=Add,属性MergeOrder=3,菜单合并后,另存为…菜单项将出现在主窗口文件菜单的新建和打开菜单项之后。增加菜单项:关闭当前文件,属性MergeType=Add,属性MergeOrder=4。
    (7) 增加顶级菜单项:编辑,其属性MergeType=Add,属性MergeOrder=3。注意属性MergeOrder=3,菜单合并后,编辑菜单将出现出现菜单文件之后。为编辑顶级菜单项弹出菜单增加菜单项:拷贝、剪贴、粘贴。
    (8) 增加顶级菜单项:窗口,其属性MergeType=Add,属性MergeOrder=6,属性Name=menuItemWindow,属性MdiList=true,该属性将在窗口菜单下增加子窗口列表。为窗口菜单增加菜单项:层叠,属性Name为menuItemCascade。
    (9) 放RichTextBox1控件到子窗体。修改属性Dock=Fill,Text=””,属性 Modifiers=public,使RichTextBox1为公有成员,在主窗体可以访问RichTextBox1。
    (10) 为主窗体菜单项新文件增加单击事件函数如下:
    private void menuItemNew_Click(object sender, System.EventArgs e)
    { formChild ChildForm=new formChild(this);
    ChildForm.MdiParent=this;
    ChildForm.Show();
    }
    (11) 把OpenFileDialog控件放到主窗体中。单击打开文件菜单项事件处理函数如下:
    private void menuItemOpen_Click(object sender, System.EventArgs e)
    { if(openFileDialog1.ShowDialog(this)==DialogResult.OK)
    { formChild ChildForm=new formChild(this);
    ChildForm.MdiParent=this;
    ChildForm.richTextBox1.LoadFile(openFileDialog1.FileName);
    ChildForm.Show();
    }
    }
    (12) 为主窗体菜单项退出增加单击事件处理函数如下:
    private void menuItemExit_Click(object sender, System.EventArgs e)
    { Close();}
    (13) 为子窗体菜单项层叠增加单击事件处理函数如下:
    private void menuItemCascade_Click_1(object sender, System.EventArgs e)
    { MainForm1.LayoutMdi(MdiLayout.Cascade);}
    (14) 把SaveFileDialog控件放到子窗体中。为子窗体菜单项另存为增加单击事件函数如下:
    private void menuItemChildSaveAs_Click(object sender, System.EventArgs e)
    { if(saveFileDialog1.ShowDialog(this)==DialogResult.OK)
    { richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }
    }
    (15) 为子窗体菜单项关闭当前窗口增加单击事件函数如下:
    private void menuItemCloseChild_Click(object sender, System.EventArgs e)
    { Close();}
    (16) 为子窗体菜单项拷贝、剪贴和粘贴增加单击事件函数如下:
    语句分别为:richTextBox1.Cut();richTextBox1.Copy();richTextBox1.Paste();
    (17) 运行效果如下图。


    4.9.3 建立类似Visualstudio.Net的编辑器
    Visualstudio.Net的编辑器有多个选项卡页,可以编辑多个文件。建立选项卡页数固定,每选项卡页显示一行文本,类似Visualstudio.Net的编辑器的文本编辑器的具体实现步骤如下:
    (1) 新建项目。放TabControl控件到子窗体。修改属性Dock=Fill。
    (2) 单击TabControl属性TabPages后按钮,打开TabPage集合编辑器,单击添加按钮,增加1个选项卡页。修改属性Text分别为:第一页,第二页。如图4.9.3。
    (3) 选中第一页,可以在页中放置控件,例如放置Label控件,属性Text=”这是第一个选项卡页”。同样在第二页中也放置Label控件,属性Text=”这是第二个选项卡页”。如果放置RichTextBox控件,可以做成多文档编辑器。
    (4) 运行,可以看到多页,单击每页的标题,可以转换选项卡页。运行效果如右图:

    图4.9.3
    如可以有多个选项卡页,每选项卡页处理一个文档,并能动态增加新选项卡页,关闭当前选项卡页。实现步骤如下:
    (1) 新建项目。放TabControl控件到子窗体。修改属性Dock=Fill。
    (2) 把Mainmenu控件放到主窗体中。增加顶级菜单项:文件,为其弹出菜单增加4个菜单项:新页、关闭当前页、打开、另存为。属性Name分别为:menuItemFile、menuItemFileNew、menuItemFileClose、menuItemFileOpen、menuItemFileSaveAs。
    (3) 增加一个新方法MakeNewTbpage()如下:
    private object MakeNewTbpage()
    { //增加选项卡页TabPage
    TabPage tabPage1=new TabPage();
    tabControl1.Controls.Add(tabPage1);//将tabPage1放到tabControl1中
    tabPage1.Location=new Point(4, 21);
    tabPage1.Size=new Size(284, 248);
    tabPage1.Text="第"+tabPage1.TabIndex.ToString()+"页";
    //增加RichTextBox
    RichTextBox richTextBox1=new RichTextBox();
    richTextBox1.Dock=DockStyle.Fill;
    richTextBox1.Size=new Size(284, 248);
    richTextBox1.Text="";
    tabPage1.Controls.Add(richTextBox1);//将richTextBox1放到tabPage1中
    return (object)richTextBox1;
    }
    (4) 为菜单项新页增加事件处理函数如下:
    private void menuItemFileNew_Click(object sender, System.EventArgs e)
    { MakeNewTbpage();}
    (5) 为菜单项关闭当前页增加事件处理函数如下:
    private void menuItemFileClose_Click(object sender, System.EventArgs e)
    { TabPage tabPage1=tabControl1.SelectedTab;//得到当前选定的选项卡页
    tabControl1.Controls.Remove(tabPage1);//从tabControl1中移走该页
    //得到当前选定的选项卡页中第0个控件,即RichTextBox控件
    RichTextBox richTextBox1=(RichTextBox)tabPage1.Controls[0];
    if(richTextBox1!=null)
    richTextBox1.Dispose();//删除当前选定选项卡页中RichTextBox控件对象
    if(tabPage1!=null)
    tabPage1.Dispose();//删除当前选定的选项卡页
    }
    (6) 把OpenFileDialog控件放到子窗体中。为菜单项打开增加事件处理函数如下:
    private void menuItemFileOpen_Click(object sender, System.EventArgs e)
    { if(openFileDialog1.ShowDialog()==DialogResult.OK)
    { RichTextBox richTextBox1=(RichTextBox)MakeNewTbpage();
    richTextBox1.LoadFile(openFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }
    }
    (7) 把SaveFileDialog控件放到子窗体中。为菜单项另存为增加事件处理函数如下:
    private void menuItemFileSaveAs_Click(object sender, System.EventArgs e)
    { if(saveFileDialog1.ShowDialog()==DialogResult.OK)
    { TabPage tabPage1=tabControl1.SelectedTab;
    RichTextBox richTextBox1=(RichTextBox)tabPage1.Controls[0];
    richTextBox1.SaveFile(saveFileDialog1.FileName,
    RichTextBoxStreamType.PlainText);
    }
    }
    (8) 编译,运行,建立新文件,关闭当前选项卡页,打开新文件,存文件,开是否正常。
    习题
    (1) RichTextBox控件Lines属性记录控件中所有文本的字符串数组,每两个回车之间字符串是数组的一个元素。定义一个数组,将属性Lines 中的内容存到这个数组中。(提示:string[] s=new string [richTextBox1.Lines.Length];s= richTextBox1.Lines)
    (2) 为设计的单文档写字板增加工具栏,实现建新文件,打开文件,存文件等功能。
    (3) 在工具栏中,增加2个下拉列表文本框,一个选择使用的字体,一个选择字体的字号。
    (4) 在工具栏中,增加3个按钮,分别设定字符为黑体,斜体,增加下划线。
    (5) 在工具栏中,增加1个按钮,用ColorDialog对话框选择字体的颜色。
    (6) 如何实现全选菜单项。
    (7) RichTextBox控件的属性Modified可以指示用户是否修改文本框控件的内容。请修改4.7节程序,使用属性Modified判断用户是否修改了RichTextBox控件中文本内容。
    (8) 在查找对话框中,增加两个多选框,选择是否允许反向查和区分大小写,并实现反向查找和不区分大小写查找。
    (9) 在实现打开文件和另存为功能时,使用属性InitialDirectory和属性DefaultExt。
    (10) RichTextBox控件的属性SelectionAlignment表示段落的对齐方式,在工具栏中增加三个按钮,分别实现段落的左对齐(HorizontalAlignment.Left)、右对齐(HorizontalAlignment.Right)、中间对齐(HorizontalAlignment.Center)。(提示:3个按钮应是互斥的,用分隔符表示3个按钮是一组。工具条按钮属性Style设置为ToolBarButtonStyle.Separator,则按钮将显示为一个按钮分隔符,而不是按钮。)
    (11) RichTextBox控件的属性SelectionCharOffset可以设定选中的字符为上下标,可以为一个整数,为0表示正常字符,负数表示下标,正数表示上标。请在工具条中增加三个按钮,分别实现上下标功能。
    (12) 请实现完整的单文档编辑器,具有前边介绍的单文档编辑器的功能。
    (13) 请实现完整的多文档编辑器,具有前边介绍的单文档编辑器的功能。
    (14) 请实现完整的选项卡式多文档文本编辑器,具有前边介绍的单文档编辑器的功能。


    第五章 图形图像编程
    本章的目的是介绍图形图像编程的方法,希望在学了本章以后,能编制象Windows画图那样的程序。本章的重点是学习Graphics类中对象(象笔、刷子等)及各种方法的使用,以及Bitmap类的应用。
    5.1 图形设备环境接口(GDI)
    为了在Windows窗口输出数据(字符或图形),Windows操作系统提供了一些工具和函数,例如提供笔用来定义图形外轮廓线的颜色及粗细,提供刷子定义添充封闭图形内部的颜色和格式,提供不同输出字体,提供函数用来输出字符或绘制图形等等。所有这些工具和函数被放在图形设备接口函数库中(GDI32.DLL),它负责CRT显示及打印。根据设备不同,可以构造不同的设备环境(GDI),使输出图形或字符与设备无关,既无论是在CRT显示还是在打印机上打印同一个图形或字符,都用相同的函数。GDI所扮演的角色如下图所示:

    用户应用程序根据是在CRT显示还是在打印机打印,首先选择CRT显示设备环境或打印设备环境,然后调用GDI中的同名函数实现在CRT显示或在打印机上打印。而GDI设备环境根据选择的不同设备,调用不同的设备驱动程序,在CRT上显示或在打印机上打印。而这些驱动程序都是各个设备制造厂商提供的。这样做的最大好处是应用程序和设备无关,应用程序不必为不同的设备编制不同的程序。为使用不同的设备,无论是不同的显卡,还是不同的打印机,只要安装该设备的驱动程序,应用程序就可以使用该设备,微软的Word程序可以使用不同的打印机就是使用了这个原理。
    .NET系统的基础类库(.Net FrameWork)对Windows操作系统的图形设备接口函数库(GDI32.DLL)进行了扩充,并用类进行了封装,一般叫做GDI+。使用GDI+绘图更加方便快捷。为了使用GDI+图形功能,必须引入以下命名空间:System.Drawing,System.Drawing.Priniting,System.Drawing.Imaging,System.Drawing.Drawing2D,System.Drawing.Design,System.Drawing.Text。
    5.2 Graphics类
    System.Drawing.Graphics类对GDI+进行了封装,Graphics类提供一些方法完成各种图形的绘制。Graphics类对象与特定的设备关联,为了在不同的设备上用完全相同的代码完成同样的图形,应根据不同的设备建立不同的Graphics类对象。Graphics类是密封类,不能有派生类。
    5.2.1 使用Graphics类绘图的基本步骤
    GDI+大部分功能被封装在Graphics类中,Graphics类提供了一些工具和函数,例如提供笔用来定义图形外轮廓线的颜色及粗细,提供刷子定义添充封闭图形内部的颜色和格式,提供不同输出字体,提供函数用来输出字符或绘制图形等等。为了在窗体中或其它控件中使用这些工具和函数绘图,必须首先得到这些窗体或控件的使用的Graphics类对象。下面的例子,在窗体中增加了一个按钮,单击按钮将在窗体中画一个边界为红色,内部填充蓝色的圆。该程序段说明了使用Graphics类绘图的基本步骤。按钮的单击事件处理函数如下:
    private void button1_Click(object sender,System.EventArgs e)
    { Graphics g=this.CreateGraphics();//得到窗体使用的Graphics类对象
    Pen pen1=new Pen(Color.Red);//创建红色笔对象
    SolidBrush brush1=new SolidBrush(Color.Blue);//创建蓝色刷子对象
    g.DrawEllipse(pen1,10,10,100,100);//用红色笔画圆的边界
    g.FillEllipse(brush1,10,10,100,100);//用蓝色刷子填充圆的内部
    }
    运行后,单击按钮,出现边界为红色,内部填充为蓝色的圆。
    5.2.2 窗体的Paint事件
    运行上例,单击按钮,出现边界为红色,内部填充蓝色的圆。最小化后,再最大化,图形不见了。这是因为用户Form窗体用户区内容可能被破坏,例如窗体最小化后,再最大化,菜单被打开再关闭,打开对话框再关闭等,用户区内容被覆盖。Windows并不保存被破坏的用户区内容,而是由应用程序自己恢复被破坏的用户区的内容。当应用程序窗口用户区内容被破坏后需恢复时,Windows操作系统向应用程序发送Paint事件,应用程序应把在窗口用户区输出数据的语句放在Paint事件处理函数中,Windows发Paint事件时,能调用这些在窗口用户区输出数据的语句恢复被破坏的内容。Form窗体不能自动响应Paint事件,程序员必须生成Paint事件处理函数。修改上例,增加Form窗体的Paint事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;//得到窗体的使用的Graphics类对象
    Pen pen1=new Pen(Color.Red);
    SolidBrush brush1=new SolidBrush(Color.Blue);
    g.DrawEllipse(pen1,10,10,100,100);
    g.FillEllipse(brush1,10,10,100,100);
    }
    运行后,出现边界为红色,内部填充蓝色的圆。最小化后,再最大化,图形不消失。
    5.3 GDI+中三种坐标系统:
    GDI+定义了三种坐标系统,并提供了三种坐标转换的方法Graphics.TransformPoints()。
     全局坐标系统。
     页面(Page)坐标系统:左上角为原点,横向x轴向右为正方向,纵向y轴向下为正方向。单位为像素。这是默认的坐标系统。
     设备坐标系统:可以指定特定测量单位的页面(Page)坐标系统。如果单位为像素,和页面(Page)坐标系统相同。
    5.4 GDI+中常用的结构
    本节介绍GDI+中常用的结构,包括:Point、PointF、Size、SizeF、Rectangle、RectangleF、Color等。它们都在名字空间System.Drawing中定义的。
    5.4.1 结构Point和PointF
    点结构有两个成员:X,Y,表示点的x轴和y轴的坐标。其常用构造函数如下:
    Point p1=new Point(int X,int Y);//X,Y为整数
    PointF p2=new PointF(float X,floa Y);//X,Y为浮点数
    5.4.2 结构Size和SizeF
    Size和SizeF用来表示尺寸大小,有两个成员:Width和Height。常用构造函数如下:
    public Size(int width,int height);
    public SizeF(float width,float height);
    5.4.3 结构Rectangle和RectangleF
    结构Rectangle和RectangleF用来表示一个矩形,常用属性如下:
     Top:Rectangle结构左上角的y坐标。
     Left:Rectangle结构左上角的x坐标。
     Bottom:Rectangle结构右下角的y坐标。
     Right:Rectangle结构右下角的x坐标。
     Width:获取或设置此Rectangle结构的宽度。
     Height:获取或设置此Rectangle结构的高度。
     Size:获取或设置此Rectangle的大小。
     X:获取或设置此Rectangle结构左上角的x坐标。
     Y:获取或设置此Rectangle结构左上角的y坐标。
    其常用构造函数为:
    //参数为矩形左上角坐标的点结构location和代表矩形宽和高的Size结构size
    Rectangle(Point location,Size size);//参数也可为PointF和SizeF
    //参数为矩形左上角x和y坐标,宽,高
    Rectangle(int X,int Y,int width,int height);//X和Y也可为float
    5.4.4 结构Color
    Color结构表示颜色,结构中包含一个无符号32位数代表颜色。任何一种颜色可以用透明度(al),蓝色(bb),绿色(gg),红色(rr)合成,格式为0xalrrbbgg,其中al,bb,gg,rr为0到255间的二进制数。常用方法如下:
     public static Color FromArgb(int alpha,int rr,int gg,int bb);
    从四个分量(透明度、红色、绿色和蓝色)值创建Color结构。每个分量的值仅限于8位(小于256)。alpha值表示透明度,=0为完全透明,=255为完全不透明
     public static Color FromArgb(int rr,int gg,int bb);
    从指定的8位颜色值(红色、绿色和蓝色)创建Color结构。透明度值默认为255(完全不透明)。每个分量的值仅限于8位(小于256)。红色为(255,0,0),绿色为(0,255,0),蓝色为(0,0,255)。
     public static Color FromArgb(int alpha,Color color);
    从指定的Color结构创建新Color结构,使用新指定的透明度值alpha。alpha值仅限于8位。透明度及颜色的使用方法的例子如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;
    SolidBrush RedBrush=new SolidBrush(Color.FromArgb(128,255,0,0));//半透明
    SolidBrush GreenBrush=new SolidBrush(Color.FromArgb(128,0,255,0));
    SolidBrush BlueBrush=new SolidBrush(Color.FromArgb(128,0,0,255));
    g.FillRectangle(RedBrush,0,0,80,80);
    g.FillRectangle(GreenBrush,40,0,80,80);
    g.FillRectangle(BlueBrush,20,20,80,80);
    }
    效果如右图,可以将透明度alpha值设为255,再运行一次,看看有何不同。C#中还预定义了一些颜色常数,例如黑色为Color.Black,红色为Color.Red等等,可用帮助察看。
    5.5 画笔
    Pen类对象指定绘制的图形外轮廓线宽度和颜色。Pen类有4个构造函数,分别是:
     public Pen(Color color);//建立颜色为color的笔,宽度默认为1
     public Pen(Color color,float width);//建立颜色为color的笔,宽度为width
     public Pen(Brush brush);//使用刷子为笔
     public Pen(Brush,float width);//使用刷子为笔,宽度为width
    Pen类常用的属性:Color为笔的颜色,Width为笔的宽度,DashStyle为笔的样式,EndCap和StartCap为线段终点和起点的外观。下例显示各种笔的DashStyle、EndCap和StartCap不同选项的样式(见下图)。主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;
    Pen pen1=new Pen(Color.Red,6);//默认为实线笔
    g.DrawLine(pen1,10,10,100,10);//画实线,图中左边第1条线
    pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.Dash;//虚线笔
    g.DrawLine(pen1,10,20,100,20);//画虚线,图中左边第2条线
    pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.DashDot;//点,短线风格的线
    g.DrawLine(pen1,10,30,100,30);//图中左边第3条线
    //双点,短线风格的线
    pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.DashDotDot;
    g.DrawLine(pen1,10,40,100,40);//图中左边第4条线
    pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.Dot;//由点组成的线
    g.DrawLine(pen1,10,50,100,50);//图中左边第5条线
    pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.Solid;//实线笔
    pen1.EndCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;//后箭头
    g.DrawLine(pen1,150,10,250,10);//图中右边第1条线
    pen1.StartCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;//前箭头
    g.DrawLine(pen1,150,22,250,22);//图中右边第2条线
    pen1.EndCap=System.Drawing.Drawing2D.LineCap.RoundAnchor;
    g.DrawLine(pen1,150,34,250,34);//图中右边第3条线
    pen1.EndCap=System.Drawing.Drawing2D.LineCap.SquareAnchor;
    g.DrawLine(pen1,150,46,250,46);//图中右边第4条线
    pen1.EndCap=System.Drawing.Drawing2D.LineCap.Triangle;
    g.DrawLine(pen1,150,58,250,58);//图中右边第5条线
    pen1.EndCap=System.Drawing.Drawing2D.LineCap.DiamondAnchor;
    //图中右边第6条线
    g.DrawLine(pen1,150,70,250,70);
    }
    运行效果如右图:


    5.6 创建画刷
    画刷类对象指定填充封闭图形内部的颜色和样式,封闭图形包括矩形、椭圆、扇形、多边形和任意封闭图形。GDI+系统提供了了几个预定义画刷类,包括:
     SolidBrush:单色画刷。在名字空间System.Drawing中定义。
     HatchBrush:阴影画刷。以下画刷在名字空间System.Drawing.Drawing2D中定义。
     TextureBrush:纹理(图像)画刷。
     LinearGradientBrush:颜色渐变画刷。
     PathGradientBrush:使用路径及复杂的混合渐变画刷。
    5.6.1 单色画刷SolidBrush
    前边已使用过单色画刷。其构造函数只有1个,定义如下:
    SolidBrush brush1=new SolidBrush(Color color);//建立指定颜色的画刷
    在使用中可以修改其属性Color来修改其颜色,例如:brush1.Color=Color.Green;
    5.6.2 阴影画刷HatchBrush
    用指定样式(例如,多条横线、多条竖线、多条斜线等)、指定线条的颜色和指定背景颜色定义的画刷,阴影画刷有两个构造函数:
    //指定样式和线条的颜色的构造函数,背景色被初始化为黑色。
    HatchBrush brush1=new HatchBrush(HatchStyle h,Color c);
    //指定样式、线条的颜色和背景颜色的构造函数。
    HatchBrush brush1=new HatchBrush(HatchStyle h,Color c1,Color c2);
    有3个属性如下:
     属性backgroundColor:画刷背景颜色。
     属性foreColor:画刷线条的颜色。
     属性HatchStyle:该属性是只读的,不能修改,表示画刷的不同样式。
    例子5.6.2:显示了阴影画刷属性HatchStyle为不同值时画刷的不同样式。在Form1.cs文件头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;//得到窗体的使用的Graphics类对象
    HatchBrush b1=new
    HatchBrush(HatchStyle.BackwardDiagonal,Color.Blue,Color.LightGray);
    g.FillRectangle(b1,10,10,50,50);//矩形被填充左斜线,第1图
    HatchBrush b2=new HatchBrush(HatchStyle.Cross,Color.Blue,Color.LightGray);
    g.FillRectangle(b2,70,10,50,50);//矩形被填充方格,第2图
    HatchBrush b3=new
    HatchBrush(HatchStyle.ForwardDiagonal,Color.Blue,Color.LightGray);
    g.FillRectangle(b3,130,10,50,50);//矩形被填充右斜线,第3图
    HatchBrush b4=new
    HatchBrush(HatchStyle.DiagonalCross,Color.Blue,Color.LightGray);
    g.FillRectangle(b4,190,10,50,50);//矩形被填充菱形,第4图
    HatchBrush b5=new
    HatchBrush(HatchStyle.Vertical,Color.Blue,Color.LightGray);
    g.FillRectangle(b5,250,10,50,50);//矩形被填充竖线,第5图
    HatchBrush b6=new
    HatchBrush(HatchStyle.Horizontal,Color.Blue,Color.LightGray);
    g.FillRectangle(b6,310,10,50,50);//矩形被填充横线,第6图
    }
    运行效果如右图:

    5.6.3 纹理(图像)画刷TextureBrush
    纹理(图像)画刷使用图像来填充封闭曲线的内部,有8个构造函数,最简单的构造函数如下,其余请用帮助查看。
    TextureBrush(Image bitmap);//使用位图类对象作为画刷构造函数的参数
    下边的例子使用文件n2k.bmp建立位图类对象作为画刷的图案,在Form1文件的头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;//得到窗体的使用的Graphics类对象
    Pen pen1=new Pen(Color.Red);
    //位图类对象作为画刷图案,使用文件n2k.bmp建立位图类对象见5.10节
    TextureBrush b1=
    new TextureBrush(new Bitmap("C:\\WINNT\\system32\\n2k.bmp"));
    g.FillRectangle(b1,10,10,200,100);
    g.DrawRectangle(pen1,10,10,200,100);
    }
    文件C:\WINNT\system32\n2k.bmp定义的图形的显示效果如下:

    运行效果如右图。
    5.6.4 颜色渐变画刷LinearGradientBrush
    该类封装双色渐变和自定义多色渐变画刷。所有颜色渐变都是沿由矩形的宽度或两个点指定的直线定义的。默认情况下,双色渐变是沿指定直线从起始色到结束色的均匀水平线性变化。有8个构造函数,最简单的构造函数如下,其余请用帮助查看。
    public LinearGradientBrush(
    Point point1,//point1作为线性渐变直线开始点,也可以为PointF
    Point point2,//point2作为线性渐变直线结束点,也可以为PointF
    Color color1,Color color2);//线性渐变开始颜色和结束颜色
    下边的例子显示了不同线性渐变直线开始点和结束点,使用颜色渐变画刷从黄渐变到蓝的效果。在Form1文件的头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;//得到窗体的使用的Graphics类对象
    Pen pen1=new Pen(Color.Red);
    Point p1=new Point(10,10);//p1作为渐变直线开始点,也可以为PointF
    Point p2=new Point(50,10);//p2作为渐变直线结束点,也可以为PointF
    LinearGradientBrush brush1=//从黄渐变到蓝,见下图左图
    new LinearGradientBrush(p1,p2,Color.Yellow,Color.Blue);
    g.FillRectangle(brush1,10,10,200,100);
    g.DrawRectangle(pen1,10,10,200,100);
    p1=new Point(220,10);
    p2=new Point(270,50);
    LinearGradientBrush brush2=//从黄渐变到蓝,见下图右图
    new LinearGradientBrush(p1,p2,Color.Yellow,Color.Blue);
    g.FillRectangle(brush2,230,10,200,100);
    g.DrawRectangle(pen1,230,10,200,100);
    }
    运行效果如下图:

    5.6.5 画刷PathGradientBrush
    画刷PathGradientBrush可以实现复杂的渐变颜色。有5个构造函数,这里只介绍其中的一个,其参数GraphicsPath类使用见例子及5.7.11节。其余构造函数请用帮助查看。
    public PathGradientBrush(GraphicsPath path);//画刷PathGradientBrush构造函数
    画刷属性SurroundColors:一个Color结构的数组,它表示与PathGradientBrush对象填充的路径中的各点相关联的颜色的数组。SurroundColors数组中的每一Color结构都对应于路径中的点。
    下边的例子介绍画刷PathGradientBrush的使用方法,在Form1.cs文件的头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下,运行效果如下图。
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;//得到窗体使用的Graphics类对象
    Point[] p1=new Point[6];//点结构数组,有6个元素
    p1[0]=new Point(30,0);
    p1[1]=new Point(160,30);
    p1[2]=new Point(90,60);
    p1[3]=new Point(100,90);
    p1[4]=new Point(30,60);
    p1[5]=new Point(0,30);
    GraphicsPath path=new GraphicsPath(//建立GraphicsPath类对象
    p1,//由点数组p1定义绘制刷子的外轮廓线的路径
    new Byte[]{//定义数组p1每个点元素的关联类型,第1点是开始点
    //贝塞尔曲线必须由4点组成,因此第1、2、3、4点组成一条贝塞尔曲线,
    (byte)PathPointType.Start,(byte)PathPointType.Bezier,
    (byte)PathPointType.Bezier,(byte)PathPointType.Bezier,
    //第5、6点是直线,从第4点到第5点和从第5点到第6点画直线
    (byte)PathPointType.Line,(byte)PathPointType.Line,});
    //为了形成闭合曲线,增加最后一条直线
    PathGradientBrush brush1=new PathGradientBrush(path);//生成画刷
    brush1.SurroundColors=new Color[]{Color.Green,Color.Yellow,Color.Red,
    Color.Blue,Color.Orange,Color.OliveDrab,};//设置属性SurroundColors的值
    g.FillPath(brush1,path);
    }
    5.7 基本图形的绘制和填充
    Graphics类提供了一些绘图方法,用来绘制或填充各种图形。本节介绍这些方法。
    5.7.1 绘制线段
    两个绘制线段的函数和一个绘制多条线段的函数定义如下:
     void DrawLine(Pen pen,int x1,int y1,int x2,int y2);
    其中pen为画笔,(x1,y1)为画线起点坐标,(x2,y2)为画线终点坐标。
     DrawLine(Pen pen,Point p1,Point p2);
    其中pen为画笔,点p1为画线起点坐标,点p2为画线终点坐标。
     public void DrawLines(Pen pen,Point[] points);
    此方法绘制多条线段。从points[0]到points[1]画第1条线,从points[1]到points[2]画第2条线,依此类推。
    例子e5_7_1A:使用DrawLine()的例子,为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;
    Pen pen1=new Pen(Color.Red);
    g.DrawLine(pen1,30,30,100,100);//用笔pen1从点(30,30)到(100,100)画直线
    Point p1=new Point(30,40);
    Point p2=new Point(100,110);
    g.DrawLine(pen1,p1,p2);//用笔pen1从点(30,40)到(100,110)画直线
    }
    例子e5_7_1B:使用绘制线段函数画任意曲线(画正弦曲线,注意如何使用数学函数)。
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();//得到窗体的使用的Graphics类对象
    Pen pen1=new Pen(Color.Red);
    float y=50,y1,x1,x2;
    for(int x=0;x<720;x++)//画正弦曲线
    { x1=(float)x;
    x2=(float)(x+1);
    y1=(float)(50+50*Math.Sin((3.14159/180.0)*(x+1)));
    g.DrawLine(pen1,x1,y,x2,y1);
    y=y1;
    }
    }
    运行,在窗体中可以看到一条红色正弦曲线如下图。

    例子e5_7_1C:在画图程序中,可以用鼠标画任意曲线,现实现用拖动鼠标左键在主窗体中画曲线。每条曲线都是由若干很短的线段组成。鼠标左键按下状态下,移动鼠标,每移动很短距离,画出这段线段,所有这些线段组合起来,形成一条曲线。
    (1) 新建项目。增加两个私有变量:
    private bool mark=false;//表示鼠标左键是否按下,如按下鼠标再移动将画曲线
    private Point point;//记录画下一很短线段的起始点。
    (2) 为Form窗体的事件OnMouseDown,OnMouseUp,OnMouseMove增加事件函数如下:
    private void Form11_MouseDown(object sender,//鼠标按下事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { if(e.Button==MouseButtons.Left)//如果鼠标左键按下
    { point.X=e.X;//记录曲线的第一个点的坐标
    point.Y=e.Y;
    mark=true;//表示鼠标左键已按下,鼠标如果再移动,将画曲线
    }
    }
    private void Form1_MouseMove(object sender,//鼠标移动事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { if(mark)//如果鼠标左键按下
    { Graphics g=this.CreateGraphics();//得到窗体的使用的Graphics类对象
    Pen pen1=new Pen(Color.Black);//黑笔
    g.DrawLine(pen1,point.X,point.Y,e.X,e.Y);//画线
    point.X=e.X;//记录画下一线段的起始点的坐标
    point.Y=e.Y;
    }
    }
    private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
    { mark=false;}//停止画线
    (3) 运行,在Form窗体拖动鼠标左键可以画线。但最小化后再最大化后,图形消失。修改上例,使其能克服这个缺点。实现的思路是记录每一条曲线的每一条很短线段的坐标。使用ArrayList类对象记录曲线以及曲线中的点,请注意ArrayList类使用方法。
    (4) 为定义主窗体的Form1类中增加私有变量:
    private ArrayList Point_List;//用来记录1条曲线的所有点。
    private ArrayList Line_List;//用来记录每条曲线,既Point_List对象。
    在Form1类构造函数中增加语句:Line_List=new ArrayList();
    (5) 修改主窗体事件OnMouseDown,OnMouseUp,OnMouseMove事件处理函数如下:
    private void Form1_MouseDown(object sender,//阴影部分为修改的内容
    System.Windows.Forms.MouseEventArgs e)
    { if(e.Button==MouseButtons.Left)
    { Point_List=new ArrayList();//建立数组,记录1条曲线的所有点
    point.X=e.X;
    point.Y=e.Y;
    mark=true;
    Point_List.Add(point);//曲线起点的坐标
    }
    }
    private void Form1_MouseMove(object sender,
    System.Windows.Forms.MouseEventArgs e)
    { if(mark)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Black);
    g.DrawLine(pen1,point.X,point.Y,e.X,e.Y);
    point.X=e.X;
    point.Y=e.Y;
    Point_List.Add(point);//记录曲线中其它点的坐标
    }
    }
    private void Form1_MouseUp(object sender,System.Windows.Forms.MouseEventArgs e)
    { mark=false;
    Line_List.Add(Point_List);//记录此条线,注意参数是Point_List
    }
    (6) 增加Form窗体的Paint事件函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;
    Pen pen1=new Pen(Color.Black);
    Point p1,p2;
    foreach(ArrayList l in Line_List)//取出每条线
    { for(int k=0;k<(l.Count-1);k++)//重画每条线的点
    { p1=(Point)l[k];
    p2=(Point)l[k+1];
    g.DrawLine(pen1,p1,p2);
    }
    }
    }
    (7) 运行,在Form窗体拖动鼠标可以画线。最小化后再最大化后,图形不消失。
    5.7.2 ArrayList类
    ArrayList类是容量可以动态增加的数组,其元素类型可以是任意类型。和其它数组一样,ArrayList类可以使用对象名[索引号]引用其元素,索引号也从零开始。前边已多次使用此类,例如:控件ListBox和ComboBox的属性Items,以及5.7.1节中的例子。其常用的属性及方法如下:
     属性Count:ArrayList中实际包含的元素数。
     方法Add:将参数指定的对象添加到ArrayList对象的结尾处。
     方法Clear:从ArrayList中移除所有元素。
     方法Contains:bool类型,确定参数指定的元素是否在ArrayList中。
     方法IndexOf:int类型,顺序查找和参数指定对象相同的第一个元素的索引。
     方法Insert:插入数据,第1个参数为插入的位置(索引号),第2个参数为插入的对象。
     方法LastIndexOf:顺序查找和参数指定对象相同的最后一个元素的索引。
     方法RemoveAt:移除指定索引处的元素。
     方法Sort:对整个ArrayList中的元素进行排序。
    5.7.3 画椭圆(圆)及键盘消息的使用
    两个画椭圆的函数的功能是画指定矩形的内切椭圆,如为正方形则画圆,两个函数如下:
     void DrawEllipse(Pen pen,int x,int y,int width,int height);
    其中pen为画笔,画外轮廓线,(x1,y1)为指定矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。
     void DrawEllipse(Pen pen,Rectangle rect);
    其中pen为画笔,画外轮廓线,rect为指定矩形结构对象。
    例子5_7_3A:画椭圆。为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    g.DrawEllipse(pen1,10,10,200,100);
    Rectangle rect=new Rectangle(20,20,100,100);
    g.DrawEllipse(pen1,rect);
    }
    例子5_7_3B:用四个箭头键移动窗体中的圆球。移动圆球,实际是先把前边画的圆擦掉,在新的位置重新画圆。如要擦掉圆,可以用窗体背景色作为笔和刷子的颜色,在圆的原先位置重画和填充圆。注意键盘事件处理函数的使用。具体实现步骤如下:
    (1) 新建项目。在Form1类中增加变量:int x,y,记录定义圆位置的矩形左上角的坐标。
    (2) 在Form1类中增加一个方法,该方法按照参数指定颜色画圆,方法定义如下:
    void DrawCir(Color color)//参数是画圆的笔和刷子的颜色
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(color);
    SolidBrush brush1=new SolidBrush(color);
    g.DrawEllipse(pen1,x,y,100,100);
    g.FillEllipse(brush1,x,y,100,100);
    }
    (3) 为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { DrawCir(Color.Red);}
    (4) 为主窗体KeyDown事件增加事件函数如下:(注意不要使用KeyPress事件,其事件处理函数的第2个参数e的e.KeyChar是按下键的ASCII值,但很多键无ASCII值。)
    private void Form1_KeyDown(object sender,System.Windows.Forms.KeyEventArgs e)
    { switch (e.KeyCode)//e.KeyCode是键盘每个键的编号
    { case Keys.Left://左箭头键编号
    DrawCir(this.BackColor);//用Form窗体的背静色画圆,即擦除圆
    x=x-10;//圆左移
    DrawCir(Color.Red);//在新的位置用红色画圆,效果是圆左移
    break;
    case Keys.Right://圆右移
    DrawCir(this.BackColor);
    x+=10;
    DrawCir(Color.Red);
    break;
    case Keys.Down://圆下移
    DrawCir(this.BackColor);
    y+=10;
    DrawCir(Color.Red);
    break;
    case Keys.Up://圆上移
    DrawCir(this.BackColor);
    y=y-10;
    DrawCir(Color.Red);
    break;
    }
    }
    (5) 运行,可以用4个箭头键移动红色圆。
    使用KeyDown事件,事件处理函数的第2个参数e的e.KeyCode是键盘每个键的编号,其它常用键的编号如下:数字键0-9编号为Keys.D0-Keys.D9;字母键A-Z为Keys.A-Keys.Z;F0-F12键表示为Keys.F0-Keys.F12等。
    如使用KeyPress事件,事件处理函数的第2个参数e的e.KeyChar表示按键的ACSII值,例如可用如下语句if(e.KeyChar==(char)13)判断是否按了回车键。
    5.7.4 画矩形
    两个绘制1个矩形(正方形)的函数和一个绘制多个矩形(正方形)的函数定义如下:
     void DrawRectangle(Pen pen,int x,int y,int width,int height);
    其中pen为画笔,画外轮廓线,(x1,y1)为矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。
     void DrawRectangle(Pen pen,Rectangle rect);
    其中pen为画笔,画外轮廓线,rect为矩形结构对象。
     public void DrawRectangles(Pen pen,Rectangle[] rects);
    绘制一系列由Rectangle结构指定的矩形。
    例子5_7_3:画矩形。为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    g.DrawRectangle(pen1,10,10,200,100);
    Rectangle rect=new Rectangle(20,20,100,100);
    g.DrawRectangle(pen1,rect);
    }
    5.7.5 绘制圆弧
    DrawArc方法绘制指定矩形的内切椭圆(圆)中的一段圆弧,方法定义如下:
    void DrawArc(Pen pen,int x,int y,int width,int height,int StartAngle,int EndAngle);
    其中pen为画笔,画外轮廓线,(x1,y1)为矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。StartAngle为圆弧的起始角度,EndAngle为圆弧的结束角度,单位为度。指定矩形的中心点做矩形宽和高的的垂线作为x,y轴,中心点为圆点。圆点右侧x轴为0度,顺时针旋转为正角度,逆时针旋转为负角度。
    例子5_7_4:画圆弧。为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    g.DrawArc(pen1,10,10,200,100,0,30);
    }
    5.7.6 DrawPie方法
    DrawPie方法方法绘制指定矩形的内切椭圆(圆)中的一段圆弧,并且用指定矩形的中心点连接开始点和结束点,这个图形叫做饼图,方法定义如下:
    void DrawPie(Pen pen,int x,int y,int width,int height,int StartAngle,int EndAngle);
    方法参数和DrawArc方法参数相同。
    例子5_7_5:画饼图。为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    g.DrawPie(pen1,10,10,200,100,0,30);
    }
    5.7.7 Bezier曲线
    可以使用DrawBezier方法画一条Bezier曲线。它的两个画线函数定义如下:
     Void DrawBezier(Pen pen,float x1,float y1,
    float x2,float y2,float x3,float y3,float x4,float y4);
    其中pen是画笔对象,画轮廓线,(x1,y1)是起始点,(x2,y2)是第一控制点,(x3,y3)是第二控制点,(x4,y4)是结束点。
     Void DrawBezier(Pen pen,Point p1,Point p2,Point p3,Point P4);
    其中pen是画笔对象,画轮廓线,p1是起始点,p2是第一控制点,p3是第二控制点,p4是结束点。
    例子5_7_6:画Bezier曲线。为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    g.DrawBezier(pen1,10,10,200,100,50,60,100,200);
    }
    5.7.8 DrawPolygon方法
    该方法画一个多边形,使用点结构数组定义多边形的顶点。两个画线函数定义如下:
     void DrawPolygon(Pen pen,Point[] points);
     void DrawPolygon(Pen pen,PointF[] points);//点坐标可以是小数
    例子5_7_7:画一个多边形如下图,为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    Point[] p1=new Point[]
    { new Point(10,10),
    new Point(60,40),
    new Point(100,80),
    new Point(60,100)
    };
    g.DrawPolygon(pen1,p1);
    }
    5.7.9 DrawClosedCurve方法
    DrawClosedCurve方法用来绘制经过Point结构数组中每个点的闭合基数样条。基数样条是一连串单独的曲线,这些曲线连接起来形成一条较大的曲线。样条由点的数组指定,并通过该数组中的每一个点。基数样条平滑地通过数组中的每一个点,请比较一下本节的图形和上节图形的区别。如果最后一个点不匹配第一个点,则在最后一个点和第一个点之间添加一条附加曲线段以使该图闭合,点Point结构数组必须至少包含四个元素,此方法使用默认张力0.5。有4个画线函数,常用的2个画线函数定义如下:
     void DrawClosedCurve(Pen pen,Point[] points);
     void DrawClosedCurve(Pen pen,PointF[] points);//点坐标可以是小数
    例子e5_7_9:使用DrawClosedCurve方法,绘制有4个元素的Point结构数组定义的闭合基数样条闭合曲线如下图,为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red);
    Point[] p1=new Point[]
    { new Point(10,10),
    new Point(60,40),
    new Point(100,80),
    new Point(60,100)
    };
    g.DrawClosedCurve(pen1,p1);
    }
    5.7.10 DrawCurve方法
    用DrawCurve方法和DrawClosedCurve方法一样,用来绘制经过Point结构数组中每个点的闭合基数样条,但最后两个点之间不连线。常用的两个画线函数定义如下:
     void DrawPolygon(Pen pen,Point[] points);
     void DrawPolygon(Pen pen,PointF[] points);
    例子5_7_9:使用DrawCurve方法,绘制有4个元素的Point结构数组定义的闭合基数样条闭合曲线如下图,为主窗体Paint事件增加事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    Pen pen1=new Pen(Color.Red,3);
    Point[] p1=new Point[]
    { new Point(10,10),
    new Point(60,40),
    new Point(100,80),
    new Point(60,100)
    };
    g.DrawCurve(pen1,p1);
    }
    5.7.11 DrawPath方法和GraphicsPath类
    用DrawPath方法可以绘制多个曲线,方法参数GraphicsPath类对象path定义每个曲线类型。DrawPath方法定义如下:
    void DrawPath(Pen pen,GraphicsPath path);
    例子5_7_10A:用DrawPath方法画一个矩形和其内切椭圆如下图,在Form1.cs文件的头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Rectangle myEllipse=new Rectangle(20,20,100,50);
    GraphicsPath myPath=new GraphicsPath();//建立GraphicsPath()类对象
    myPath.AddEllipse(myEllipse);//追加一椭圆
    myPath.Add Rectangle(myEllipse);//追加一矩形
    Pen myPen=new Pen(Color.Black,2);
    //画这条曲线,即椭圆和矩形。
    e.Graphics.DrawPath(myPen,myPath);
    }
    GraphicsPath类是表示一系列相互连接的直线和曲线的路径,应用程序使用此路径来绘制曲线的轮廓、填充形状内部和创建剪辑区域。由属性PathPoints(点数组)定义绘制直线和曲线的路径。路径可由任意数目的图形(子路径)组成,每一图形都是由一系列相互连接的直线和曲线或几何形状基元构成的。由属性PathTypes(字节数组)定义属性PathPoints(点数组)中每个点元素的关联图形或曲线类型,图形的起始点是相互连接的一系列直线和曲线中的第一点。终结点是该序列中的最后一点。GraphicsPath在System.Drawing.Drawing2D名字空间。常用属性、事件和方法定义如下:
     构造函数GraphicsPath();//建立空对象
     构造函数public GraphicsPath(Point[] pts,byte[] types);
    参数pts为Point结构数组,数组元素表示构造路径所使用的点。参数types指定路径中相应点的关联图形或曲线类型数组。参见5.6.5节。
     属性PointCount:获取 PathPoints 或 PathTypes 数组中的元素数。
     方法IsVisible:指定点是否包含在此 GraphicsPath 对象内。
     方法:void AddArc(Rectangle rect,float startAngle,float sweepAngle);
    在代表要描绘图形的GraphicsPath类对象中追加一段椭圆弧。
     方法:void AddEllipse(Rectangle rect),追加一椭圆(圆)
     方法:void AddLine(Point pt1,Point pt2),追加一线段
     方法:void AddRectangle(Rectangle rect),追加一矩形
    还有其它增加曲线的方法,例如:AddBezier 方法、AddBeziers 方法、AddClosedCurve 方法、AddCurve 方法、AddLines 方法、AddPath 方法、AddPie 方法、AddPolygon 方法、AddRectangles 方法、AddString 方法等,请用帮助查看。
    5.7.12 DrawString方法
    DrawString方法在指定位置并且用指定的Brush和Font对象绘制指定的文本字符串。有6个重载方法,常用的一个是:
    public void DrawString(string s,//s是为要显示的字符串
    Font font,//显示的字符串使用的字体
    Brush brush,//用刷子写字符串
    PointF point);//显示的字符串左上角的坐标
    最后一个参数也可以是RectangleF对象,仍表示显示的字符串位置。还可以再增加一个参数,即第5个参数,StringFormat对象,它指定应用于所绘制文本的格式化属性(如行距和对齐方式)。在打印和打印预览一节已使用了这个方法。
    例子5_7_12:用DrawString方法显示字符串,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { String drawString="Sample Text";//要显示的字符串
    Font drawFont=new Font("Arial",16);//显示的字符串使用的字体
    SolidBrush drawBrush=new SolidBrush(Color.Black);//写字符串用的刷子
    PointF drawPoint=new PointF(20.0F,20.0F);//显示的字符串左上角的坐标
    e.Graphics.DrawString(drawString,drawFont,drawBrush,drawPoint);
    }
    5.7.13 DrawImage和DrawIcon方法
    用来在指定的位置绘制指定的Image对象和图标。Graphics类中有多个DrawImage重载方法,最简单的是以下方法:
     public void DrawImage(Image image,Point point);
    在指定的位置使用原始物理大小绘制指定的Image对象。参数1为要绘制的Image对象,参数2表示所绘制图像的左上角在窗体中的位置。
     public void DrawImage(Image image,Point[] destPoints);
    在指定位置并且按指定形状和大小绘制指定的Image对象。参数1为要绘制的Image对象,参数2表示有3个元素的Point结构数组,三个点定义一个平行四边形。缩放和剪切image参数表示的图像,以在此平行四边形内显示。参数2也可以是一个矩形结构。
     public void DrawImage(Image image,//要绘制的Image对象
    Rectangle destRect,//指定所绘制图像的位置和大小,图像进行缩放以适合该矩形
    Rectangle srcRect,//指定image对象中要绘制的部分
    GraphicsUnit srcUnit);//枚举的成员,指定srcRect参数所用的度量单位
    例子5_7_1A:在指定位置绘制Image对象指定部分。
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Image newImage=Image.FromFile("d:\\CSARP\\1.jpg");//建立要绘制的Image图像
    Rectangle destRect=new Rectangle(10,10,150,150);//显示图像的位置
    Rectangle srcRect=new Rectangle(50,50,150,150);//显示图像那一部分
    GraphicsUnit units=GraphicsUnit.Pixel;//源矩形的度量单位设置为像素
    e.Graphics.DrawImage(newImage,destRect,srcRect,units);//显示
    }//如果把显示图像的位置变宽,看一下效果,为什么?其它重载方法可用帮助查看。
     public void DrawIcon(Icon icon,Rectangle targetRect);
    在Rectangle结构指定的区域内绘制指定的Icon对象表示的图标。
    例子5_7_1B:在指定位置画图标。
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Icon newIcon=new Icon("d:\\CSARP\\TASKS.ICO");
    Rectangle rect=new Rectangle(100,100,200,200);
    e.Graphics.DrawIcon(newIcon,rect);
    }
    5.7.14 FillEllipse方法
    该方法用指定画刷来填充指定矩形的内切椭圆(圆)。两个填充函数的定义如下:
     void FillEllipse(Brush brush,int x,int y,int width,int height);
    其中brush为指定画刷,(x1,y1)为指定矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。
     void DrawEllipse(Pen pen,Rectangle rect);
    其中brush为指定画刷,rect为指定矩形结构对象。
    例子5_7_13:用指定画刷来填充指定矩形的内切椭圆。
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    SolidBrush brush=new SolidBrush(Color.Blue);
    g.FillEllipse(brush,10,10,200,100);
    Rectangle rect=new Rectangle(120,120,100,100);
    g.FillEllipse(brush,rect);
    }
    5.7.15 FillRectangle方法
    FillRectangle方法用指定画刷来填充指定矩形。两个填充函数定义如下:
     void FillRectangle(Brush brush,int x,int y,int width,int height);
    其中brush为指定画刷,(x1,y1)为矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。
     void FillRectangle(Brush brush,Rectangle rect);
    其中brush为指定画刷,rect为矩形结构对象。
    例子5_7_14:用指定画刷来填充指定矩形。
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    SolidBrush brush=new SolidBrush(Color.Blue);
    g.FillRectangle(brush,10,10,200,100);
    Rectangle rect=new Rectangle(120,120,100,100);
    g.FillRectangle(brush,rect);
    }
    5.7.16 FillPie方法
    FillPie方法用指定画刷来填充指定饼图。函数定义如下:
    void FillPie(Brush brush,int x,int y,int width,
    int height,int StartAngle,int EndAngle);
    其中brush为指定画刷,方法其它参数和DrawArc方法参数相同。
    例子5_7_15:用指定画刷来填充指定饼图。
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { Graphics g=this.CreateGraphics();
    SolidBrush brush=new SolidBrush(Color.Blue);
    g.FillPie(brush,10,10,200,100,0,30);
    }
    5.7.17 FillRegion方法和Region类
    FillRegion方法用刷子填充区域Region类对象内部。Region类对象由矩形和路径构成。如果区域不闭合,则在最后一个点和第一个点之间添加一条额外的线段来将其闭合。方法定义如下:
    public void FillRegion(Brush brush,Region region);
    第1个参数是填充使用的刷子,第2个参数是指定的区域。
    例子5.7.16A:用纯蓝色刷子,使用FillRegion方法填充一个矩形区域。
    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    { SolidBrush blueBrush=new SolidBrush(Color.Blue);
    Rectangle fillRect=new Rectangle(10,10,100,100);
    Region fillRegion=new Region(fillRect);
    e.Graphics.FillRegion(blueBrush,fillRegion);
    }
    区域是输出设备显示区域的一部分。区域可以是简单的(单个矩形)或复杂的(多边形和闭合曲线的组合)。下图中的左数第1图显示了两个区域:一个利用矩形构造,另一个利用路径构造。可以通过合并现有的区域来创建复杂区域。Region类提供了以下合并区域的方法:Intersect、Union、Xor、Exclude和Complement。两个区域的交集是同时属于两个区域的所有点的集合,方法Intersect可以得到两个Region类对象的交集。并集是多个区域的所有点的集合,方法Union可以得到两个Region类对象的并集。方法Xor可以得到两个Region类对象的并集减去这两者的交集,即下图中的左数第4图显示的蓝色区域。方法Exclude和Complement可以得到1个Region类对象和参数指定的Region类对象的不相交的部分,即下图中的左数第5图显示区域。

    Region类常用的方法如下:
     构造函数Region:可以没有参数,即创建一个空区域。也可以有一个参数,可以是GraphicsPath、Rectangle、RectangleF和RegionData类型,由此生成一个区域。
     方法Exclude和Complement:得到1个Region类对象和参数指定的Region类对象的不相交的部分。参数可以是GraphicsPath、Rectangle、RectangleF和Region类型。
     方法Equals:比较2个区域是否相等。参数1是要比较的区域,参数2是要绘制表面的Graphics对象。
     方法Intersect:可以得到两个Region类对象的交集。参数可以是GraphicsPath、Rectangle、RectangleF和Region类型。
     方法IsEmpty:测试是否为空区域。
     方法IsVisible:测试参数指定的点或矩形是否在区域中。
     方法Union:可以得到两个Region类对象的并集。
     方法Xor:可以得到两个Region类对象的并集减去这两者的交集。
    例子5_7_16B:建立2个矩形,有部分相交,将相交部分填充为蓝色。在Form1.cs文件的头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { Graphics g=e.Graphics;
    Rectangle regionRect=new Rectangle(10,10,50,50);
    Pen pen1=new Pen(Color.Black);
    g.DrawRectangle(pen1,regionRect);//绘制第1个矩形
    RectangleF unionRect=new RectangleF(25,25,50,50);//第2个矩形
    pen1.Color=Color.Red;
    g.DrawEllipse(pen1,unionRect);//画椭圆
    GraphicsPath myPath=new GraphicsPath();//建立GraphicsPath()类对象
    myPath.AddEllipse(unionRect);//追加一椭圆
    Region myRegion=new Region(regionRect);//建立表示第1个矩形的区域
    myRegion.Intersect(myPath);//得到两个区域的交集
    SolidBrush myBrush=new SolidBrush(Color.Blue);
    e.Graphics.FillRegion(myBrush,myRegion);//填充区域
    }
    运行效果如右图。除了以上介绍的填充方法,还有如下方法:FillClosedCurve方法、FillPath方法、FillPolygon方法等,请用帮助查看。
    5.8 Matrix类和图形的平移、变形、旋转
    本节介绍使用Matrix类实现图形的平移、变形、旋转。
    5.8.1 Matrix类
    Matrix类封装了表示几何变形的3行3列仿射矩阵,可以记录图形的平移、变形、旋转等操作。主要包括如下方法:
     构造函数Matrix():创建一个空Matrix类对象。
     方法Rotate:在Matrix类对象中增加相对于原点顺时针旋转指定角度的操作。参数指定旋转角度。
     方法RotateAt:在Matrix类对象中增加相对于指定点顺时针旋转指定角度的操作。参数1指定旋转角度。参数2指定相应的点。
     方法Scale:在X轴或Y轴方向对图形放大或缩小。参数1指定在X轴方向缩放的值,参数2指定在Y轴方向缩放的值。
     方法Translate:使图形在X轴或Y轴方向移动。参数1指定在X轴方向移动的值,参数2指定在Y轴方向移动的值。
    例子5_8_1:下面的示例创建了复合变形(先旋转30度,再在y方向上缩放2倍,然后在x方向平移5个单位)的Matrix类对象。注意变形得顺序非常重要。一般说来,先旋转、再缩放、然后平移,与先缩放、再旋转、然后平移是不同的。
    Matrix myMatrix=new Matrix();
    myMatrix.Rotate(30);
    myMatrix.Scale(1,2,MatrixOrder.Append);
    myMatrix.Translate(5,0,MatrixOrder.Append);
    5.8.2 图形的平移、变形、旋转
    GraphicsPath类的Transform方法可以缩放、转换、旋转或扭曲GraphicsPath对象,参数Matrix对象表示需要的变形。
    例子5_8_2A:下面的示例代码执行下列操作:创建一个路径并向该路径添加一个椭圆。将路径绘制到主窗体上。创建一个Matrix类对象,在对象中增加在X轴方向上将路径移动100个单位操作。将该已变形的路径绘制到屏幕。观察一下变换前和变换后的不同,注意,初始椭圆是以黑色绘制的,而变形后的椭圆是以红色绘制的。在Form1.cs文件的头部增加语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数如下:
    private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e)
    { GraphicsPath myPath=new GraphicsPath();//创建一个路径
    myPath.AddEllipse(0,0,50,70);//向路径添加一个椭圆
    e.Graphics.DrawPath(Pens.Black,myPath);//用黑笔画出这个椭圆
    Matrix translateMatrix=new Matrix();//创建一个Matrix类对象
    translateMatrix.Translate(25,0);//在X轴方向上移动25个单位
    //根据Matrix类对象修改路径myPath
    myPath.Transform(translateMatrix);
    //用红笔按新路径画这个椭圆
    e.Graphics.DrawPath(new Pen(Color.Red,2),myPath);
    }
    运行效果如右图。请读者实现变形、旋转。
    5.8.3 仿射矩阵
    m×n矩阵是以m行和n列排列的一组数字,例如一个3×3矩阵记为如下图形式,也可简记为:[a33]。

    两个行、列分别相同的矩阵可以相加,例如:[a33]+[b33]=[c33],矩阵相加运算的规则是:ci j=ai j+bi j,i和j为常量,即相对应位置的项相加。如果有矩阵[am n]和[bn k],[am n]矩阵的列数等于[bn k]矩阵的行数,两个矩阵可以相乘,记为:[am n]*[bn k]=[cm k],矩阵相乘的运算的规则是:ci j=∑(ai t+bt j),其中,i和j为常量,t为变量,初始值为1,最大值为n。
    如果将平面中的点视为1×2矩阵,则可通过将该点乘以2×2变换矩阵来变形该点。下图是点(2,1)在X轴按比例3放大,Y轴不变。

    下图表示点(2,1)旋转了90度。

    下图表示点(2,1)以x轴为对称轴的新点。

    假定要从点(2,1)开始,将其旋转90度,在x方向将其平移3个单位,在y方向将其平移4个单位。可通过先使用矩阵乘法再使用矩阵加法来完成此操作。

    如果用矩阵[2 1 1]代表点(2,1),使用一个3×3变换矩阵,可以用一个矩阵乘法代替以上的两个矩阵运算,见下图:

    注意运运结果的矩阵[2 6 1]代表点(2,6),即点(2,1)映射到了点(2,6)。这个3×3矩阵叫作仿射矩阵,Matrix类中用这个仿射矩阵记录增加的各种变换操作。它和前边的两个2×2矩阵的关系如下图,其中第三列固定为0、0、1。

    Matrix类增加了一些方法处理这个仿射矩阵,主要包括:逆转方法Invert、相乘方法Multiply、重置为单位矩阵方法Reset等。
    5.9 图形文件格式
    在磁盘中存储图形和图像的文件格式有多种。GDI+支持以下图形文件格式。
     位图文件(.bmp):
    位图文件是Windows使用的一种标准格式,用于存储设备无关和应用程序无关的图像。BMP文件通常不压缩,因此不太适合Internet传输。
     可交换图像文件格式(.gif):
    GIF是一种用于在Web页中显示图像的通用格式。GIF文件是压缩的,但是在压缩过程中没有信息丢失,解压缩的图像与原始图像完全一样。GIF文件中的一种颜色可以被指定为透明,这样,图像将具有显示它的任何Web页的背景色。在单个文件中存储一系列GIF图像可以形成一个动画GIF。GIF文件每个像素颜色最多用8位表示,所以它们只限于使用256种颜色。
     JPG文件(.jpg):
    JPEG是联合摄影专家组提出的一种适应于自然景观(如扫描的照片)的压缩方案。一些信息会在压缩过程中丢失,但是这些丢失人眼是察觉不到的。JPEG文件每像素颜色用24位表示,因此能够显示超过16,000,000种颜色。JPEG文件不支持透明或动画。JPEG图像中的压缩级别是可以控制的,但是较高的压缩级别(较小的文件)会导致丢失更多的信息。对于一幅以20:1压缩比生成的图像,人眼难以把它和原始图像区别开来。JPEG是一种压缩方案,不是一种文件格式。“JPEG文件交换格式(JFIF)”是一种文件格式,常用于存储和传输根据JPEG方案压缩的图像。Web浏览器显示的JFIF文件使用.jpg扩展名。
     可移植网络图形(.PNG)
    PNG格式不但保留了许多GIF格式的优点,还提供了超出GIF的功能。像GIF文件一样,PNG文件在压缩时也不损失信息。PNG文件能以每像素8、24或48位来存储颜色,并以每像素1、2、4、8或16位来存储灰度。相比之下,GIF文件只能使用每像素1、2、4或8位。PNG文件还可为每个像素存储一个透明度alpha值,该值指定了该像素颜色与背景颜色混合的程度。PNG优于GIF之处在于它能够逐渐显示一幅图像,也就是说,当图像通过网络连接到达时显示将越来越近似。PNG文件可包含伽玛校正和颜色校正信息,以便图像可在各种各样的显示设备上精确地呈现。
     图元文件(.emf):
    GDI+提供Metafile类,以便能够记录和显示图元文件。图元文件,也称为矢量图像,是一种存储为一系列绘图命令和设置的图像。Metafile对象记录的命令和设置可以存储在内存中或保存到文件或流。下面示例在主窗体显示了一个图元文件的图形。
    Graphics g=this.CreateGraphics();
    Metafile myMetafile=new Metafile("SampleMetafile.emf");
    myGraphics.DrawImage(myMetafile,10,10);//图形左上角的位置是(10,10)
     支持的文件格式还有:图标文件(.ico)、.EXIF、.TIFF、.ICON、.WMF等
    5.10 图形框PictureBox控件
    PictureBox控件常用于图形设计和图像处理程序,又称为图形框,该控件可显示和处理的图像文件格式有:位图文件(.bmp)、图标文件(.ico)、GIF文件(.gif)和JPG文件(.jpg)。其常用的属性、事件和方法如下:
     属性Image:指定要显示的图像,一般为Bitmap类对象。
     属性SizeMode:指定如何显示图像,枚举类型,默认为Normal,图形框和要显示的图像左上角重合,只显示图形框相同大小部分,其余不显示;为CentreImage,将图像放在图形框中间,四周多余部分不显示;为StretchImage,调整图像大小使之适合图片框。
     方法CreateGraphics():建立Graphics对象。
     方法Invalidate():要求控件对参数指定区域重画,如无参数,为整个区域。
     方法Update():方法Invalidate()并不能使控件立即重画指定区域,只有使用Update()方法才能立即重画指定区域。使用见5.10.4节中的鼠标移动事件处理函数。
    例子e5_10:使用PictureBox控件显示图像
    (1) 新建项目。放PictureBox控件到窗体。属性Name=pictureBox1。
    (2) 放Button控件到窗体。属性Name=button1。
    (3) 放OpenFileDialog控件到窗体。属性Name=openFileDialog1。
    (4) 可以在设计阶段修改属性Image为指定图形文件,设定初始显示的图像。
    (5) button1控件事件处理函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { if(openFileDialog1.ShowDialog()==DialogResult.OK)
    { Bitmap p1=new Bitmap(openFileDialog1.FileName);//Bitmap类见下节
    pictureBox1.Image=p1;
    }
    }
    5.11 Bitmap类
    System.Drawing命名空间有一个类Image,用来处理图像。Image类的派生类Bitmap类封装了GDI+中的位图,可以处理由像素数据定义的图像。Image类的派生类metafile处理元文件,此类文件用记录绘图命令的方法存储图像。
    5.11.1 Bitmap类支持的图像类型
    使用Bitmap类可以显示和处理多种图像文件,可处理的文件类型及文件扩展名如下:扩展名为.bmp的位图文件、扩展名为.ico的图标文件、扩展名为.gif的GIF文件、扩展名为.jpg的JPG文件。当使用构造函数Bitmap(string FileName)建立Bitmap类对象时,如果文件是以上类型,将自动转换为位图格式存到Bitmap类对象中。可使用Bitmap类方法Save(string FileName,ImageFormat imageFormat)把Bitmap类对象中的位图存到文件中,其中第1个参数是选定的文件名,第2个参数是指定文件存为那种类型,可以是如下类型:System.Drawing.Imaging.ImageFormat.bmp(或.ico、.gif、.jpg)。
    5.11.2 Bitmap类的方法
     方法SetPixel():画点方法,前2个参数是指定点的位置,第3个参数是颜色值。
     方法GetPixle():得到指定点的颜色,2个参数是指定点的位置,返回颜色值。
     有多个构造函数例如:new Bitmap(”图像文件名”),new Bitmap(宽,高)等。
     方法Save():第1个参数是文件名,第2个参数是指定文件存为那种类型,可以是如下类型:System.Drawing.Imaging.ImageFormat.bmp(或.ico、.gif、.jpg)。
     方法Dispose():释放位图对象
    5.11.3 画点
    例子e5_11_3:用SetPixel画点,GetPixle得到指定点的颜色。放Button和PictureBox控件到主窗体。增加Button控件单击事件函数如下:
    private void button1_Click(object sender,System.EventArgs e)
    { pictureBox1.Width=720;//设定pictureBox1的宽和高
    pictureBox1.Height=110;
    Bitmap bits=new Bitmap(720,110);//建立位图对象,宽=720,高=110
    int x,y;
    for(x=0;x<720;x++)//画正弦曲线
    { y=(int)(50+50*Math.Sin((3.14159/180.0)*x));
    bits.SetPixel(x,y,Color.Red);
    }
    pictureBox1.Image=bits;//位图对象在pictureBox1中显示
    Color c1=bits.GetPixel(20,20);
    string s="R="+c1.R+",G="+c1.B+",G+"+c1.G;
    MessageBox.Show(s);
    }
    5.11.4 在PictureBox中画任意曲线
    例子e5_11_4:例子5_7_1C实现了画任意曲线程序,用ArrayList类对象记录绘制的曲线。在该程序中增加橡皮功能、图像的拷贝、图像的剪贴、图像的粘贴比较困难,也不能和画图程序交换文件。为了实现这些功能,用图形框(PictureBox控件)显示绘制图形。绘制图形必须存在图形框属性Image引用的位图对象中,图形框显示的图像被破坏,图形框响应Paint事件,将用其属性Image引用的位图对象恢复所绘制的图形。仅将图形绘制在图形框表面,图形框响应Paint事件,绘制的图形将不能被恢复,也就是说,绘制在图形框表面的图形丢失了。具体实现步骤如下:
    (1) 新建项目。增加4个私有变量:private bool mark=false; private Point point;
    private Bitmap bits; private Graphics bitG;
    (2) 放PictureBox控件到窗体,修改属性Dock=Fill。
    (3) 在构造函数中增加语句:
    //建立位图对象,宽和高为指定值
    bits=new Bitmap(pictureBox1.Width,pictureBox1.Height);
    bitG=Graphics.FromImage(bits);//得到位图对象的Graphics类的对象
    bitG.Clear(Color.White);//用白色清除位图对象中的图像
    pictureBox1.Image=bits;//位图对象在pictureBox1中显示
    (4) 为控件PictureBox事件OnMouseDown,OnMouseUp,OnMouseMove增加事件处理函数:
    private void pictureBox1_MouseDown(object sender,//鼠标按下事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { if(e.Button==MouseButtons.Left)//是否是鼠标左键按下
    { point.X=e.X;
    point.Y=e.Y;//画线段开始点
    mark=true;//鼠标左键按下标识
    }
    }
    private void pictureBox1_MouseMove(object sender,//鼠标移动事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { if(mark)//如果鼠标左键按下
    { Graphics g=pictureBox1.CreateGraphics();
    Pen pen1=new Pen(Color.Black);
    g.DrawLine(pen1,point.X,point.Y,e.X,e.Y);//图形画在PictureBox表面
    bitG.DrawLine(pen1,point.X,point.Y,e.X,e.Y);//图形画在位图对象bits中
    EndPoint.X=e.X;
    EndPoint.Y=e.Y;//下次绘制画线段开始点
    }
    }
    private void pictureBox1_MouseUp(object sender,
    System.Windows.Forms.MouseEventArgs e)
    { mark=false;
    pictureBox1.Image=bits;//保存了所画的图形
    }
    (5) 运行,在PictureBox控件拖动鼠标可以画线。最小化后再最大化后,图形不消失。
    5.11.5 存取位图文件
    例子e5_11_5:为上例增加存取位图文件功能。
    (6) 把Mainmenu控件放到主窗体中。增加菜单:文件,属性Name=menuItemFile。为文件菜单增加菜单项:新建、打开、另存为、退出,属性Name分别为menuItemNew、menuItemOpen、menuItemSaveAs、menuItemExit。
    (7) 为主窗体菜单项新文件增加单击事件函数如下:
    private void menuItemNew_Click(object sender, System.EventArgs e)
    { bitG.Clear(Color.White);//用白色清空位图对象bitG
    pictureBox1.Image=bits;//pictureBox1显示用白色清空位图对象bitG
    }
    (8) 把OpenFileDialog控件放到窗体中。为主窗体菜单项打开文件增加单击事件函数如下:
    private void menuItemOpen_Click(object sender,System.EventArgs e)
    { if(openFileDialog1.ShowDialog(this)==DialogResult.OK)
    { bits.Dispose();//撤销bitG所引用的对象
    bits=new Bitmap(openFileDialog1.FileName);//建立指定文件的新位图对象
    bitG=Graphics.FromImage(bits);//得到位图对象使用的Graphics类对象
    pictureBox1.Image=bits;
    }
    }
    (9) 把SaveFileDialog控件放到子窗体中。为主窗体菜单项另存为增加单击事件函数如下:
    private void menuItemSaveAs_Click(object sender,System.EventArgs e)
    { if(saveFileDialog1.ShowDialog(this)==DialogResult.OK)
    { string s=saveFileDialog1.FileName+".bmp";
    bits.Save(s,System.Drawing.Imaging.ImageFormat.Bmp);
    }
    }//也可以存为其它格式,例如:Jpg,Gif等。请读者试一下。
    (10) 为主窗体菜单项退出增加单击事件函数如下:
    private void menuItemExit_Click(object sender, System.EventArgs e)
    { Close();}
    (11) 运行,在PictureBox控件拖动鼠标可以画线。存所画的图形到文件,再重新读出该文件,看是否正常运行。
    5.11.6 用拖动鼠标方法画椭圆或圆
    例5_11_6:画笔程序中,拖动鼠标方法画椭圆或圆,拖动鼠标时显示椭圆或圆的轮廓,鼠标抬起时,按指定条件画椭圆或圆。如果图形仅画在图形框(PictureBox控件)上,而不保存到其属性Image引用的位图对象中,当调用图形框的Invalidate()方法,图形框响应Paint事件,用图形框属性Image引用的位图对象恢复图像,将擦除仅画在图形框上的图形。拖动鼠标方法画椭圆或圆时,仅将椭圆或圆画在PictureBox上,在鼠标拖动到下一个位置,用图形框的Invalidate()方法将前一位置所画的图形擦除。实现步骤如下:
    (1) 新建项目。增加5个私有变量:private bool mark=false;private Point StartPoint; private Bitmap bits;private Graphics bitG;private Point EndPoint;
    (2) 放PictureBox控件到子窗体。修改属性Dock=Fill。
    (3) 在构造函数中增加语句:
    //bits用来保存pictureBox1中位图图像
    bits=new Bitmap(pictureBox1.Width,pictureBox1.Height);
    bitG=Graphics.FromImage(bits);
    bitG.Clear(Color.White);
    pictureBox1.Image=bits;
    (4) 在Form1类中增加MakeRectangle方法返回由参数指定的两个点定义的矩形。方法如下:
    private Rectangle MakeRectangle(Point p1,Point p2)
    { int top,left,bottom,right;
    top=p1.Y<=p2.Y? p1.Y:p2.Y;//计算矩形左上角点的y坐标
    left=p1.X<=p2.X? p1.X:p2.X;//计算矩形左上角点的x坐标
    bottom=p1.Y>p2.Y? p1.Y:p2.Y;//计算矩形右下角点的y坐标
    right=p1.X>p2.X? p1.X:p2.X;//计算矩形右下角点的x坐标
    return(new Rectangle(left,top,right-left,bottom-top));//返回矩形
    }
    (5) 为PictureBox事件OnMouseDown、OnMouseUp、OnMouseMove增加事件处理函数如下:
    private void pictureBox1_MouseDown(object sender,//鼠标按下事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { if(e.Button==MouseButtons.Left)
    { StartPoint.X=e.X;
    StartPoint.Y=e.Y;
    EndPoint.X=e.X;
    EndPoint.Y=e.Y;
    mark=true;
    }
    }
    private void pictureBox1_MouseMove(object sender,//鼠标移动事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { if(mark)
    { Rectangle r1=MakeRectangle(StartPoint,EndPoint);//计算重画区域
    r1.Height+=2;
    r1.Width+=2;//区域增大些
    pictureBox1.Invalidate(r1);//擦除上次鼠标移动时画的图形,r1为擦除区域
    pictureBox1.Update();//立即重画,即擦除
    Graphics g=pictureBox1.CreateGraphics();
    Pen pen1=new Pen(Color.Black);
    EndPoint.X=e.X;
    EndPoint.Y=e.Y;
    r1=MakeRectangle(StartPoint,EndPoint);//计算椭圆新位置
    g.DrawEllipse(pen1,r1);//在新位置画椭圆
    }
    }
    private void pictureBox1_MouseUp(object sender,//鼠标抬起事件处理函数
    System.Windows.Forms.MouseEventArgs e)
    { Pen pen1=new Pen(Color.Black);
    EndPoint.X=e.X;
    EndPoint.Y=e.Y;
    Rectangle r1=MakeRectangle(StartPoint,EndPoint);
    bitG.DrawEllipse(pen1,r1);
    mark=false;
    pictureBox1.Image=bits;
    }
    (6) 运行,在PictureBox控件中拖动鼠标可以画圆或椭圆。
    5.12 图像剪贴板功能
    Windows中的许多程序都支持剪贴板功能。通过剪贴板可以完成显示数据的剪贴(Cut),复制(Copy),粘贴(Paste)等功能。剪贴板可以理解为一块存储数据的公共区域,用户可以用菜单项复制(Copy)或剪贴(Cut)把数据放入到剪贴板中,当本任务或其它任务要用剪贴板中的数据时,可以用菜单项粘贴(Paste)从剪贴板中把数据取出。存入剪贴板中的数据,可以是字符,位图,或者其它格式数据。在图形模式下使用剪贴板包括如下动作:选定剪贴区域、剪贴(Cut)、复制(Copy)、粘贴(Paste)等。使过画图程序的读者都知道,在使用剪贴和复制前,必须首先选定剪贴或复制区域,首先按一个按钮,通知程序要选定剪贴或复制区域,然后在要选定区域的左上角按下鼠标左键,拖动鼠标画出一个矩形,抬起鼠标后显示一个矩形既为要选定剪贴或复制区域。剪贴或复制后,矩形自动消失。下面详细介绍实现以上功能的方法。
    5.12.1 剪贴区域选定
    剪贴区域选定的方法和前边章节中拖动鼠标方法绘制椭圆或圆的方法基本一样,只是在这里绘制的是矩形,而且在鼠标抬起时,不把矩形存入PictureBox控件属性Image引用的位图对象中,仅仅记录矩形的位置。请读者自己实现此功能。
    5.12.2 剪贴板复制功能的实现
    假定已选定剪贴区域,例如为区域Rectangle(10,10,50,50),把此区域的图形或图像放到剪贴板中。具体实现步骤如下:
    (1) 新建项目。放PictureBox控件到窗体,修改属性Dock=Fill。属性Name=pictureBox1,修改属性Image,使其显示一幅图。
    (2) 把Mainmenu控件放到主窗体中。增加顶级菜单项:编辑,属性Name=menuItemEdit。为编辑弹出菜单增加菜单项:复制、剪贴、粘贴。属性Name分别为menuItemCopy、menuItemCut、menuItemPaste。
    (3) 为窗体菜单项复制增加单击事件函数如下:
    private void menuItemCopy_Click(object sender, System.EventArgs e)
    { Bitmap myBitmap=new Bitmap(pictureBox1.Image);
    Rectangle cloneRect=new Rectangle(10,10,50,50);
    System.Drawing.Imaging.PixelFormat format=myBitmap.PixelFormat;
    Bitmap cloneBitmap=myBitmap.Clone(cloneRect,format);
    Clipboard.SetDataObject(cloneBitmap);
    }
    (4) 运行,选中复制菜单项,复制图形到剪贴板。打开画图程序,选中画图程序粘贴菜单项,可以看到被复制的图形能正确粘贴到画图程序中。
    5.12.3 剪贴板剪贴功能的实现
    (5) 剪贴是先复制,再把选中区域图形清除,菜单项剪贴单击事件处理函数如下:
    private void menuItemCut_Click(object sender,System.EventArgs e)
    { menuItemCopy_Click(sender,e);//调用复制菜单项单击事件处理函数
    Bitmap bits=new Bitmap(50,50);//建立位图对象,宽和高为选中区域大小
    Graphics g=Graphics.FromImage(bits);//得到位图对象的Graphics类的对象
    g.Clear(Color.White);//用白色清除位图对象中的图像
    Bitmap myBitmap=new Bitmap(pictureBox1.Image);
    g=Graphics.FromImage(myBitmap);
    g.DrawImage(bits,10,10,50,50);
    pictureBox1.Image=myBitmap;//位图对象在pictureBox1中显示,即清除
    }
    (6) 运行,选中剪贴菜单项,拷贝图形到剪贴板,原位置图形被清空为白色,最小化后再最大化,图形不变。打开画图程序,选中画图程序粘贴菜单项,可以看到被拷贝的图形能正确粘贴到画图程序中。
    5.12.4 剪贴板粘贴功能的实现
    (7) 为窗体菜单项粘贴增加单击事件函数如下:
    private void menuItemPaste_Click(object sender, System.EventArgs e)
    { IDataObject iData=Clipboard.GetDataObject();//得到剪贴板对象
    if(iData.GetDataPresent(DataFormats.Bitmap))//判断剪贴板有无位图对象
    { Bitmap bits=(Bitmap)iData.GetData(DataFormats.Bitmap);//得到剪贴板位图
    Bitmap myBitmap=new Bitmap(pictureBox1.Image);
    Graphics g=Graphics.FromImage(myBitmap);
    g.DrawImage(bits,30,30);
    pictureBox1.Image=myBitmap;//位图对象在pictureBox1中显示
    }
    }
    (8) 运行画图程序,选中拷贝菜单项,拷贝图形到剪贴板。运行自己编制的程序,选中粘贴菜单项,可以看到画图程序中被拷贝的图形能正确粘贴到自己编制的程序中。
    (9) 画图程序粘贴后,能用鼠标移动粘贴的图形,现实现此功能。放PictureBox控件到窗体,属性Name=pictureBox2,属性Visable=false。这里把粘贴后的图形放到PictureBox2中,使其可以移动。为Form1类增加变量:bool mark=false;int x=0,y=0;为pictureBox2控件的事件OnMouseDown,OnMouseUp,OnMouseMove增加事件函数如下:
    private void pictureBox2_MouseDown(object sender,
    System.Windows.Forms.MouseEventArgs e)
    { mark=true;
    x=e.X;
    y=e.Y;
    }
    private void pictureBox2_MouseMove(object sender,
    System.Windows.Forms.MouseEventArgs e)
    { if(mark)
    { int x1,y1;
    x1=e.X-x;
    y1=e.Y-y;
    pictureBox1.Invalidate();//擦除上次鼠标移动时画的图形
    pictureBox1.Update();//立即重画,即擦除
    pictureBox2.Left+=x1;
    pictureBox2.Top+=y1;
    x=e.X;//原来没有此2句
    y=e.Y;
    }
    }
    private void pictureBox2_MouseUp(object sender,
    System.Windows.Forms.MouseEventArgs e)
    { mark=false;}
    (10) 修改窗体菜单项粘贴单击事件函数如下:
    private void menuItemPaste_Click(object sender,System.EventArgs e)
    { IDataObject iData=Clipboard.GetDataObject();
    if(iData.GetDataPresent(DataFormats.Bitmap))
    { Bitmap bit=(Bitmap)iData.GetData(DataFormats.Bitmap);
    pictureBox2.Width=bit.Width;//阴影为修改部分
    pictureBox2.Height=bit.Height;
    pictureBox2.Image=bit;
    pictureBox2.Top=pictureBox1.Top;
    pictureBox2.Left=pictureBox1.Left;
    pictureBox2.Parent=pictureBox1;
    pictureBox2.Visible=true;
    }
    }
    (11) 在pictureBox1控件任意位置单击鼠标,表示已将粘贴图像拖到指定位置,需将粘贴图像粘贴到pictureBox1控件。为pictureBox1控件的事件OnMouseDown增加事件函数如下:
    private void pictureBox1_MouseDown(object sender,
    System.Windows.Forms.MouseEventArgs e)
    { if(pictureBox2.Image!=null&&pictureBox2.Visible)
    { Bitmap bits=new Bitmap(pictureBox2.Image);
    Bitmap myBitmap = new Bitmap(pictureBox1.Image);
    Graphics g=Graphics.FromImage(myBitmap);
    g.DrawImage(bits,pictureBox2.Left,pictureBox2.Top);
    pictureBox1.Image=myBitmap;//位图对象在pictureBox1中显示
    pictureBox2.Visible=false;
    }
    }
    (12) 运行画图程序,选中拷贝菜单项,拷贝图形到剪贴板。运行自己编制的程序,选中粘贴菜单项,可以看到画图程序中被拷贝的图形能正确粘贴到自己编制的程序中。拖动被拷贝的图形,使其运动到指定位置,在pictureBox2外,单击鼠标右键,图形固定到指定位置。
    5.13 图像的处理
    本节介绍图像的处理的最基础知识,要想深入了解这方面的知识,还要读这方面的专著。
    5.13.1 图像的分辨力
    例子e5.13.1:将原图形的分辨率降低16倍,其方法是将原图形分成4*4的图形块,这16个点的颜色都置成这16个点中某点的颜色,例如4*4的图形块左上角的颜色。
    (1) 新建项目。放两个PictureBox控件到窗体,属性Name分别为pictureBox1,pictureBox2,修改pictureBox1属性Image,使其显示一幅图。
    (2) 放Button控件到窗体,为其增加事件处理函数如下:
    private void button1_Click(object sender,System.EventArgs e)
    { Color c;
    int i,j,size,k1,k2,xres,yres;
    xres=pictureBox1.Image.Width;//pictureBox1显示的图像的宽
    yres=pictureBox1.Image.Height;//pictureBox1显示的图像的高
    size=4;
    pictureBox2.Width=xres;//令pictureBox2和pictureBox1同宽,同高。
    pictureBox2.Height=yres;
    Bitmap box1=new Bitmap(pictureBox1.Image);
    Bitmap box2=new Bitmap(xres,yres);
    for(i=0;i<=xres-1;i+=size)
    { for(j=0;j<=yres-1;j+=size)
    { c=box1.GetPixel(i,j);
    for(k1=0;k1<=size-1;k1++)
    { for(k2=0;k2<=size-1;k2++)
    box2.SetPixel(i+k1,j+k2,c);
    }
    }
    }
    pictureBox2.Image=box2;
    }
    (3) 运行,单击按钮,在PictureBox2中可以看到分别率低的图形。
    5.13.2 彩色图像变换为灰度图像
    例子e5.13.2:本例把彩色图像变换为灰度图像。其方法是将原彩色图形每一个点的颜色取出,求出红色、绿色、蓝色分量的平均值,即(红色+绿色+蓝色)/3,作为这个点的红色、绿色、蓝色分量,这样就把彩色图像变成了灰度图像。具体步骤如下:
    (1) 新建项目。放两个PictureBox控件到窗体,属性Name分别为pictureBox1,pictureBox2,修改pictureBox1属性Image,使其显示一幅图。
    (2) 放Button控件到窗体,为其增加事件函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { Color c;
    int i,j,xres,yres,r,g,b;
    xres=pictureBox1.Image.Width;
    yres=pictureBox1.Image.Height;
    pictureBox2.Width=xres;
    pictureBox2.Height=yres;
    Bitmap box1=new Bitmap(pictureBox1.Image);
    Bitmap box2=new Bitmap(xres,yres);
    for(i=0;i<xres;i++)
    { for(j=0;j<yres;j++)
    { c=box1.GetPixel(i,j);
    r=c.R;
    g=c.G;
    b=c.B;
    r=(r+g+b)/3;
    c=Color.FromArgb(r,r,r);
    box2.SetPixel(i,j,c);
    }
    }
    pictureBox2.Image=box2;
    }
    (3) 运行,单击按钮,在PictureBox2中可以看到黑白图形。
    5.13.3 灰度图像处理
    例子e5.13.3:将一幅灰度图像变换为另一幅灰度图像,两幅灰度图形的灰度满足如下关系:设图1和图2的灰度分别为d1和d2,如d1<85,d2=0;如85<=d1<=170,d2=(d1-85)*3;如d1>170,d2=255。变换的效果是增强了对比度。具体步骤如下:
    (1) 新建项目。放两个PictureBox控件到窗体,属性Name分别为pictureBox1,pictureBox2,修改pictureBox1属性Image,使其显示一黑白幅图。
    (2) 放Button控件到窗体,为其增加事件函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { Color c;
    int i,j,xres,yres,m;
    xres=pictureBox1.Image.Width;
    yres=pictureBox1.Image.Height;
    pictureBox2.Width=xres;
    pictureBox2.Height=yres;
    Bitmap box1=new Bitmap(pictureBox1.Image);
    Bitmap box2=new Bitmap(xres,yres);
    int[] lut=new int[256];
    for(i=0;i<85;i++)
    lut[i]=0;
    for(i=85;i<=170;i++)
    lut[i]=(i-85)*3;
    for(i=171;i<256;i++)
    lut[i]=255;
    for(i=0;i<xres;i++)
    { for(j=0;j<yres;j++)
    { c=box1.GetPixel(i,j);
    m=lut[c.R];
    c=Color.FromArgb(m,m,m);
    box2.SetPixel(i,j,c);
    }
    }
    pictureBox2.Image=box2;
    }
    (3) 运行,单击按钮,在PictureBox2中可以看到对比度增强的黑白图形。
    5.13.4 动画
    学习了以上知识,制作一些简单动画是比较容易的。例如,如果一段动画,要求一个动画小人从窗体左侧走到右侧,如何实现呢?首先,为了看到人在走动,应该有3个动作:右脚在前,左脚在后;两脚并排;左脚在前,右脚在后。因此应制作3幅图画,表示这三个动作。每当转换一幅图画,图画应在X轴方向右移一步的距离。将3幅图画放到3个PictureBox控件中,用定时器产生中断,中断处理程序负责使其中一幅图画显示,其余两幅不显示,同时,修改PictureBox控件属性Left,使其在正确的位置上。这样就可以看到人的走动了。请读者自己完成这段动画。
    习题
    (1) 使用PictureBox控件显示图像,修改属性SizeMode为不同值,例如pictureBox1.SizeMode=PictureBoxSizeMode.StretchImage看一下效果。
    (2) 实现画图程序的橡皮功能。
    (3) 有时为了很快找到一幅图像,把很多图像都压缩后在窗体中并排显示,如希望更仔细的查看某幅图像,单击这幅压缩图像,放大这幅图像。请实现此功能。
    (4) 实现设定剪贴板剪贴区域为矩形的功能。
    (5) 实现设定剪贴板剪贴区域为任意封闭曲线的功能。(提示:使用GraphicsPath类)
    (6) 如何将PictureBox控件显示图像存为其它格式文件,例如:Jpg,Gif等。
    (7) 完成5.11.4所要求的动画,并能走到窗体右边界后,从右向左走回来,到左边界后,再向右走回去。如果有背景,如何处理。
    (8) 制作自己的画图程序,看一看能完成那些功能。
    (9) 有些时候,为突出图形的分界,例如医院的X片时黑白的,为了使医生能更清楚的看到肿瘤,将黑白图形变为彩色的图形,在分界两侧用不同颜色表示,这种方法叫伪彩色。实现的原理,就是在黑白图像灰度变化很大处,认为是边界。试一下,能否实现黑白图像的伪彩色。


    第六章 文件和流
    编程语言在如何处理输入/输出问题方面已经经过了很多变革。早期语言,例如Basic语言,使用I/O语句。后来的语言,例如C语言,使用标准的I/O库(stdio.h)。在C++和Java语言中,引入了抽象的概念:流。流的概念不仅可用于文件系统,也可用于网络。但在C++和Java语言中流的概念比较复杂。C#语言也采用了流的概念,但是使用起来要简单的多。本章介绍C#语言中,如何处理目录和文件夹,如何处理文件,如何使用流的概念读写文件。
    6.1 用流读写文件
    C#把每个文件都看成是顺序的字节流,用抽象类Stream代表一个流,可以从Stream类派生出许多派生类,例如FileStream类,负责字节的读写,BinaryRead类和BinaryWrite类负责读写基本数据类型,如bool、String、int16、int等等,TextReader类和TextWriter类负责文本的读写。本节介绍这些类的用法。
    6.1.1 用FileStream类读写字节
    写字节代码段如下:
    byte[] data=new byte[10];
    For(int i=0;i<10;i++)
    data[i]=(byte)i;
    System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate);
    fs.Write(data,0,10);
    读字节代码段如下:
    byte[] data=new byte[10];
    System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate);
    fs.Seek(-5,SeekOrigin.End);
    int n=fs.Read(data,0,10);//n为所读文件字节数
    6.1.2 用BinaryReader和BinaryWriter类读写基本数据类型
    C#中除了字节类型以外,还有许多其它基本数据类型,例如,int、bool、float等等,读写这些基本数据类型需要使用BinaryReader和BinaryWriter类。写int类型数据代码段如下:
    System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate);
    System.IO.BinaryWrite w=new System.IO. BinaryWrite(fs);
    For(int i=0;i<10;i++)
    w.Write(i);
    w.Close();
    读int类型数据代码段如下:
    int[] data=new int[10];
    System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate);
    System.IO.BinaryReader r=new System.IO. BinaryReader(fs);
    For(int i=0;i<10;i++)
    data[i]=r.ReadInt();
    r.Close();
    6.1.3 用StreamReader和StreamWriter类读写字符串
    读写字符串可以用StreamReader和StreamWriter类。写字符串类型数据代码段如下:
    System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate);
    System.IO.StreamWrite w=new System.IO.StreamWrite(fs);
    w.Write(100);
    w.Write("100个");
    w.Write("End of file");
    w.Close();
    读字符串代码段如下:
    String[] data=new String[3];
    System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate);
    System.IO.StreamReader r=new System.IO.StreamReader(fs);
    For(int i=0;i<3;i++)
    data[i]=r.ReadLine();
    r.Close();
    6.2 File类和FileInfo类
    C#语言中通过File和FileInfo类来创建、复制、删除、移动和打开文件。在File类中提供了一些静态方法,使用这些方法可以完成以上功能,但File类不能建立对象。FileInfo类使用方法和File类基本相同,但FileInfo类能建立对象。在使用这两个类时需要引用System.IO命名空间。这里重点介绍File类的使用方法。
    6.2.1 File类常用的方法
     AppendText:返回StreamWrite,向指定文件添加数据;如文件不存在,就创建该文件。
     Copy:复制指定文件到新文件夹。
     Create:按指定路径建立新文件
     Delete:删除指定文件。
     Exists:检查指定路径的文件是否存在,存在,返回true。
     GetAttributes:获取指定文件的属性。
     GetCreationTime:返回指定文件或文件夹的创建日期和时间。
     GetLastAccessTime:返回上次访问指定文件或文件夹的创建日期和时间。
     GetLastWriteTime:返回上次写入指定文件或文件夹的创建日期和时间。
     Move:移动指定文件到新文件夹。
     Open:返回指定文件相关的FileStream,并提供指定的读/写许可。
     OpenRead:返回指定文件相关的只读FileStream。
     OpenWrite:返回指定文件相关的读/写FileStream。
     SetAttributes:设置指定文件的属性。
     SetCretionTime:设置指定文件的创建日期和时间。
     SetLastAccessTime:设置上次访问指定文件的日期和时间。
     SetLastWriteTime:设置上次写入指定文件的日期和时间。
    下面通过程序实例来介绍其主要方法:
    6.2.2 文件打开方法:File.Open
    该方法的声明如下:public static FileStream Open(string path, FileMode mode)。下面的代码打开存放在c:\Example目录下名称为e1.txt文件,并在该文件中写入hello。
    FileStream TextFile=File.Open(@"c:\ Example\e1.txt",FileMode.Append);
    byte [] Info={(byte)'h',(byte)'e',(byte)'l',(byte)'l',(byte)'o'};
    TextFile.Write(Info,0,Info.Length);
    TextFile.Close();
    6.2.3 文件创建方法:File.Create
    该方法的声明如下:public static FileStream Create(string path)。下面的代码演示如何在c:\Example下创建名为e1.txt的文件。
    FileStream NewText=File.Create(@"c:\Example\e1.txt");
    NewText.Close();
    6.2.4 文件删除方法:File.Delete
    该方法声明如下:public static void Delete(string path)。下面的代码演示如何删除c:\Example目录下的e1.txt文件。
    File.Delete(@"c:\Example\e1.txt");
    6.2.5 文件复制方法:File.Copy
    该方法声明如下:
    public static void Copy(string sourceFileName,string destFileName,bool overwrite);
    下面的代码将c:\Example\e1.txt复制到c:\Example\e2.txt。由于Cope方法的OverWrite参数设为true,所以如果e2.txt文件已存在的话,将会被复制过去的文件所覆盖。
    File.Copy(@"c:\Example\e1.txt",@"c:\Example\e2.txt",true);
    6.2.6 文件移动方法:File.Move
    该方法声明如下:public static void Move(string sourceFileName,string destFileName);下面的代码可以将c:\Example下的e1.txt文件移动到c盘根目录下。注意:只能在同一个逻辑盘下进行文件转移。如果试图将c盘下的文件转移到d盘,将发生错误。
    File.Move(@"c:\Example\BackUp.txt",@"c:\BackUp.txt");
    6.2.7 设置文件属性方法:File.SetAttributes
    方法声明如下:public static void SetAttributes(string path,FileAttributes fileAttributes);下面的代码可以设置文件c:\Example\e1.txt的属性为只读、隐藏。
    File.SetAttributes(@"c:\Example\e1.txt",
    FileAttributes.ReadOnly|FileAttributes.Hidden);
    文件除了常用的只读和隐藏属性外,还有Archive(文件存档状态),System(系统文件),Temporary(临时文件)等。关于文件属性的详细情况请参看MSDN中FileAttributes的描述。
    6.2.8 判断文件是否存在的方法:File.Exist
    该方法声明如下:public static bool Exists(string path);下面的代码判断是否存在c:\Example\e1.txt文件。
    if(File.Exists(@"c:\Example\e1.txt"))//判断文件是否存在
    {…}//处理代码
    6.2.9 得到文件的属性
    用下面的代码可以得到文件的属性,例如文件创建时间、最近访问时间、最近修改时间等等。
    FileInfo fileInfo=new FileInfo(“file1.txt”);
    string s=fileInfo.FullName+”文件长度=”+fileInfo.Length+”,建立时间=”+ fileInfo.CreationTime+”;
    也可用如下代码:
    string s=”建立时间=”+File.File.GetCreationTime(“file1.txt”)+”最后修改时间=”+ File.GetLastWriteTime(“file1.txt”)+”访问时间=”+File.GetLastAccessTime(“file1.txt”);
    6.3 Directory类和DirectoryInfo类
    C#语言中通过Directory类来创建、复制、删除、移动文件夹。在Directory类中提供了一些静态方法,使用这些方法可以完成以上功能。但Directory类不能建立对象。DirectoryInfo类使用方法和Directory类基本相同,但DirectoryInfo类能建立对象。在使用这两个类时需要引用System.IO命名空间。这里重点介绍Directory类的使用方法。
    6.3.1 Directory类常用的方法如下:
     CreateDirectory:按指定路径创建所有文件夹和子文件夹。
     Delete:删除指定文件夹。
     Exists:检查指定路径的文件夹是否存在,存在,返回true。
     GetCreationTime:返回指定文件或文件夹的创建日期和时间。
     GetCurrentDirectory:获取应用程序的当前工作文件夹。
     GetDirectories:获取指定文件夹中子文件夹的名称。
     GetDirectoryRoot:返回指定路径的卷信息、根信息或两者同时返回。
     GetFiles:返回指定文件夹中子文件的名称。
     GetFileSystemEntries:返回指定文件夹中所有文件和子文件的名称。
     GetLastAccessTime:返回上次访问指定文件或文件夹的创建日期和时间。
     GetLastWriteTime:返回上次写入指定文件或文件夹的创建日期和时间。
     GetLogicalDrives:检索计算机中的所有驱动器,例如A:、C:等等。
     GetParent:获取指定路径的父文件夹,包括绝对路径和相对路径。
     Move:将指定文件或文件夹及其内容移动到新位置。
     SetCreationTime:设置指定文件或文件夹的创建日期和时间。
     SetCurrentDirectory:将应用程序的当前工作文件夹设置指定文件夹。
     SetLastAccessTime:设置上次访问指定文件或文件夹的日期和时间。
     SetLastWriteTime:设置上次写入指定文件夹的日期和时间。
    6.3.2 目录创建方法:Directory.CreateDirectory
    方法声明如下:public static DirectoryInfo CreateDirectory(string path);下面的代码演示在c:\Dir1文件夹下创建名为Dir2子文件夹。
    Directory.CreateDirectory(@"c:\Dir1\Dir2");
    6.3.3 目录属性设置方法:DirectoryInfo.Atttributes
    下面的代码设置c:\Dir1\Dir2目录为只读、隐藏。与文件属性相同,目录属性也是使用FileAttributes来进行设置的。
    DirectoryInfo DirInfo=new DirectoryInfo(@"c:\Dir1\Dir2");
    DirInfo.Atttributes=FileAttributes.ReadOnly|FileAttributes.Hidden;
    6.3.4 目录删除方法:Directory.Delete
    该方法声明如下:public static void Delete(string path,bool recursive);下面的代码可以将c:\Dir1\Dir2目录删除。Delete方法的第二个参数为bool类型,它可以决定是否删除非空目录。如果该参数值为true,将删除整个目录,即使该目录下有文件或子目录;若为false,则仅当目录为空时才可删除。
    Directory.Delete(@"c:\Dir1\Dir2",true);
    6.3.5 目录移动方法:Directory.Move
    该方法声明如下:public static void Move(string sourceDirName,string destDirName);下面的代码将目录c:\Dir1\Dir2移动到c:\Dir3\Dir4。
    File.Move(@"c:\Dir1\Dir2",@"c:\Dir3\Dir4");}
    6.3.6 获取当前目录下所有子目录:Directory.GetDirectories
    该方法声明如下:public static string[] GetDirectories(string path;);下面的代码读出c:\Dir1\目录下的所有子目录,并将其存储到字符串数组中。
    string [] Directorys;
    Directorys = Directory. GetDirectories (@"c:\Dir1");
    获得所有逻辑盘符:
    string[] AllDrivers=Directory.GetLogicalDrives();
    6.3.7 获取当前目录下的所有文件方法:Directory.GetFiles
    该方法声明如下:public static string[] GetFiles(string path;);下面的代码读出c:\Dir1\目录下的所有文件,并将其存储到字符串数组中。
    string [] Files;
    Files = Directory. GetFiles (@"c:\Dir1",);
    6.3.8 判断目录是否存在方法:Directory.Exist
    该方法声明如下:public static bool Exists(string path;);下面的代码判断是否存在c:\Dir1\Dir2目录。
    if(File.Exists(@"c:\Dir1\Dir2"))//判断目录是否存在
    {…}//处理语句
    注意:路径有3种方式,当前目录下的相对路径、当前工作盘的相对路径、绝对路径。以C:\dir1\dir2为例(假定当前工作目录为C:\Tmp)。“dir2”,“\dir1\dir2”,“C:\dir1\dir2”都表示C: \dir1\dir2。另外,在C#中 “\”是特殊字符,要表示它的话需要使用“\\”。由于这种写法不方便,C#语言提供了@对其简化。只要在字符串前加上@即可直接使用“\”。所以上面的路径在C#中应该表示为”dir2”,@”\dir1\dir2”,@”C:\dir1\dir2”。
    6.4 例子:查找文件
    6.4.1 Panel和ListView控件

    6.4.2 在指定文件夹中查找文件
    Windows操作系统提供了一个查找文件的程序,可以查找指定文件夹中的指定文件,本例也实现了同样的功能。具体实现步骤如下:
    (1) 新建项目。
    (2) 放Panel控件到窗体,属性Dock=Left。Panel控件可以把窗体分割为多个部分,这里将窗体分割为左右两部分。
    (3) 在Panel控件中增加两个Label控件,属性Text分别为”要搜索的文件或文件夹”和”搜索范围”。
    (4) 在Panel控件中增加一个TextBox控件,属性Name=textBox1,属性Text为空,用来输入要搜索的文件或文件夹。
    (5) 在Panel控件中增加一个TextBox控件,属性Name=textBox2,属性Text为空,用来输入搜索范围。在其后增加一个Button控件,属性Name=Broswer,属性Text=”浏览”。
    (6) 为”浏览”按钮增加事件函数如下:
    private void Broswer_Click(object sender, System.EventArgs e)
    {
    OpenFileDialog dlg=new OpenFileDialog();
    if(dlg.ShowDialog()==DialogResult.OK)
    {
    textBox2.Text=dlg.FileName;
    }
    }
    (7) 在Panel控件中增加一个Button控件,属性Name分别为Start和Stop,属性Text分别为”开始搜索”和”停止搜索”。
    (8) 放分割器控件Splitter到窗体,属性Dock=Left。
    (9) 在分割器控件右侧放置视图控件ListView,属性Dock=Right,属性SmallImgeList =”imageList”,属性View=”Detail”。点击属性Column右侧标题为…的按钮,在弹出的ColumnHeader编辑对话框中添加4个列头,属性Name分别为:FileName、FileDirectory、FileSize和LastWriteTime,属性Text分别为:名称、所在文件夹、大小和修改时间。
    (10) 为窗体增加一个方法:FindFiles(DirectoryInfo dir,string FileName),该方法是在第一个参数指定的文件夹中查找第二个参数指定的所有文件。在一个文夹中可能还有子文件夹,子文件夹中可能还有子文件夹,因此要在第一个参数指定的文件夹中和其子文件夹中查找第二个参数指定的所有文件。为了实现能够查找所有文件夹中的同名文件,采用递归调用方法,如果在一个文件夹中存在子文件夹,在一次调用函数自己,查找子文件夹中的文件。具体实现代码如下:
    void FindFiles(DirectoryInfo dir,string FileName)
    {
    FileInfo[] files=dir.GetFiles(FileName);//查找所有文件并在ListView中显示
    If(files.Length!=0)
    {
    foreach(FileInfo aFile in files)
    {
    ListViewItem lvi;
    lvi=new ListViewItem(aFile.Name,aFile.Directory.FullName,aFile.Length.ToString, aFile.LastWriteTime.ToShortDateString());
    lvi.ImageIndex=0;
    listView1.Items.Add(lvi);
    }
    }
    DirectoryInfo[] dirs=dir.GetDirectories();//查找子文件夹中的匹配文件
    If(dirs.Length!=0)
    {
    foreach(DirectoryInfo aDir in dirs)
    {
    FindFiles(aDir,FileName);
    }
    }
    }
    (11) 为”开始搜索”按钮增加事件函数如下:
    private void Start_Click(object sender, System.EventArgs e)
    {
    DirectoryInfo aDir=CreateDirectorie(comboBox1.Text);
    FindFiles(aDir,textBox1.Text);
    }
    (12) 为”停止搜索”按钮增加事件函数如下:
    private void Stop_Click(object sender, System.EventArgs e)
    {
    }
    (13) 编译、运行,
    6.5 例子:拆分和合并文件
    在将一个文件作为电子邮件的附件传送时,由于附件的大小有限制,可以将较大的文件分割为较小的多个文件,传送后再合并为一个文件,下边两个方法实现文件的拆分和合并。首先是拆分方法,参数1时要拆分的文件名,参数2是拆分后的文件名,文件名后边由拆分方法自动增加序号,参数3是被拆分后的文件大小。拆分方法定义如下:
    void SplitFile(string f1,string f2,int f2Size)
    {
    FileStream inFile=new FileStream(f1,FileMode.OpenOrCreate,FileAccess.Read);
    bool mark=true;
    int i=0;
    int n=0;
    byte[] buffer=new byte[f2Size];
    while(mark)
    {
    FileStream OutFile=new FileStream(f2+i.ToString+”.fsm”,
    FileMode.OpenOrCreate,FileAccess.Read);
    if((n=inFile.Read(buffer,0,f2Size))>0)
    {
    OutFile.Write(buffer,0,n);
    i++;
    OutFile.Close();
    }
    else
    {
    mark=false;
    }
    }
    inFile.Close();
    }
    合并文件方法,参数1时要合并的文件名,参数2是被拆分的文件名,文件名后边有序号,要将这些文件合并到一起,参数3是要合并的文件数。合并方法定义如下:
    void MergeFile(string f1,string f2,int f2Num)
    {
    FileStream OutFile=new FileStream(f1,FileMode.OpenOrCreate,FileAccess.Write);
    int n,l;
    for(int i=0;i<f2Num;i++)
    {
    FileStream InFile=new
    FileStream(f2+i.ToString+”.fsm”,FileMode.OpenOrCreate,FileAccess.Read);
    l=InFile.Length;
    byte[] buffer=new byte[l];
    n=inFile.Read(buffer,0,l);
    OutFile.Write(buffer,0,n);
    InFile.Close();
    }
    OutFile.Close();
    }
    习题:
    (1) 实现拆分和合并文件的完整程序。


    第七章 多线程程序设计
    如果在一个程序中,有多个工作要同时做,可以采用多线程。在Windows操作系统中可以运行多个程序,把一个运行的程序叫做一个进程。一个进程又可以有多个线程,每个线程轮流占用CPU的运算时间,Windows操作系统将时间分为许多个时间片,一个线程使用一个时间片后,操作系统将此线程挂起,将另一个线程唤醒,使其使用下一个时间片,操作系统不断的把线程挂起,唤醒,再挂起,再唤醒,如此反复,由于现在CPU的速度比较快,给人的感觉象是多个线程同时执行。Windows操作系统中有很多这样的例子,例如复制文件时,一方面在进行磁盘的读写操作,同时一张纸不停的从一个文件夹飘到另一个文件夹,这个飘的动作实际上是一段动画,两个动作是在不同线程中完成的,也就是说两个动作是同时完成的。又如Word程序中的拼写检查也是在另一个线程中完成的。每个进程最少有一个线程,叫主线程,是进程自动创建的,每进程可以创建多个线程。
    不同语言和操作系统对线程提供了不同支持,编写多线程应用程序的方法也不尽相同。例如,VB6没有提供对线程的支持,程序员不能处理自己的线程。VC++6.0开发人员必须充分理解Windows线程和处理模型的复杂性,同时拥有这种线程模型的强大功能。C++程序员可以创建出多线程程序,但必须学习掌握很多复杂的技巧,以确保线程在自己的控制之下。
    .NET Framework提供了一个完整而功能强大的线程模型,该模型允许编程人员精确控制线程中运行的内容,线程何时退出,以及它将访问多少数据等。所以,在.NET中,既提供了C++的强大功能,又具有VB6的简单性。
    7.1 线程类(Thread)的属性和方法
    线程类在命名空间System..Threading中定义的,因此如果要创建多线程,必须引入命名空间System..Threading。Thread类的常用方法如下:
     属性ThreadPriority:设置线程优先级,有5种优先级类别:(AboveNormal)稍高、(BelowNormal)稍低、Normal(中等,默认值)、Highest(最高)和Lowest(最低)。
     构造函数:new Thread(new ThreadStart(线程首先执行的方法名)),构造方法参数中指定的方法需要程序员自己定义,在这个方法中完成线程要完成的任务,退出该方法,线程结束。该方法必须为公有void类型的方法,不能有参数。
     方法Start():建立线程类对象后,线程并不能自动运行,用这个方法启动线程。
     方法IsAlive():判断线程对象是否存在,=true,存在。
     方法Abort():撤销线程对象。不能撤销一个已不存在的线程对象,因此在撤销一个线程对象前,必须用方法IsAlive()判断线程对象是否存在。
     方法Sleep():参数为毫秒,线程暂时停止参数指定的时间,允许其它线程运行。
     方法Suspend():线程挂起。如只是暂时停止线程的运行,可用此函数将线程挂起。必须用Resume()方法唤醒线程。
     方法Resume():恢复挂起线程。如希望继续运行挂起线程,可用此方法唤醒线程。需要注意的是,如果线程多次被挂起,调用一次Resume()方法就可以把线程唤醒。
    7.2 线程的创建
    例子:多线程程序设计,该程序包括一个子线程,在标签控件中显示子线程运行的时间。增加4个按钮,分别单击按钮,可以建立、挂起、恢复和停止线程。
    (1) 新建项目。在窗体中放置4个按钮和一个标签控件,属性Name分别为button1、button2、button3、button4和label1,按钮属性Text分别为新线程、挂起、恢复和撤销。button1属性Enabled=true,其余按钮的属性Enabled=false。
    (2) 在Form1.cs头部增加语句:using System.Threading。
    (3) 为Form1类定义一个线程类变量:private Thread thread;
    (4) 为新线程按钮(button1)增加单击事件函数如下:
    thread= new Thread(new ThreadStart(fun);//生成线程类对象
    label1.Text=”0”;
    thread.Start();
    button1.Enabled=false;
    button2.Enabled=true;
    button3.Enabled=false;
    button4.Enabled=true;
    (5) 为挂起按钮(button2)增加单击事件函数如下:
    thread. Suspend();
    button1.Enabled=false;
    button2.Enabled=false;
    button3.Enabled=true;
    button4.Enabled=false;
    (6) 为恢复按钮(button3)增加单击事件函数如下:
    thread. Resume();
    button1.Enabled=false;
    button2.Enabled=true;
    button3.Enabled=false;
    button4.Enabled=true;
    (7) 为撤销按钮(button4)增加单击事件函数如下:
    if(thread.IsAlive())
    {
    thread.Abort();//撤销线程对象
    button1.Enabled=true;
    button2.Enabled=false;
    button3.Enabled=false;
    button4.Enabled=false;
    }
    (8) C#线程模型由于允许将任何一个原型为void类型的公有类成员方法(静态或非静态)作为线程方法,因此它实际上允许在任何一个类(不要求这个类是某个类的子类)中实现线程方法,而且同一个类中可以实现多个线程方法。为Form1类定义一个方法如下:
    public void fun()//在线程中执行的方法,必须为公有void类型方法,不能有参数。
    {
    while(true)//死循环,线程将一直运行
    {
    int x=Convert.ToInt(label1.Text);
    x++;
    label1.Text=Convert.ToString(x);
    thread.Sleep(1000);//休眠1秒钟,休眠一次,线程运行了1秒钟
    }
    }
    (9) 编译,运行,按新线程(Button1)按钮,新线程开始,计数器从0开始计数。按挂起(Button2)按钮,线程暂停,计数器也暂停。按恢复(Button3)按钮,线程重新启动,计数器也重新计数。按撤销(Button4)按钮,线程对象被撤销,线程对象不存在,计数器停止计数。
    7.3 建立线程类
    有时需要建立多个线程,每个线程要实现的功能基本相同,但有个别参数不同,例如,每个线程完成同样的任务,但控制的对象不同。线程构造函数参数指定的方法需要自己定义,在这个方法中完成一些任务,但该方法不能有参数,因此不能通过方法的参数传递不同设置。为解决这个问题,可以定义一个线程类。具体实现方法件下例。
    例子:建立两个线程,分别控制进度条(ProgressBar)控件,每个进度条的速度不一样。首先介绍进度条(ProgressBar)控件。
    7.3.1 进度条(ProgressBar)控件
    进度条(ProgressBar)控件经常用来显示一个任务的进度。有时,要在后台完成一个长时间的任务,例如一个软件的安装,如果没有任何提示,使用者可能分不清任务是在进行中,还是死机了,为了让用户知道安装正在进行,可以使用进度条控件显示一个安装进度。进度条控件常用的属性如下:
     属性Maximum:进度条所代表的整数最大值,等于此值,任务完成。默认值100。
     属性Minimum:进度条所代表的整数最小值,等于此值,任务开始。默认值0。
     属性Step:变化的步长,默认值为10。
     属性Value:进度条当前位置代表的值。修改该值,达到一个Step,进度增加一格。
    7.3.2 用线程控制进度条
    例子实现的具体实现步骤如下:
    (1) 新建项目。在Form1.cs头部增加语句:using System.Threading。
    (2) 在窗体中放置2个进度条(ProgressBar)控件和一个标签控件,属性Name分别为progressBar1、progressBar2和label1。Label1的属性Text=””。
    (3) 在文件Form1.cs的最后建立线程类如下:
    public class myThread
    {
    private int SleepTime;
    private ProgressBar progressBar;
    private Thread thread1;
    public myThread(int Time,ProgressBar p1)
    {
    SleepTime=Time;
    progressBar=p1;
    thread1=new Thread(new ThreadStart(Fun));
    Thread1.Start();
    }
    public void fun()
    {
    while(progressBar.Value!=100)
    {
    progressBar.Value+=1;
    thread1.Sleep(SleepTime);
    }
    if(label.Text==””)
    label1.Text=”第一个线程结束”;
    else
    label.Text+=”,第二个线程结束”;
    }
    }
    (4) 为Form1类增加变量:myThread myThread1,myThread2。
    (5) 为Form1类构造函数增加语句如下:
    myThread1=new myThread(100,progressBar1);
    myThread2=new myThread(200,progressBar2);
    (6) 编译,运行,可以看到两个进度条以不同的速度前进,当进度条被添满,线程停止。
    7.4 线程的优先级
    当一个程序被调入内存准备运行时,操作系统自动创建一个进程和一个主线程,并为进程指定基本优先级,进程基本优先级分为以下四种:
     IDLE_PROCESS_CLASS 系统空闲时才执行
     NORMAL_PROCESS_CLASS 系统默认进程优先级
     BELOW_NORMAL_PROCESS_CLASS 比系统默认进程优先级低一级
     ABOVE_NORMAL_PROCESS_CLASS 比系统默认进程优先级高一级
     HIGH_PROCESS_CLASS 高进程优先级
     REALTIME_PROCESS_CLASS 进程最高(实时)优先级
    操作系统一般分配用户应用程序进程为NORMAL_PROCESS_CLASS系统默认进程优先级。对于进程中的各个子线程,可以修改属性ThreadPriority来调整其的优先级,属性ThreadPriority可以取如下值:
     THREAD_PRIORITY_IDLE 系统空闲时才执行
     THREAD_PRIORITY_LOWEST 比NORMAL低2级
     THREAD_PRIORITY_LOWER 比NORMAL低1级
     THREAD_PRIORITY_NORMAL 系统默认线程优先级
     THREAD_PRIORITY_HIGHT 比NORMAL高1级
     THREAD_PRIORITY_HIGHTEST 比NORMAL高2级
     THREAD_PRIORITY_TIME_CRITICAL 线程最高(实时)优先级
    一个线程的优先权并不是越高越好,应考虑到整个进程中所有线程以及其他进程的情况做出最优选择。优先级相同的线程按照时间片轮流运行。优先级高的线程先运行,只有优先级高的线程停止、休眠或暂停时,低优先级线程才能运行。
    7.5 多个线程互斥
    多个线程同时修改同一个共享数据可能发生错误,例如,两个线程记录不同入口进入的人数,用一个变量实时显示总人数。每个线程都要对这个总人数变量执行加1操作,这个加1操作是一个高级语言语句,可能包含若干机器语言语句,例如,可能先从内存取数,加1,再存回内存。假如,当前人数为100,第一个线程运行,从内存取出总人数100,时间片时间到,第2个线程启动,执行加1操作,总人数变为101,第2个线程退出运行。第一个线程恢复运行,执行加1操作,存回内存,总数本应为102,实际为101,少计算了一个。为了防止此类错误,在一个线程操作这个总人数变量时,不允许其它线程对它进行操作,这叫线程的互斥。
    7.5.1 多个线程同时修改共享数据可能发生错误
    例子e7_5_1:下边的例子模拟多个线程同时修改同一个共享数据发生的错误。
    (1) 新建项目。在Form1.cs头部增加语句:using System.Threading。
    (2) 在窗体中放置一个标签控件,属性Name=label1。
    (3) 为Form1类定义2个线程类变量:Thread thread1,thread2。定义1个整形变量:int num=0。
    (4) 为Form1类构造函数增加语句如下:
    thread1= new Thread(new ThreadStart(Fun1);
    thread2= new Thread(new ThreadStart(Fun2);
    (5) 为Form1类定义Fun1()和Fun2()方法如下:
    public void Fun1()
    {
    int k,n;
    for(k=0;k<4;k++)
    {
    n=num;
    n++;
    thread1.Sleep(100);
    num=n;
    }
    label1.Text=Convert.ToString(num);
    }
    public void Fun2()
    {
    int k,n;
    for(k=0;k<4;k++)
    {
    n=num;
    n++;
    thread2.Sleep(200);
    num=n;
    }
    label1.Text=Convert.ToString(num);
    }
    (6) 编译,运行,标签控件应显示8,实际运行多次,显示的数要小于8。
    7.5.2 用LOCK语句实现互斥

    7.5.3 用Mutex类实现互斥

    7.5.4 用Monitor类实现互斥

    7.6 Monitor类

    7.7 线程的同步:生产者和消费者关系
    在生产者和消费者关系中,生产者线程产生数据,并把数据存到公共数据区,消费者线程使用数据,从公共数据区取出数据,并进行分析。很显然,如果公共数据区只能存一个数据,那么在消费者线程取出数据前,生产者线程不能放新数据到公共数据区,否则消费者线程将丢失数据。同样,只有在生产者线程把数据已经放到公共数据区,消费者线程才能取出数据,如果新数据未放到公共数据区,消费者线程不能取数据。这些就是所谓的生产者和消费者关系,这要求生产者线程和消费者线程同步。
    7.7.1 生产者线程和消费者线程不同步可能发生错误
    例子e7_7_1:下边的例子模拟生产者线程和消费者线程不同步可能发生错误。有一个公共变量,要求生产者线程顺序放1到4到这个公共变量中,每放一个变量,消费者线程取出这个数求和,最后把和显示出来,显然和应为10。如不采取同步措施,和的结果不正确。
    (1) 新建项目。在Form1.cs头部增加语句:using System.Threading。
    (2) 在窗体中放置一个标签控件,属性Name=label1。
    (3) 为Form1类定义2个线程类变量:Thread thread1,thread2。
    (4) 为Form1类定义2个整形变量:int sum=0,x=-1。
    (5) 为Form1类构造函数增加语句如下:
    thread1= new Thread(new ThreadStart(Fun1);
    thread2= new Thread(new ThreadStart(Fun2);
    (6) 为Form1类定义Fun1()和Fun2()方法如下:
    public void Fun1()//生产数据
    {
    int k,n;
    for(k=1;k<5;k++)
    {
    x=i;
    thread1.Sleep(200);
    }
    }
    public void Fun2()//消费数据
    {
    int k,n;
    for(k=0;k<4;k++)
    {
    sum+=x;
    thread2.Sleep(100);
    }
    label1.Text=Convert.ToString(sum);
    }
    (7) 编译,运行,标签控件应显示10,实际运行多次,显示的数要小于10。
    7.7.2 生产者线程和消费者线程同步的实现
    修改上例,为Form1类定义1个布尔变量:bool mark=false。其值为false,表示数据还未放到公共数据区(即x)中,生产者线程可以放数据到公共数据区中,由于没有数据,消费线程不能取数据,必须等待。mark=true,表示数据已未放到公共数据区(即x)中,消费线程还未取数据,生产者线程不能再放数据到公共数据区中,必须等待。由于有了数据,消费线程可以取数据。修改Fun1()如下:
    public void Fun1()//生产数据
    {
    int k,n;
    for(k=1;k<5;k++)
    {
    Monitor.Enter(this);
    If(mark)
    Monitor.Wait(this);//如果消费者数据未取走,生产者等待
    !mark;
    x=i;
    Monitor.Pulse(this);//激活消费者线程
    Monitor.Exit(this);
    }
    }
    修改Fun2()如下:
    public void Fun2()//消费数据
    {
    int k,n;
    for(k=0;k<4;k++)
    {
    Monitor.Enter(this);
    If(!mark)
    Monitor.Wait(this);//如果生产者未放数据,消费者等待
    !mark;
    sum+=x;
    Monitor.Pulse(this);
    Monitor.Exit(this);
    }
    label1.Text=Convert.ToString(sum);
    }
    编译,运行,标签控件应显示10。
    习题:
    (1) 修改例子e7_5,用线程类实现。


    第八章 ADO.NET与数据操作
    8.1 数据库基本概念
    数据库系统提供了一种将信息集合在一起的方法。数据库主要由三部分组成:数据库管理系统(DBMS),是针对所有应用的,例如ACCESS。数据库本身,按一定的结构组织在一起的相关数据。数据库应用程序,它是针对某一具体数据库应用编制的程序,用来获取,显示和更新数据库存储的数据,方便用户使用。这里讲的就是如何编写数据库应用程序。
    常见的数据库系统有:FoxPro,Access,Oracle,SQLserver,Sybase等。数据库管理系统主要有四种类型:文件管理,层次数据库,网状数据库和关系数据库。目前最流行,应用最广泛的是关系数据库,以上所列举的数据库系统都是关系数据库。关系数据库以行和列的形式来组织信息,一个关系数据库由若干表组成,一个表就是一组相关的数据按行排列,例如一个通讯录就是这样一个表,表中的每一列叫做一个字段,例如通讯录中的姓名,地址,电话都是字段。字段包括字段名及具体的数据,每个字段都有相应的描述信息,例如数据类型,数据宽度等。表中每一行称为一条记录。
    数据库可分为本地数据库和远程数据库,本地数据库一般不能通过网络访问,本地数据库往往和数据库应用程序在同一系统中,本地数据库也称为单层数据库。远程数据库通常位于远程计算机上,用户通过网络来访问远程数据库中的数据。远程数据库可以采用两层,三层和四层结构,两层结构一般采用C/S模式,即客户端和服务器模式。三层模式一般采用B/S模式,用户用浏览器访问WEB服务器,WEB服务器用CGI,ASP,PHP,JSP等技术访问数据库服务器,生成动态网页返回给用户。四层模式是在WEB服务器和数据库服务器中增加一个应用服务器。利用ADO.NET可以开发数据库应用程序。
    由于ADO.Net的使用,设计单层数据库或多层数据库应用程序使用的方法基本一致,极大地方便了程序设计,因此,这里讨论的内容也适用于后边的Web应用程序设计。
    8.2 设计连接和不连接数据库应用程序的基本步骤:
    设计一个数据库应用程序可以采用连接和不连接方式。所谓连接方式,是数据库应用程序运行期间,一直保持和数据库连接,数据库应用程序通过SQL语句直接对数据库操作,例如,查找记录、删除记录、修改记录。所谓不连接方式,是数据库应用程序把数据库中感兴趣的数据读入建立一个副本,数据库应用程序对副本进行操作,必要时将修改的副本存回数据库。设计一个不连接方式数据库应用程序一般包括以下基本步骤:
    (1) 建立数据库,包括若干个表,在表中添入数据。
    (2) 建立和数据库的连接。
    (3) 从数据库中取出感兴趣的数据存入数据集DataSet对象,包括指定表和表中满足条件的记录,DataSet对象被建立在内存中,可以包含若干表,可以认为是数据库在内存中的一个子集。然后断开和数据库的联接。
    (4) 用数据绑定的方法显示这个子集的数据,供用户浏览、查询、修改。
    (5) 把修改的数据存回源数据库。
    设计一个连接方式数据库应用程序一般包括以下基本步骤:
    (1) 建立数据库,包括若干个表,在表中添入数据。
    (2) 建立和数据库的连接。
    (3) 使用查询、修改、删除、更新等Command对象直接对数据库操作。
    以下章节将按以上步骤说明数据库应用程序的具体设计方法。
    8.3 用ACCESS创建数据库
    本例创建一个学生信息管理系统,包括两个表,第一个表记录学生的基本情况,包括以下字段:学号、姓名、性别等。第二个表记录学生的学习成绩,包括以下字段:记录编号、课程名称、分数、拥有该课程成绩学生的学号。注意,把学习成绩表字段定义为:学号、语文成绩、数学成绩、物理成绩等字段是不合适的,这样做,增加一门课程,就要增加一个字段,字段要动态增加,这显然不合理。用Access2000程序创建数据库具体步骤如下:
    (1) 运行microsoft Access2000程序,出现《microsoft Access》对话框如下图,选择空Access数据库,单击确定按钮,打开标题为《文件新建数据库》对话框。

    (2) 在标题为《文件新建数据库》对话框中,添入数据库文件名:StudentI,选择保存位置和保存类型如下图。单击创建按钮,出现《StudentI:数据库》对话框。

    (3) 在《StudentI:数据库》对话框中,双击《使用设计器创建数据表》,出现《表1:表》对话框。在表中可以创建数据库表的字段。

    (4) 在《表1:表》对话框中,创建字段StudentNum,数字类型的整形,必填字段,默认值为0,标题为学生编号。字段StudentName,文本,字段大小8,必填字段,默认值为空,标题为学生姓名。字段StudentSex,文本,字段大小2,标题为性别。右击字段StudentNum,在弹出菜单中选择主键菜单项,设置字段StudentNum为主关键字。如下图。

    (5) 选择《文件》弹出菜单的《保存》菜单项,出现《另存为》对话框,在对话框中的《表名称(N)》编辑框中输入表名:Student,单击确定按钮。关闭《表1:表》对话框。
    (6) 在《StudentI:数据库》对话框左侧,选择对象为:表,在右测出现Student表,双击Student表,出现《Student:表》对话框。

    (7) 在《Student:表》对话框中为各个字段输入数据:例如:1,张三,男;2,李四,女;3,王五,男;4,鲁六,女。然后存盘。如下图。

    (8) 同样方法建立表Score,记录所有学生学习成绩。包括字段ScoreID(记录编号),自动编号;ClassName(课程名称),文本,字段大小26,必填字段,默认值为空;Score(分数),字节类型,必填字段,默认值为0;StudentNum(拥有该课程成绩学生的学号),数字类型的整形,必填字段,默认值为空。设置字段ScoreID为主关键字。增加若干数据。
    (9) 退出microsoft Access2000程序。
    8.4 结构化查询语言SQL
    用户通过SQL(Structed Query Language,结构化查询语言)来访问数据库中的数据,使用SQL语句可以对数据库中的数据进行查询、增加、删除记录,修改记录中的数据。几乎所有的数据库都支持SQL语言,编写数据库应用程序必须学习SQL语言。
    8.4.1 Select语句
    Select语句是最常用的语句,可以从数据库的表中获得满足一些条件的数据集。常见的Select语句如下:
     select * from student
    从表Student表中选择所有字段的所有记录
     select StudentNum,StudentName from student
    从表Student表中选择字段StudentNum和字段StudentName的所有记录
     select * from score where StudentNum=1
    从表score表中查找学号StudentNum=1同学的所有课程的成绩。
    8.4.2 Insert语句
    用于向数据库表中插入一个新记录。例如,向表student中插入一个新纪录的语句如下:
    Insert student (StudentNum,StudentName,StudentSex)Value(5,"田七","男")
    8.4.3 Delete语句
    用于删除数据库表中的一个记录。例如,删除student表中学号为1的学生,语句如下:
    Delete From student where StudentNum=1
    8.4.4 Update语句
    更新数据库的Student表中学号为1的学生名字为"陈七":
    Update Student Set StudentName="陈七" Where StudentNum=1。
    8.5 用Connection对象连接数据库。
    Connection类对象用来连接数据库。ADO.NET中有两类Connection对象,一类用于微软的Sql Server数据库,该对象连接微软SQL数据库时效率较高,另一类用于其它支持ODBC的数据库。连接Sql Server数据库序应引用如下命名空间:
    Using System.Data;
    using System.Data.SqlClient;
    连接其它数据库序应引用如下命名空间:
    Using System.Data;
    using System.Data.OleDb;
    使用SqlConnection的例子:
    string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;";
    SqlConnection conn=new SqlConnection(txtConn);//建立连接
    其中,DATABASE为数据库名称,这里为Northwind,是微软Sql Server数据库自带的数据库例子,必须安装此数据库才能使用。UID为用户名,PWD为密码,Northwind数据库安装后的用户名为sa,密码为空。SERVER为所使用的数据库服务器,这里数据库服务器和数据库应用程序在同一台计算机中,因此为localhost,中文意义是本地主机。
    使用OleDbConnection的例子:
    string txtConn=
    "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\VC#\\studentI.mdb";
    OleDbConnection conn = new OleDbConnection(txtConn);//建立连接
    Provider为所使用的数据库驱动程序。DataSource为数据库的位置,有时还增加参数ConnectionTimeOut,为链接超时时间,默认为15秒。
    也可以使用Visual Stutdio.Net建立连接,例子见8.10B。
    Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\myFolder\*.accdb;Persist Security Info=False;
    http://zm10.sm.cn/?src=http%3A%2F%2Fwww.codefans.net%2Farticles%2F321.shtml&uid=56c023f99f0b87c81990565cfbf7702e&hid=3f68bec9381b228930fa08d2f35779b4&pos=8&cid=9&time=1431233777690&from=click&restype=1&pagetype=0300004000000402&bu=web&uc_param_str=pidi
    8.6 Command对象
    建立连接后,ADO.Net通过Command对象用SQL语句来访问数据库中的数据,对数据库中的数据进行查询,增加、删除记录,修改记录中的数据。具体用法如下:
    string txtCommand="SELECT * FROM student";
    使用OleDbCommand
    OleDbCommand Command1=new OleDbCommand(txtCommand,conn);
    使用SQLCommand
    SQLCommand Command1=new SQLCommand(txtCommand,conn);
    例子8.6:连接方式数据库应用程序。有时,数据库应用程序使用连接方式可能更方便一些,例如,用户的注册信息应该立即存到数据库中。下边的例子模拟用户注册,首先请用户输入个人信息,单击注册按钮,用SQL语句把数据存到StudentI数据库的Student表中。具体步骤如下:
    (1) 新建项目。放三个Label控件到窗体,修改属性Text分别为:学号、姓名、性别。
    (2) 放三个TextBox控件到窗体,修改属性Text都为空。TextBox1输入学号,TextBox2输入学生姓名,TextBox3输入学生性别。
    (3) 引入命名空间using System.Data.OleDb;
    (4) 增加变量:OleDbConnection conn; OleDbCommand da;
    (5) 增加一个按钮,属性Text="增加记录"。界面如下图。为其增加单击事件函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { if(textBox1.Text==""&&textBox2.Text==""&&textBox3.Text=="")
    { MessageBox.Show("所有项都必须填写!");
    return;
    }//注意,下边语句必须保证数据库文件studentI.mdb在文件夹D:\\vc#中
    string txt1=
    "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\vc#\\studentI.mdb";
    string txt2=
    "Insert Into Student(StudentNum,StudentName,StudentSex) Values('";
    txt2+=textBox1.Text + "' , '";
    txt2+=textBox2.Text + "' , '";
    txt2+=textBox3.Text+"')";//最后结果要保证在TextBox中输入的内容被单引号括住
    conn = new OleDbConnection(txt1);
    conn.Open();
    da=new OleDbCommand();
    da.CommandText=txt2;
    da.Connection=conn;
    da.ExecuteNonQuery();
    textBox1.Text="";
    textBox2.Text="";
    textBox3.Text="";
    conn.Close();
    }
    (6) 运行,输入学号,姓名,性别,单击《增加记录》按钮,察看数据库,可以看到增加了一个记录。请读者修改为删除一个记录,修改一个记录。
    8.7 DataAdapter对象
    DataAdapter对象包含4个Command对象:SelectCommand对象,InsertCommand对象,UpdateCommand对象,DeleteCommand对象,完成对数据库中的数据的选择,插入,更新,删除等功能。DataAdapter对象隐藏了Connection对象和Command对象沟通的细节,方便使用,在许多数据库应用中,都使用DataAdapter对象。使用DataAdapter对象例子如下:
    string txtConn=
    "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\VC#\\studentI.mdb";
    OleDbConnection conn=new OleDbConnection(txtConn);//建立连接
    string txtCommand="SELECT * FROM student";
    OleDbDataAdapter da=new OleDbDataAdapter(txtCommand,conn);
    8.8 DataSet对象
    DataSet对象从数据库中取出感兴趣的数据,包括指定表和表中满足条件的记录,DataSet对象被建立在内存中,可以包含若干表,可以认为是数据库在内存中的一个子集。DataSet对象只在获取或更新数据时保持和数据库连接,其它时间都是断开的。
    8.8.1 使用DataSet的优点
    在传统的数据库应用程序中,必须建立与数据库的连接,并在数据库应用程序运行过程中保持连接状态。出于各种原因,该方法在许多数据库应用程序中是不实用的。
     一般情况下,数据库只可以维持少量的并发连接。维持过多并发连接将降低数据库的总体性能,增加数据库应用程序的访问时间。保持四个用户连接的执行也许还可以接受,但连接100个用户时可能就不行了。同时访问Web数据库的访问者可能非常多,例如大型网上商店,所有访问都保持连接是不现实的。
     在Web应用程序中,浏览器从服务器请求网页,服务器发送该页后,服务器就不再与浏览器有任何连接,直到下一次请求为止。在这种情况下,维持打开的数据库连接是不可行的,因为没有办法知道数据使用者(客户端)是否还将对数据库访问。
     如果数据库应用程序的多个控件需对数据库数据操作,则多个控件都必须和数据库建立连接,或者为这些控件设计一种方式以相互传递数据。这使问题变得复杂。
    出于以上这些原因,ADO.NET数据库访问被设计为以不连接的数据模型为基础,应用程序只在获取或更新数据时保持连接,其它时间都是断开的。由于数据库并未被大部分时间空闲的连接占用,所以它可以为更多用户服务。
    8.8.2 数据集DataSet概念
    在不连接的数据模型中,每次数据库应用程序需要处理下一条记录时都连接回数据库是不可行的,这样做会大大消除使用不连接数据的优越性。解决方案是临时存储从数据库检索的记录,然后使用该临时集。这便是数据集的概念。数据集DataSet是从数据库检索的记录的缓存。数据集DataSet中包含一个或多个表(这些表基于源数据库中的表),并且还可以包含有关这些表之间的关系,以及对表包含数据的约束信息。数据集DataSet的数据通常是源数据库内容的子集,可以用与操作实际数据库十分类似的方式操作数据集DataSet,但操作时,将保持与源数据库的不连接状态,使数据库可以自由执行其他任务。
    因为数据集DataSet是数据库数据的私有子集,所以它不一定反映源数据库的当前状态,因此,需要经常更新数据集DataSet中的数据。可以修改数据集DataSet中的数据,然后把这些修改写回到源数据库。为了从源数据库获取数据和将修改写回源数据库,请使用数据适配器DataAdapter对象。数据适配器DataAdapter对象包含更新数据集DataSet和将修改写回源数据库的方法。DataAdapter.Fill()方法执行更新数据集DataSet操作。DataAdapter.Update()方法执行将修改写回源数据库操作。
    尽管数据集是作为从数据库获取的数据的缓存,但数据集与数据库之间没有任何实际关系。数据集是容器,它用数据适配器的SQL命令或存储过程填充。
    8.8.3 使用DataSet对象
    使用DataSet对象例子如下:
    string txtConn=
    "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\VC#\\studentI.mdb";
    OleDbConnection conn=new OleDbConnection(txtConn);//建立连接
    string txtCommand="SELECT * FROM student";//将表student所有记录从数据库取出
    OleDbDataAdapter da=new OleDbDataAdapter(txtCommand,conn);
    DataSet MyDataSet=new DataSet();//建立DataSet对象MyDataSet
    da.Fill(MyDataSet,"MyTable");//将数据填充到数据集MyDataSet中,新表名为:MyTable
    下例说明了DataSet的使用方法:
     添加记录
    DataRow dr=MyDataSet.Tables["Student"].NewRow();
    dr["StudentNum"]=4;
    dr["StudentName"]="鲁豫";
    dr["StudentSex"]="女"
     修改Student表中第0个记录的"StudentName"字段的值为"田歌"
    MyDataSet.Tables["Student"].Rows.[0]["StudentName"]="田歌";
     删除Student表中第0个记录
    MyDataSet.Tables["Student"].Rows.[0].Delete();
     恢复数据
    if(MyDataSet.HasErros)
    MyDataSet.RejectChanges();
     检查DataSet是否有改动
    if(MyDataSet.HasChanges())
    da.Update(MyDataSet);//更新数据库
    8.8.4 为DataSet对象中的表指定主键、建立关系
    为DataSet对象中的表指定主键、建立关系,可以保证数据的完整性,例如,主键取值不能重复,不能删除主表中的数据(例如某个学生),而不删除另一个表中和其有关的数据(例如另一个表中的学生成绩)等等。
     设置表的主键:
    DataColumn[] pKey=new DataColumn[1];
    pKey[0]=MyDataSet.Tables["Student"].Columns["StudentNum"];
    MyDataSet.Tables["Student"].PrimaryKey=pKey;
     建立两个表的联系:
    MyDataSet.Relations.Add("StudentNum",
    MyDataSet.Tables["Student"].Columns["StudentNum"],
    MyDataSet.Tables["Score"].Columns["StudentNum"]);
    8.9 用DataGraid控件显示数据和数据绑定
    DataGraid控件用来按行和列格式显示数据表中的数据。DataGraid控件属性DataSource用来指定数据表所在的数据集DataSet对象。DataGraid控件属性DataMember用来指定在数据集DataSet对象中要显示的数据表的名字。当这两个属性被正确设定,DataGraid控件将以网格形式正确显示指定数据表。DataGraid控件中的数据被修改后,DataSet对象中相应数据表中的数据也被修改。这叫做数据绑定。数据绑定有两个要点:第一,数据绑定控件能按绑定的数据源正确显示数据源的数据,第二,在数据绑定控件中被修改的数据能被正确写回数据源,这里的数据源一般是数据集DataSet对象中的一个表或者是表中的一个字段。许多控件都可以数据绑定。
    8.10 不连接数据库应用程序的完整的例子
    例8.10A:
    (1) 新建项目。增加语句using System.Data.OleDb;
    (2) 添加控件DataGraid,属性Name=dataGrid1
    (3) 为Form1类增加变量:OleDbConnection conn; OleDbDataAdapter da; DataSet ds;
    (4) 为Form1类增加一个方法:
    private void OnLoadData()
    { string txtConn=
    "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\vc#\\studentI.mdb";
    conn=new OleDbConnection(txtConn);
    string txtCommand="SELECT StudentName, StudentNum, StudentSex FROM Student";
    da=new OleDbDataAdapter(txtCommand,conn);
    ds=new DataSet("ds");
    da.Fill(ds,"Student");
    dataGrid1.DataSource=ds;
    dataGrid1.DataMember="Student";
    }
    (5) 在构造函数中增加语句
    OnLoadData();
    (6) 运行,可以看到表Student中的信息。运行效果如上图。
    例8.10B:使用Visual Stutdio.Net连接数据库StudentI并显示Student表,具体步骤如下:
    (1) 新建项目。在窗体中放置控件OleDbConnection,其属性Name=OleDbConnection1。单击控件oleDbConnection属性ConnectionString的下拉列表的箭头,在列表中选择新建连接,打开《数据链接属性》对话框,选择提供程序选项卡页,选择OLE DB提供程序为Microsoft.Jet.OLEDB.4.0后,单击下一步按钮,在1.选择或输入数据库名称下的编辑框中,单击其后按钮,选择数据库StudentI。在2.输入登录服务器信息下,选中使用指定的用户名称和密码单选按钮,在用户名称中输入Admin,选中空白密码多选按钮。单击测试连接按钮,应出现测试连接成功对话框。按确定按钮退出。
    (2) 在窗体中放置控件DataGrid,其属性Name=dataGrid1。
    (3) 选择菜单项视图/服务器资源管理器,打开服务器资源管理器窗口,可以看到新建立的连接,单击前边的加号,展开此连接树后,再展开表(Table)树,可以看到数据库中的所有表名。拖表Student到窗体控件DataGrid中,将自动增加控件oleDbDataAdapter1到窗体。
    (4) 单击oleDbDataAdapter1选中它,单击菜单项数据/生成数据集…,打开生成数据集对话框,他选择默认值。按确定按钮退出。自动增加数据集DataSet对象dataSet11。
    (5) 修改DataGrid1属性DataSource= dataSet11,属性Datamember=Student,Student为表名。此时在DataGrid1中可看到表的表头。
    (6) 在构造函数中增加语句如下:
    oleDbDataAdapter1.Fill(dataSet11);
    (7) 运行,可以在控件DataGrid1中看到表Student的内容。
    (8) 以下将标题改为中文。选中控件DataGrid1,单击其属性TableStyles后的标题为…的按钮,打开DataGridTableStyle集合编辑器对话框,单击添加按钮,增加一个dataGridTableStyle对象。从属性MapingName的下拉列表中选择Student表。单击其属性GridColumnStyles后的标题为…的按钮,打开DataGridTextBoxColumn集合编辑器对话框,单击添加按钮,增加一个dataGridTextBoxColumn对象。修改HeadText属性为:学生姓名,从属性MapingName的下拉列表中选择Student表的StudentName字段。按此办法,再增加两个dataGridTextBoxColumn对象,修改HeadText属性分别为:学生学号、学生性别,属性MapingName分别为:StudentNum、StudentSex。
    (9) 运行,可以在控件DataGrid1中看到表头为中文。
    8.11 修改数据并保存修改的数据到源数据库
    在控件DataGrid1中可以修改数据,可以增加记录。现增加一个按钮,标题为:删除,单击此按钮后,将删除控件DataGrid1中选定的纪录。为其增加单击事件函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { int x;
    x=dataGrid1.CurrentRowIndex;
    dataSet11.Tables["Student"].Rows[x].Delete();
    }
    运行,选中一个控件DataGrid中某行(一条记录),单击按钮可以删除此行。由于排序等原因,控件DataGrid中数据记录的顺序可能和dataSet11中表的记录的顺序不一致,因此此方法删除数据可能出出错。修改按钮单击事件函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { int x;
    object z;
    x=dataGrid1.CurrentRowIndex;
    z=dataGrid1[x,1];
    DataRow foundRow;
    object[] findTheseVals = new object[1];
    findTheseVals[0] = z;
    foundRow = dataSet11.Tables["Student"].Rows.Find(findTheseVals);
    if(foundRow != null)
    foundRow.Delete();
    }
    为了保存修改的数据到源数据库,现增加一个按钮,标题为:保存数据,单击此按钮后,将保存修改的数据到源数据库。为其增加单击事件函数如下:
    private void button2_Click(object sender, System.EventArgs e)
    { if(dataSet11.HasChanges())//在退出程序时,也应用此函数检查,提醒用户是否存盘
    oleDbDataAdapter1.Update(dataSet11);
    }
    运行,增加一个记录,单击第二个按钮,关闭程序,再打开,可以看到新增的记录被显示,说明新增记录已被存到源数据库。
    8.12 其它数据绑定控件
    本例用TextBox控件显示Student表,具体步骤如下:
    (1) 新建项目。
    (2) 从工具箱中,将3个Label控件放到窗体上,属性Text分别为:学号、姓名、性别。
    (3) 从工具箱中,将 3个TextBox控件放到窗体上,属性Text都为空。
    (4) 从“工具箱”的“数据”选项卡中,将 OleDbDataAdapter 对象拖到窗体上。“数据适配器配置向导”启动,它将帮助您创建连接和适配器。
    (5) 在该向导中,执行下列操作:
     在第二个窗格中,创建或选择一个指向数据库StudentI的连接。
     在第三个窗格中,指定您要使用 SQL 语句来访问数据库。
     在第四个窗格中创建以下 SQL 语句:SELECT * FROM Student
     单击“完成”完成该向导。
    (6) 从“数据”菜单中选择“生成数据集”。 如果看不到“数据”菜单,请在窗体中单击;该窗体必须具有焦点,该菜单才会出现。
    (7) 选择“新建”选项,将该数据集命名为 DataSet1。在“选择要添加到数据集中的表”下面的列表中,确保选择了“Student”。 选中“将此数据集添加到设计器”,然后单击“确定”。 从“文件”菜单中选择“全部保存”,存所有文件。
    (8) 选中textBox1控件的属性DataBindings,单击前边+号展开,选中Text属性,从下拉列表中选择dataSet11 - Student.StudentNum。同样办法textBox2为dataSet11 - Student.StudentName,textBox3为dataSet11 - Student.StudentSex。
    (9) 增加主窗体Form1的Load事件函数如下:
    private void Form1_Load(object sender, System.EventArgs e)
    { dataSet11.Clear();
    oleDbDataAdapter1.Fill(dataSet11);
    }
    (10) 运行,可以看到第一个学生情况。现增加移动记录功能,以显示不同学生情况。
    (11) 增加变量:BindingManagerBase Navigator;
    (12) 修改主窗体Form1的Load事件函数如下:
    private void Form1_Load(object sender, System.EventArgs e)
    { dataSet11.Clear();
    oleDbDataAdapter1.Fill(dataSet11);
    Navigator=this.BindingContext[dataSet11,"Student"];
    }
    (13) 从工具箱中,将 4个Button控件放到窗体上,属性Text分别为:第一记录、下一记录、前一记录、最后记录。各个按钮的单击事件函数如下:
    private void button1_Click(object sender, System.EventArgs e)
    { Navigator.Position=0;//第一记录
    }
    private void button2_Click(object sender, System.EventArgs e)
    { if(Navigator.Position!=Navigator.Count-1)
    Navigator.Position+=1;//下一记录
    }
    private void button3_Click(object sender, System.EventArgs e)
    { if(Navigator.Position!=0)
    Navigator.Position-=1;//前一记录
    }
    private void button4_Click(object sender, System.EventArgs e)
    { Navigator.Position=Navigator.Count-1;//最后记录
    }
    (14) 运行,可以看到第一个学生情况。单击4个按钮可以移动记录。
    8.13 建立主从关系表
    在数据库StudentI中,在显示表Student和Score时,希望选中某个学生时,表Score只显示此学生的成绩,两个表的这种关系叫做主从关系。现在实现这两个表的主从关系。具体步骤如下:
    (1) 新建项目。
    (2) 从“工具箱”的“数据”选项卡中,将 OleDbDataAdapter 对象拖到窗体上。“数据适配器配置向导”启动,它将帮助您创建连接和适配器。
    (3) 在该向导中,执行下列操作:
     在第二个窗格中,创建或选择一个指向数据库StudentI的连接。
     在第三个窗格中,指定您要使用 SQL 语句来访问数据库。
     在第四个窗格中创建以下 SQL 语句:SELECT * FROM Student
     单击“完成”完成该向导。
    (4) 将第二个 OleDbDataAdapter 对象拖到窗体上。“数据适配器配置向导”再次启动。
    (5) 重复第(3)步,其中有以下差异:
     在第二个窗格中,选择上次所使用或创建的同一连接。
     创建下列 SQL 语句来访问Score表:SELECT Score.* FROM Score
    (6) 从“数据”菜单中选择“生成数据集”。 如果看不到“数据”菜单,请在窗体中单击;该窗体必须具有焦点,该菜单才会出现。
    (7) 选择“新建”选项,将该数据集命名为 DataSet1。在“选择要添加到数据集中的表”下面的列表中,确保选择了“Student”和“Score”。 选中“将此数据集添加到设计器”,然后单击“确定”。从“文件”菜单中选择“全部保存”,存所有文件。
    (8) Visual Studio 生成某类型化数据集类 (DataSet1) 和定义该数据集的架构。将在“解决方案资源管理器”中看到新架构 (DataSet1.xsd)。
    (9) 在“解决方案资源管理器”中,双击刚创建的数据集的架构(名为 DataSet1.xsd)。“XML 设计器”在“架构”视图中打开,显示数据集内的两个表。
    (10) 从“工具箱”的“XML 架构”选项卡中,将 Relation 对象拖到Score表(子表)上。“编辑关系”对话框打开,其中带有从这两个表中派生的默认值。父元素为Student表,子元素为Score表,键字段和外键字段都为StudentNum。其它不修改选默认值。单击“确定”按钮,关闭“编辑关系”对话框。在“XML 设计器”中,这两个表之间显示一个关系图标。如果需要更改关系设置,则可以右击相应的关系,选择“编辑关系”。
    (11) 保存该架构并关闭XML设计器。此刻,为执行从数据库获取信息并转移到数据集的操作所需的全部设置均已完成。可以向窗体添加显示数据的控件了。
    (12) 返回到创建该项目时已打开的默认窗体 (Form1)。从“工具箱”的“Windows 窗体”选项卡中,将 DataGrid 控件拖到窗体上,Name=dataGrid1。RowHeadersVisable=false。在“属性”窗口中,将 DataSource 属性设置为 dataSet11,将 DataMember属性设置为Student。
    (13) 从“工具箱”的“Windows 窗体”选项卡中,将 DataGrid 控件拖到窗体上,Name=dataGrid2。在“属性”窗口中,将 DataSource 属性设置为 dataSet11,将 DataMember属性设置为Student.StudentScore,设置这两个属性将网格绑定到关系对象,以便网格只包含dataGrid1表中选择的学生成绩。
    (14) 增加主窗体Form1的Load事件函数如下:
    private void Form1_Load(object sender, System.EventArgs e)
    { dataSet11.Clear();
    oleDbDataAdapter1.Fill(dataSet11);
    oleDbDataAdapter2.Fill(dataSet11);
    }
    (15) 运行,可以看到两个DataGrid 控件,dataGrid1显示学生情况表,dataGrid2显示dataGrid1表中选中的学生的成绩。在dataGrid1表中选择不同学生,dataGrid2显示相应学生的成绩。


    第九章 ASP.Net编程基础知识
    本章首先介绍用ASP.Net技术编制服务器端动态网页所需的网络和HTML标记语言方面有关的知识。然后介绍ASP.Net技术基础。
    9.1 网络基础
    用ASP.Net技术编制服务器端动态网页,必然要和网络打交道,具备一些网络方面的知识是必要的。这里假设读者已经学过计算机基础课程,在此基础上,进一步介绍用ASP.Net技术编制服务器端动态网页所需的必备网络基础知识。
    9.2.1 局域网、广域网和INTERNET
    把分布在不同地理区域的计算机以及专门的外部设备利用通信线路互连,使各个计算机之间能够相互通讯,实现信息和资源共享,就组成了计算机网络。在一个较小区域,例如在单位内部组成的计算机网络,称为局域网。一个较大区域的计算机网络,称为广域网。为了使各个局域网之间互相通讯,可以把各个局域网连起来,组成广域网。如将全世界范围的计算机网络采用TCP/IP网络传输协议联到一起,则组成INTERNET。
    INTERNET提供了许多服务,例如:远程登录服务Telnet、文件传送服务FTP、电子邮件服务E-mail、电子公告板系统BBS、万维网WWW(Web)、电子商务、IP电话等等。本课程主要介绍万维网WWW中服务器端动态网页的设计方法。
    9.2.2 网络传输协议
    网络的目的是为了通讯,共享资源。通讯即传输数据,为传输数据应遵守一定规则,这个规则叫网络传输协议。不同的网络操作系统采用不同的网络传输协议。而在INTERNET中,为了传输数据,大家都必须采用相同的传输协议,即TCP/IP协议。
    9.2.3 IP地址
    INTERNET中有成千上万台计算机,它允许任何两台计算机之间进行通讯,为了区分不同的计算机,必须给每一台计算机一个唯一的编号,这个编号叫计算机的IP地址,它是一个32位二进制数,用四个十进制数表示,中间用点隔开,每个十进制数允许值为0-255(一个字节),例如,202.112.10.105,这种记录方法叫点数记法。一个IP地址一般由两部分组成,网络标志号及此网络中的计算机号,例如,202.112.10.105如果是一个C类地址,其网络标志号为202.112.10.0,网络中的计算机号为105。一个局域网络中,所有计算机中都应有相同的网络标志号,每个计算机有不同的计算机号,两个不同局域网络,其网络标志号必须不同,但不同网络中,主机号可以相同。每个计算机要和INTERNET联接,必须有自己的IP地址。
    32位IP地址被分为5类,即A、B、C、D和E类地址。A类地址的第一字节为网络标志号,其余3字节为计算机号。B类地址的前两个字节为网络标志号,其余2字节为计算机号。C类地址的前三个字节为网络标志号,最后一字节为计算机号。D为特殊地址,E为私有地址。设置TCP/IP时,有一项叫子网掩码,用子网掩码表示IP地址是哪类地址,A类地址的子网掩码是:255.0.0.0,B类地址的子网掩码是:255.255.0.0,C类地址的子网掩码是:255.255.255.0。我国大部分单位的IP地址为C类地址。
    9.2.4 域名
    用点数法表示的IP地址,虽然简单,但很难记忆,为了解决此问题,可以为INTERNET网中的每台计算机起一个名字,在INTERNET中叫域名,并使此计算机的名字和IP地址对应起来,使我们可以使用名字访问计算机,就象我们使用IP地址一样。例如微软的域名www.microsoft.com,清华大学的域名www.tsinghua.deu.cn。
    在INTERNET中访问其它计算机必须使用IP地址,因此域名必须转换为IP地址。实现域名(和WINDOWS/2000/98中计算机名有区别)和IP地址转换的软件叫DNS(域名服务器)。在网内有一台计算机运行DNS服务器软件,这台计算机叫DNS服务器。它负责一定区域内的计算机域名和IP地址的转换,这个区域一般是一个网的内部的所有计算机。当网内的计算机用域名和其它计算机通讯时,则首先呼叫DNS服务器,DNS服务器送出此域名对应的IP地址,网内的计算机收到IP地址后,再利用IP地址和其它计算机通讯。如果,本DNS不能转换相应的域名,则向上一级DNS申请转换。计算机要和INTERNET联接,设置TCP/IP时,必须设置DNS服务器IP地址。
    9.2.5 URL
    我们在用浏览器浏览网页时,实际上是用URL来定位一个网页的。URL是Uniform Resource Location(统一资源定位器)的简称。他的一般格式是:传输协议://域名:端口号/全路径文件名。http:为www专用超文本传输协议。域名,即上边讲到,例如www.microsoft.com微软域名,此处可以是IP地址,其格式为http://IP地址:端口号/全路径文件名。全路径文件名:它指示访问文件的全路径,只写出宿主目录以下的全路径文件名。如果在访问主页时不指定此项,则访问其默认主页,例如当我们在浏览器的URL(地址)处键入http://www.sun.com/时,将访问sun公司默认主页。当光标移到某关键词,光标变为手型,单击此关键词则显示和此关键词相联系的URL所指定的网页。此网页可能在Internet中某网站的计算机中。在www系统中,每一个网页都有自己的URL,由它决定此网页在www网中的具体位置。它很象计算机文件系统中的文件全路径名。
    9.2.6 端口号
    一台计算机上可能运行多个服务器软件,如www服务器软件(可能不只一个)、ftp服务器软件等,它们的IP地址是相同的。为了区分不同的服务器,为每个服务器编一个号,叫端口号。此项不是必须的,如果一台计算机仅运行一个www服务器软件,则一般使用默认端口号80,运行一个ftp服务器软件,其端号为采用默认值20或21等,如采用默认端口号,端口号可以不写。但如有多个相同服务器运行,则应指定不同端口,其中不是采用默认端口号值的服务器软件在使用URL定位时,则应指明使用的端口号。
    9.2.7 HTML,HTTP和网页
    网页使用HTML标记语言写成。HTML标记是用特殊的ASCII字符来定义网页中的格式、字体、颜色等内容。由于各种系统中,例如Windows、Linux、Unix和苹果系统,都支持ASCII字符标准,不同系统中的浏览器都可以解释这些ASCII标记,将其所表示的网页在屏幕中显示。这样,不同的系统都可以使用统一的ASCII标记,访问其它系统中的网页。网页是WWW系统最小传输单位,它是用HTML语言写的具有一定格式的数据集合,可供浏览器显示。HTTP是超文本传输协议,用在WWW服务器和浏览器之间传输网页。本质上是TCP/IP协议,所有的WWW服务器和浏览器都应遵守HTML和HTTP协议,才能使同一网页在任何计算机中,使用任何浏览器都能显示同一画面,但实际上各公司浏览器软件是有差异的,最明显的是IE和Netscape之间在显示同一网页时,是有差别的。做好网页后,应用不同浏览器测试一下,看是都能通过
    9.2.8 Web服务器和浏览器工作方式
    Web是基于客户机/服务器模式,运行Web服务器软件的计算机叫Web服务器,运行浏览器的计算机叫客户机。服务器24小时开机,在指定的文件夹(宿主目录)上存贮大量的网页,这些网页用URL定位,Web服务器软件总是在监视是否有浏览器访问自己。客户机用浏览器访问Web服务器,在浏览器上键入要访问的网页的URL,例如:http://www.sun.com/,用DNS(域名系统)转换www.sun.com域名为IP地址,通过IP地址和sun公司的Web服务器通讯,sun公司的Web服务器接到信息后,由于未指定文件名,将默认主页送出。浏览器接到主页数据,将其显示。主页中列出各项主题,每当鼠标经过这些主题时,鼠标变为手形,双击此主题,将和主题有关的网页调入并显示。这种方法叫超链接。超链接的本质是:单击该题目,转换为所联系网页的URL,在此URL中,在文件路径名处,指出了网页在Web服务器中路径及文件名,当把这些信息送给Web服务器后,Web服务器就送出指定的网页。
    9.2.9 宿主目录、默认主页及网站
    默认文档(主页)就是当用户用不带文件名的URL访问www服务器时送出去的网页。它所在的目录,叫宿主目录。宿主目录下的文件对访问者都是可见的。宿主目录和默认文档名在不同的Web服务器中是不同的。Windows2000的IIS服务器宿主目录为…\InetPub\wwwroot,默认文件名(主页名)为Default.htm。大部分www服务器允许修改宿主目录。当用户使用不带文件名的URL访问www服务器时,并且宿主目录中不存在默认文档,而且www服务器允许列出宿主目录下的所有目录列表,将返回宿主目录下的所有目录列表。
    由此,可以看出,创建一个网站,就是创建一个文件夹,在文件夹中存入预先设计好的网页。然后,把此文件夹设定为宿主目录,例如Windows2000的IIS服务器默认的宿主目录为…\InetPub\wwwroot。然后运行WEB服务器软件,例如微软的IIS服务器软件。网络上的其它计算机就可以通过URL访问该计算机中宿主目录中的网页。
    9.2.10 静态网页
    创建一个网站,必须编制若干网页,网站才算建成。静态网页是预先做好的网页,在被访问时不被修改。相对讲静态网页制作比较容易,即使不懂超文本语言,使用FrontPage,Dreamerware就可以完成,而动态网页制作则比较困难。
    9.2.11 客户端动态网页
    例如,网页根据上下午,晚上问客人早上好,下午好和晚上好,又如,网页对于用户输入的内容进行检查,根据用户的选择完成不同的功能。有时,希望在浏览器内实现动画,放映影片等,这些都是客户端动态网页的例子。这可以在HTML语言加入Javascript或VBscript脚本语言来实现。也可以在HTML语言嵌入Java小程序(Applet)来实现。因此应比较系统的学习一下HTML语言,还应该学习Javascript或VBscript脚本语言,进一步还可以学习Java语言。另外使用微软的AxtiveX控件也是实现客户端动态网页的一种方法。
    9.2.12 服务器端动态网页
    举一个客户登记的例子,当客人在客户机端用浏览器填好表格后,将表格数据返回Web服务器,应把这些信息存入运行Web服务器的计算机内的数据库。Web服务器并不能完成此工作,它调用运行Web服务器的计算机上的其它程序完成,这个程序叫Web服务器应用程序。又如,建立一个中学校园网,可以建立一个学生情况数据库,可供校长、教导处、老师查询,可能要根据一些条件进行查询,如某班的学生,获得三好生的学生等,浏览器把所查找的条件送给Web服务器后,Web服务器必须调用Web服务器应用程序完成此项工作,此程序查到有关数据后,要变成用HTML语言的网页,送给Web服务器,再送给请求此项内容的客户机。编制服务器端的Web服务器应用程序的方法主要有如下几种:CGI,ISAPI,NSAPI,ASP,ASP.NET,JSP,PHP等。ASP.NET技术是微软最新提出的编制动态网页技术。现在Web数据库比较流行的方法是三层数据库,如下图:


    访问数据库的方法
    这里的3层是指浏览器、Web服务器和数据库。一些文献中提到4层数据库系统是在Web服务器和数据库之间增加一个应用服务器层。本章既是讲述设计Web服务器应用程序的方法。
    9.2.13 修改宿主目录及建立虚拟目录
    创建一个网站,必须有如下条件:第一,有固定的IP地址。第二,安装并运行Web服务器软件,例如Windows操作系统下的IIS,Unix或Liunx下的Aparc。第三,在宿主目录下有可供游览的网页。Windows2000的IIS服务器的宿主目录为\InetPub\wwwroot。可以修改宿主目录为其它任意目录,修改Windows2000的IIS服务器的宿主目录方法如下:
    (1) 在D盘建文件夹:ASP
    (2) 打开控制面板,双击管理工具图标。
    (3) 双击Internet 服务管理器图标。
    (4) 右击第一个+号后的计算机名,出现弹出菜单,选择菜单项新建/WEB站点,按向导步骤选择D:/asp为新站点。单击第一个+,打开文件树,右击默认WEB站点,将其停止。右击管理WEB站点,将其停止。
    请读者作如下试验:首先用记事本创建如下网页:
    <html>
    <body>
    这是我的第一个网页
    </body>
    </html>
    以文件名Test.htm存到d:/asp文件夹中,查找本机IP地址,假如为:202.204.206.98。在另一台计算机中打开浏览器,输入地址:http://202.204.206.98/Test.htm,在浏览器中应能看到文字:这是我的第一个网页。由此可知,必须把创建的网页拷贝到宿主目录下。从本机访问宿主目录下的网页时,可以在浏览器的URL(地址)处键入:http://localhost/网页以宿主目录为根目录的全路经。其他人访问时,可以在浏览器的URL(地址)处键入:http://IP地址或域名/网页以宿主目录为根目录的全路经。
    也可以用建虚拟目录的方法,存放网页,具体步骤如下:
    (1) 打开控制面板,双击管理工具图标。
    (2) 双击Internet 服务管理器图标。
    (3) 单击计算机名前的+号。
    (4) 右击默认Web站点,出现弹出菜单,选择菜单项新建/虚拟目录,按向导步骤选择D:/asp为新虚拟目录。此时,在默认Web站点下将会出现设定的虚拟目录,此目录允许其他人访问。
    请读者想一想,如把文件Test.htm拷贝到新虚拟目录,在另一台计算机中如何访问此网页。
    9.2 HTML标记语言
    网页使用HTML标记语言写成。HTML标记是用特殊的ASCII字符来定义网页中的格式,字体等等特点。由于各种系统中,都支持ASCII字符标准,不同系统中的浏览器都可以解释这些ASCII标记,将其所表示的网页在屏幕中显示。这样,不同的系统都可以使用统一ASCII标记,访问其它系统中的网页。
    如果有WEB服务器时,用IE浏览器显示网页前,必须把网页拷贝到宿主目录下,例如Windows2000的IIS服务器为\InetPub\wwwroot。访问此网页时,在浏览器的URL(地址)处键入此网页的URL,回车即可。
    为了在没有WEB服务器时,能用IE浏览器显示静态网页,首先将IE的默认网页设置为about:blank。然后运行IE,在地址栏中输入网页文件的路径,回车即可。修改IE的默认网页为about:blank及IE的默认编辑器为写字板的具体办法是:单击IE菜单工具/Internet选项,打开Internet选项对话框。在常规页中的主页选择使用空白页,程序页中,HTML编辑器选择Windows Notepad。
    9.2.1 HTML标记
    HTML标记是用特殊的ASCII字符来定义网页中的格式、字体、字符对齐方式等特点。其格式为:<标记名称>被控制的文字</标记名称>。其中,<标记名称>为开始标记,</标记名称>为结束标记,一般用来定义被控制的文字的格式或字体等。例如,下列标记使被控制的文字ASP.NET中间对齐:<center>ASP.NET</center>
    9.2.2 HTML文件结构
    一个网页文件的最基本HTML标记结构如下:
    <html>
    <head>
    <title>
    显示在浏览器标题栏中的文字
    </title>
    </head>
    <body>
    这里是网页的内容
    </body>
    </html>
    <html>表示网页文件的开始,</html>表示网页文件的的结束,网页的所有内容都应在这两个标记之间。<head>…</head>之间可以设定网页的一些信息,<title> </title>之间的文字显示在IE浏览器的标题栏中。<body>…</body>之间时网页的主题内容。由此可以看出,在HTML语法中,HTML标记可以嵌套,一个HTML标记可以包含另一个HTML标记,但标记的嵌套不能是交错的。下边是一个实际例子:
    <html>
    <head>
    <title>
    这是我的第一个网页
    </title>
    </head>
    <body>
    <center>
    这是我的第一个网页。<BR>
    其中&ltBR&gt标记表示换行,
    注意,仅键入回车时不能在网页中换行的。<BR>
    注意空格的用法, 键入四个空格,网页中只有一个空格。<BR>
    增加4个空格,&nbsp&nbsp&nbsp&nbsp是4个空格<BR>
    网页中不区分大小写,不同浏览器显示的效果可能不同<BR>
    <!--这是注释,浏览时不显示-->
    注意,特殊符号的显示:&lt,&gt,&quot,&amp,&copy,&reg
    </center>
    </body>
    </html>
    网页中可以增加一些标记,例如:
    <html>
    <head>
    <title>,<base>,<link>,<isindex>,<meta>
    </head>
    <body>
    HTML文件的正文写在这里…
    </body>
    </html>
    这些标记的用法见以下各节。
    9.2.3 语言字符集的信息
    语言字符集的信息用法见下边的网页。主要是选用网页使用的字符集,gb2312是中文字符集。
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
    <title>meta的使用</title>
    </head>
    <body>
    meta的使用。
    </body>
    </html>
    9.2.4 背景色彩和文字色彩
    设置背景和文字色的语法如下:<body bgcolor=#,text=#,link=#,alink=#,vlink=#>其中,bgcolor,text,link,alink,vlink的意义见书,#表示颜色,可用rrggbb表示,rr,gg,bb分别是表示红色,绿色,蓝色的16进制数,例如,红色为:ff0000。下例设置背景色为红色。
    <html>
    <head>
    <title>设置背景色为红色</title>
    </head>
    <body bgcolor=ff0000>
    设置背景色为红色。
    </body>
    </html>
    9.2.5 页面空白
    可以设置页面的上下左右边距,单位为像素。例如设置左边距为20个像素格式如下:
    <body leftnargin=20>
    9.2.6 显示一幅图
    下例在网页中显示一幅图。注意<IMG src="file:///D:/耿肇英/ASP.NET教案/baobao048.jpg" width="320" height="240">的写法。其中,file://是文件协议,用来选择本地文件。一般网页中用http://,即超文本协议,此时要求运行WEB服务器。
    <html>
    <head>
    <title>
    增加一幅图形!
    </title>
    </head>
    <body>
    <IMG src="file:///D:/耿肇英/ASP.NET教案/baobao048.jpg" width="320" height="240">
    </body>
    </html>
    9.2.7 超级链接
    浏览网页时,当鼠标变为手形时,单击,可以打开另一个网页,下边的例子在当前窗口打开另一个网页。
    <html>
    <head>
    <title>
    链接的例子
    </title>
    </head>
    <body>
    这是一个
    <a href="C214.htm">链接的例子</a>
    点一下带下划线的文字!
    </body>
    </html>
    9.2.8 超级链接在新窗口打开另一网页
    如下例子:
    <html>
    <head>
    <title>
    开一个新窗口例子
    </title>
    </head>
    <body>
    <a href="window.htm"target="window_name">
    开一个新窗口!
    </a>
    </body>
    </html>
    将光标移到”开一个新窗口”,光标变为手型,单击”开一个新窗口”,将打开另一个网页窗口,即window.htm网页。window.htm网页如下:
    <html>
    <head>
    <title>
    New Window Form HyperLink Dir
    </title>
    </head>
    <body>
    <H1aligh=center>这是新开的窗口!</H1>
    <center><p>返回原先窗口<a href="C226.htm#window">开新一个(浏览器)窗口</a>"一处
    </p></center>
    </body>
    </html>
    9.2.9 标尺线
    标尺线标记格式为:<hr align=left color="red" noshade size=10 width=50>。其中,align为标尺线对齐方式,可以取left,right,center。Color为标尺线颜色。Noshade设定标尺线为没有三维效果的实心黑线。Size为标尺线的厚度。width为标尺线的宽度,可以是像素值,也可以相对于窗口的百分比。实际使用见下例:
    <html>
    <head>
    <title>
    标尺线
    </title>
    </head>
    <body>
    <hr size=10>
    <hr width=#>
    <hr width=50>
    <hr width=50%>
    <hr align=left>
    <hr align=right>
    <hr width=50% align=left>
    <hr width=50% align=right>
    <hr noshade>
    <hr color="red">
    </body>
    </html>
    9.2.10 网页中标题的字体
    网页中一共有6种标题字体,见下例:
    <html>
    <head>
    <title>
    标题字体
    </title>
    </head>
    <body>
    <h1>这是1级标题!</h1>
    <h2>这是2级标题!</h2>
    <h3>这是3级标题!</h3>
    <h4>这是4级标题!</h4>
    <h5>这是5级标题!</h5>
    <h6>这是6级标题!</h6>
    </body>
    </html>
    其中,标题字体是黑体,每个标题自动插入一个空行。
    9.2.11 网页中正文字体
    网页中正文字体只有7种大小,可以用如下方法设置正文字体:
    <html>
    <head>
    <title>
    正文字体
    </title>
    </head>
    <body>
    <font size=7>字号为7,最大字体</font>
    <font size=6>字号为6</font>
    <font size=5>字号为5</font>
    <font size=4>字号为4</font>
    <font size=3>字号为4</font>
    <font size=2>字号为2</font>
    <font size=1>字号为1,最小字体</font>
    </body>
    </html>
    注意字号只能是1到7。
    9.2.12 斜体、粗体字符及为字体增加下划线,删除线
    可以使字体为斜体、粗体,为字体增加下划线,删除线。见下例:
    <html>
    <head>
    </head>
    <body>
    <b>标记内的字为黑体</b> <br>
    <i>标记内的字为斜体</i> <br>
    <u>标记内的字有下划线</u> <br>
    <tt>标记内的字等宽,例如w和i等宽</tt> <br>
    H<sub>2</sub> ,2为下标<br>
    A<sup>2</sup> ,2为上标<br>
    <s>标记内的字加删除线</s> <br>
    <strike>标记内的字加删除线</strike> <br>
    </body>
    </html>
    9.2.13 字体标记的组合使用
    见下例:
    <html>
    <head>
    <title>
    字体标记的组合使用
    </title>
    </head>
    <body>
    <i><font size=5>
    <b>今天</b> 天气<font size=6>真好!</font>
    </font></i>
    </body>
    </html>
    9.2.14 字体的颜色
    见下例:
    <html>
    <head>
    </head>
    <body>
    <font size=5 color=000000>Black</font>&
    <font size=5 color=red>Red</font>
    </body>
    </html>
    9.2.15 客户端字体
    每个操作系统都提供若干字体,网页可以使用操作系统提供的字体,下边的网页显示如何使用这些字体。
    <html>
    <head>
    </head>
    <body>
    <h1>客户端字体事例</h1>
    <font face="Arial">Arial...</font><br>
    <font face="Comic Sans MS">Comic Sans MS...</font><br>
    <font face="Courier">Courier...</font><br>
    <font face="Courier New">Courier New...</font><br>
    <font face="Modern">Modern...</font><br>
    <font face="MS Serif">MS Serif...</font><br>
    <font face="MS-DOS CP 932">MS-DOS CP 932...</font><br>
    <font face="Roman">Roman...</font><br>
    <font face="Script">Script...</font><br>
    <font face="Small Fonts">Small Fonts...</font><br>
    <font face="Symbol">Symbol...</font><br>
    <font face="Times Roman">Times Roman...</font><br>
    <font face="Times New Roman">Times New Roman...</font><br>
    <font face="WingDings">WingDings...</font><br>
    </body>
    </html>
    9.2.16 网页中控件的概念
    WEB服务器为了和用户进行交互,必须解决如下问题:首先,用户应能输入一些数据,例如,要查询的条件,用户登录的信息等等。第二,这些数据用什么方法传到WEB服务器。第三,WEB服务器用那个程序响应用户。为了实现以上功能,必须使用窗体控件,也叫表单控件form,Visual Studio.net中叫WebForm。同时还需要一些其它控件,例如,编辑框控件,列表框控件,下拉列表框控件和按钮等。可以用HTML标记语言定义控件。IE浏览器看到这些标记,就把它显示为相应的控件。控件有许多属性,也可以用HTML标记语言表示,每个属性用空格分开,用属性名称=属性值格式定义。
    9.2.17 窗体控件和其它控件的使用
    窗体控件是其它控件的容器,所有其它控件都要放到窗体控件中。Form控件的基本语法如下:
    <form action=URL method="POST">
    <!--在此处增加交互控件,例如编辑框、列表框-->
    <input type="submit" value="提交" name="B1">
    <input type="reset" value="全部重写" name="B2">
    </form>
    其中,<form action=URL method="POST">…</form>定义Form控件,action是WEB服务器用响应用户程序的URL,method="POST"是数据用POST方法传到WEB服务器,也可以是get方法。用户用交互控件输入信息。<input type="submit" value="提交" name="B1">是一个按钮控件,其类型为submit,按钮标题为:提交,代表此控件的name为B1。当用户单击此按钮,form控件将把控件内的所有交互控件中的数据用POST方法,传递给action指定WEB服务器的程序处理。<input type="reset" value="全部重写" name="B2">也是一个按钮,用户单击此按钮,将清空form控件内的所有交互控件。
    form控件内可以增加交互控件,交互控件的使用<input>标记,其语法如下:<input align=top|middle|bottom [check] maxlength=? Name=? Size=? Src=? Type=? Value=?>各个属性意义如下:
    align为对齐方式,可以取top、middle和bottom。
    Type为控件类型,可以是:type="submit"为提交按钮。type="reset"为全部重写按钮。type="text"为编辑框。type="password"为口令编辑框。type="checkbox"为复选框。type="radio"为单选框。type="image"为图形。type="hidden"用户不能看到,可以用来传递网页的一些默认的数据。
    [check]只有在type="checkbox"或"radio"时使用,表示缺省被选中。
    Maxlength属性在type="text"编辑框时表示最大字符长度。
    Name属性代表此控件的名字。
    Size属性在type="text"编辑框时表示编辑框最大宽度。
    Value如为按钮,则为标题,为编辑框,则为缺省内容。此值将按Name/Value对的形式传递给WEB服务器。
    9.2.18 例子:文字输入和密码输入
    <html>
    <head>
    <title>Form的例子</title>
    </head>
    <body>
    <form method="POST" action="--WEBBOT-SELF--">
    您的姓名:<input type="text" name="T1" size="20"><br>
    您的主页的网址:<input type="text" name="T2" size="20"><br>
    密码:<input type="password" name="T3" size="20"><br>
    <input type="submit" value="提交"><input type="reset" value="全部重写"><br>
    </form>
    </body>
    </html>
    9.2.19 用FontPage做网页的例子,使用复选框和单选按钮
    用FontPage或Dreamerware要比使用记事本编辑网页方便的多。下边用FontPage做网页的例子。下边是具体步骤:
    (1) 运行FontPage。
    (2) 单击菜单文件/新建/网页。出现新建对话框,选择常规页的普通网页,单击确定按钮,创建一个新网页。
    (3) 单击菜单插入/表单/表单,增加一个新表单(form)。在表单中已有一个提交按钮和一个全部重写按钮。将光标移到提交按钮前后回车,增加form的尺寸。
    (4) 右击提交按钮,在弹出菜单中选中菜单项表单域属性,出现按钮属性对话框,可修改名称(Name属性)属性,值/标签(value属性)=提交查询内容,按钮类型为提交。按确定按钮。用同样方法修改另一个按钮的标题为重置。
    (5) 将光标移到第一行,单击菜单插入/表单/复选框。右击复选框,在弹出菜单中选中菜单项表单域属性,出现复选框属性对话框,修改名称(Name属性)属性=水果1。在复选框后键入字符:Banana。用同样方法增加另两个复选框,注意初始状态修改为选中。
    (6) 用FontPage创建的网页文件如下:
    <html>
    <head>
    <meta http-equiv="Content-Language" content="en-us">
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
    <meta name="GENERATOR" content="Microsoft FrontPage 4.0">
    <meta name="ProgId" content="FrontPage.Editor.Document">
    <title>New Page 2</title>
    </head>
    <body>
    <form method="POST" action="--WEBBOT-SELF--">
    <!--webbot bot="SaveResults" U-File="fpweb:///_private/form_results.txt"
    S-Format="TEXT/CSV" S-Label-Fields="TRUE" -->
    <p><input type="checkbox" name="水果1" value="香蕉">Banana</p>
    <p><input type="checkbox" name="水果2" value="苹果" checked>Apple</p>
    <p><input type="checkbox" name="水果3" value="桔子" checked>Orange</p>
    <p><input type="submit" value="提交查询内容" name="B1"><input type="reset" value="全部重写" name="B2"></p>
    </form>
    </body>
    </html>
    9.3 ASP.NET技术基础
    设计静态网页有两种方法:一种是使用记事本,用HTML语言编写,另一种是使用可视化工具,如FrontPage,Dreamware等。显然,使用可视化工具要方便快捷的多。以往设计服务器端动态网页时,例如ASP,往往只能使用记事本一行一行的写,效率很低。程序员迫切需要一种设计服务器端动态网页的可视化工具,能象使用C#设计Window应用程序一样设计动态网页,使用控件类、属性和事件等面向对象的概念。为了实现这个目的,引入ASP.NET服务器端控件概念。静态网页中一般有一个表单(Form),在表单中可以有多个控件,例如,列表框、编辑框、按钮等等,通过这些控件,完成一定的功能。同样,ASP.NET服务器端控件首先引入运行在服务端WebForm概念,在WebForm中可以放入多个服务器端控件,例如,列表框、编辑框、按钮等等,所有这些控件,都是.NET框架类库中相应类的对象,每个对象都有自己的属性、方法和事件。这些概念和编制Windows应用程序相应的概念基本相同。这些ASP.NET服务器端控件,也使用HTML标记描述,但这些服务器端控件并不传送这些HTML标记给浏览器解释,而是由Web服务器负责解释,翻译为所有浏览器都能解释的标准HTML标记后传送给浏览器解释,这样就极大地简化了服务器端动态网页的设计,也保证了生成的网页的显示效果和浏览器无关。使用ASP.Net技术创建的服务器端动态网页的扩展名为.aspx。
    本节首先介绍ASP.NET服务器端控件基本概念,然后介绍使用记事本编写ASP.NET动态网页的方法,最后介绍如何使用Visual Studio.NET编写ASP.NET动态网页。
    9.3.1 HTML服务器端控件
    ASP.Net中的HTML服务器端控件和标准的HTML控件有着对应关系,但功能更强大。可以在程序中修改HTML服务器端控件的属性,能够在服务器端响应事件,支持数据绑定等。例如增加一个HTML服务器端控件编辑框用如下HTML语句:
    <INPUT TYPE=”TEXT” ID=”Text1” MAXLENGTH=16 RUNAT=”SERVER”/>
    这里和标准的HTML控件的区别是增加了属性RUNAT=”SERVER”。属性ID是代表这个控件的唯一标志,和Winndows应用程序中的控件属性Name的意义是一样的。HTML服务器端控件是为了方便原来学习HTML或ASP编写Web应用程序的程序员而提供的。如果,你以前是Windows应用程序员,建议使用Web服务器端控件,这些控件不但功能更强大,而使用上更象Windows应用程序中的控件,因此学习更方便。因此这里就不介绍HTML服务器端控件了。
    9.3.2 Web服务器端控件
    在ASP.NET系统中,除了常规的HTML控件外,还包括Web服务器端控件。同HTML服务器端控件一样,这些控件可以在程序中修改服务器端控件的属性,能够在服务器端响应事件,支持数据绑定等。例如定义一个Web服务器端控件编辑框控件,方法如下:
    <asp:TextBox id="textBox1" runat="server"/>
    服务器端控件不但功能更强大,而且和编制Windows应用程序中的控件使用方法基本一致,因此学习更方便。本书的所有例子都使用Web服务器端控件。
    9.3.3 Web Form的事件处理
    象Windows应用程序一样,ASP.Net应用程序也采用事件驱动的概念,用户对浏览器的各种操作都被看作事件,事件产生后,Web应用程序用事件处理函数响应事件。但ASP.Net的事件驱动和Windows应用程序的事件驱动有着本质上的区别。Web应用程序的事件产生后,由于事件处理程序在Web服务器端,Web应用程序把事件通过网络使用HTTP协议由浏览器传到Web服务器,在Web服务器执行事件处理程序,把运行结果转变为标准HTML标志的网页,传回浏览器。
    在Web事件处理机制中,每一次Web应用程序响应事件都会使得网页重新生成。事实上,一旦服务器完成某一个网页的处理操作并将它传送至浏览器,则会随即移除该网页的所有信息,也就是说,网页中定义的对象和变量在服务器端已不存在了,网页生命周期结束。当Web应用程序再一次响应事件时,服务器又会将上述处理过程重做一次。基于此原因,我们说网页是无状态的——即网页变量与控件的数据值并不会保留在服务器上。
    因此,我们增加事件函数时,应考虑网络传播的速度的影响,不能象Windows应用程序那样,响应太多的事件。在网页中,每个控件都有属性AutoPosBack,其值为true,事件才能自动调用事件处理函数,如果不希望响应该事件,应将该控件的属性AutoPosBack设为false。
    9.3.4 记事本编写ASP.NET动态网页
    ASP.NET中的服务器端控件也用HTML标记,但这些服务器端控件的HTML标记并不传送给浏览器解释,而是由Web服务器负责解释,翻译为所有浏览器都能解释的标准HTML标记后传送给浏览器解释。所有ASP.NET服务器端控件都要放到Web窗体(WebForm)中,Web窗体(WebForm)也由Web服务器负责解释。下边是一个最简单的使用服务器端控件的例子:
    <%@ Page language="c#" %>
    <html>
    <head>
    <title>这是我的第一个ASP.NET网页</title>
    </head>
    <script language="c#" runat=server>
    void Page_Load(Object src,EventArgs e)
    {Label1.Text="现在的时间是:"+DateTime.Now;}
    void EnterBtn_Click(Object src,EventArgs e)
    {Label1.Text="现在的时间是:"+DateTime.Now;}
    </script>
    <body>
    <form action="e1.aspx" runat=server>
    <asp:Label id="Label1" font-size="14" font-bold="true" forecolor="red" Text=""runat=server>
    </asp:Label>
    <br>
    <asp:button text="查看时间" Onclick="EnterBtn_Click" runat=server/>
    </form>
    </body>
    </html>
    网页文件第一条语句表示网页中使用C#语言。<html>表示网页文件的开始,</html>表示网页文件的的结束,网页的所有内容都应在这两个标记之间。定义在标记<html>和</html>之间的内容被分为三部分,第一部分:<head>和</head>之间可以设定网页的一些信息,<title>和</title>之间的文字显示在IE浏览器的标题栏中。第二部分:<script language="c#" runat=server>和</script>标记之间可以定义方法,变量或对象,language="c#"表示在此标记之间定义的方法使用C#语言,runat=server表示在此标记之间定义的方法运行在Web服务器端,这里定义了两个方法,方法Page_Load()是Web服务器装载本网页时调用的方法,可以在此方法中做一些初始化工作,方法EnterBtn_Click()是"查看时间"按钮的事件函数。第三部分:<body>和</body>之间是网页在浏览器中显示的内容。<form action="e1.aspx" runat=server>和</form>标记定义Web窗体(WebForm),注意runat=server表示Web窗体由Web服务器解释。在Web窗体中增加了两个控件对象,第一个是Label控件,asp:Label表示本控件是Label控件,id相当Windows应用程序中控件的Name属性,用来区分不同对象,runat=server表示次控件由Web服务器解释,其余是设定属性值,注意不同属性用空格分隔。第二个控件是按钮,请注意定义单击事件函数的方法。
    将其以文件名e1.aspx存入d:/asp文件夹,如果d:/asp文件夹已被设定为Web站点,可以在IE的地址栏输入:http://localhost/c411.aspx后,看到这个网页。在浏览器端看不到这些代码,用IE菜单查看/源代码,可以看到用超文本编制的网页。
    9.3.5 用Visual Studio.NET实现ASP.NET动态网页
    用Visual Studio.NET实现上节的例子。具体步骤如下:
    (1) 运Visaul C#后,则进入开始界面,选择新建项目。打开新建项目对话框,在项目类型中选择Visual C#项目,在模板中选择[ASP.NET Web应用程序],指定项目项目放置的位置为http://localhost/e1,这里http://localhost代表当前激活的宿主目录,即将本应用的所有文件存入宿主目录下的文件夹e1中,点击“确定”,生成一个空白窗体(WebForm)。用户可在窗体中放入控件。Visual Studio.NET为我们建立了一个应用项目。
    (2) 向项目中添加控件需要使用[Toolbox]窗口,若看不到,可以用菜单视图/工具箱打开这个窗口。
    (3) 先选中[Toolbox]窗口中[Web窗体]类型下的[Label]条目,然后在设计的窗体中按下鼠标左键并拖动鼠标,画出一个Label控件。该控件用来显示一行文本。
    (4) 使用[Properties]窗口修改Label控件的文本内容和文本字体属性。在右下侧属性窗口中找到[text]属性,把它的值由“Label1”改为”现在的时间是:”;接着在属性窗口中找到[Font]属性,选中Font所在的单元格,单击Font属性左边的