Embed
Email

DotNet_

Document Sample

Shared by: huanghengdong
Categories
Tags
Stats
views:
6
posted:
2/9/2012
language:
pages:
167
1







1. .NET Framework 概念概述

.NET Framework 是 支 持 生 成 和 运 行 下 一 代 应 用 程 序 和 XML Web Services 的 内 部 Windows 组 件 。



.NET Framework 具 有 两 个 主 要 组 件 : 公 共 语 言 运 行 库 和 .NET Framework 类 库 。 公 共 语 言 运 行 库 是 .NET Framework 的 基 础 。 您 可 以 将 运 行 库 看 作 一 个 在 执



行 时 管 理 代 码 的 代 理 ,它 提 供 内 存 管 理 、线 程 管 理 和 远 程 处 理 等 核 心 服 务 ,并 且 还 强 制 实 施 严 格 的 类 型 安 全 以 及 可 提 高 安 全 性 和 可 靠 性 的 其 他 形 式 的 代 码 准 确



性 。 事 实 上 , 代 码 管 理 的 概 念 是 运 行 库 的 基 本 原 则 。 以 运 行 库 为 目 标 的 代 码 称 为 托 管 代 码 , 而 不 以 运 行 库 为 目 标 的 代 码 称 为 非 托 管 代 码 。 .NET Framework 的



另一个主要组件是类库,它是一个综合性的面向对象的可重用类型集合,您可以使用它开发多种应用程序,这些应用程序包括传统的命令行或图形用户界面



(GUI) 应 用 程 序 , 也 包 括 基 于 ASP.NET 所 提 供 的 最 新 创 新 的 应 用 程 序 ( 如 Web 窗 体 和 XML Web Services) 。



公共语言运行库的功能



公共语言运行库管理内存、线程执行、代码执行、代码安全验证、编译以及其他系统服务。这些功能是在公共语言运行库上运行的托管代码所固有的。



至 于 安 全 性 , 取 决 于 包 括 托 管 组 件 的 来 源 ( 如 Internet、 企 业 网 络 或 本 地 计 算 机 ) 在 内 的 一 些 因 素 , 托 管 组 件 被 赋 予 不 同 程 度 的 信 任 。 这 意 味 着 即 使 用 在 同



一活动应用程序中,托管组件既可能能够执行文件访问操作、注册表访问操作或其他须小心使用的功能,也可能不能够执行这些功能。



例 用 但 文

运行库强制实施代码访问安全。 如, 户可以相信嵌入在网页中的可执行文件能够在屏幕上播放动画或唱歌, 不能访问他们的个人数据、 件系统或网络。



这 样 , 运 行 库 的 安 全 性 功 能 就 使 通 过 Internet 部 署 的 合 法 软 件 能 够 具 有 特 别 丰 富 的 功 能 。



运 行 库 还 通 过 实 现 称 为 通 用 类 型 系 统 (CTS) 的 严 格 类 型 验 证 和 代 码 验 证 基 础 结 构 来 加 强 代 码 可 靠 性 。 CTS 确 保 所 有 托 管 代 码 都 是 可 以 自 我 描 述 的 。 各 种



Microsoft 和 第 三 方 语 言 编 译 器 生 成 符 合 CTS 的 托 管 代 码 。 这 意 味 着 托 管 代 码 可 在 严 格 实 施 类 型 保 真 和 类 型 安 全 的 同 时 使 用 其 他 托 管 类 型 和 实 例 。



此 外 ,运 行 库 的 托 管 环 境 还 消 除 了 许 多 常 见 的 软 件 问 题 。例 如 ,运 行 库 自 动 处 理 对 象 布 局 并 管 理 对 对 象 的 引 用 ,在 不 再 使 用 它 们 时 将 它 们 释 放 。这 种 自 动 内 存



管理解决了两个最常见的应用程序错误:内存泄漏和无效内存引用。



运 行 库 还 提 高 了 开 发 人 员 的 工 作 效 率 。例 如 ,程 序 员 可 以 用 他 们 选 择 的 开 发 语 言 编 写 应 用 程 序 ,却 仍 能 充 分 利 用 其 他 开 发 人 员 用 其 他 语 言 编 写 的 运 行 库 、类 库



和 组 件 。 任 何 选 择 以 运 行 库 为 目 标 的 编 译 器 供 应 商 都 可 以 这 样 做 。 以 .NET Framework 为 目 标 的 语 言 编 译 器 使 得 用 该 语 言 编 写 的 现 有 代 码 可 以 使 用 .NET



Framework 的 功 能 , 这 大 大 减 轻 了 现 有 应 用 程 序 的 迁 移 过 程 的 工 作 负 担 。



尽 管 运 行 库 是 为 未 来 的 软 件 设 计 的 , 但 是 它 也 支 持 现 在 和 以 前 的 软 件 。 托 管 和 非 托 管 代 码 之 间 的 互 操 作 性 使 开 发 人 员 能 够 继 续 使 用 所 需 的 COM 组 件 和 DLL。



运 行 库 旨 在 增 强 性 能 。 尽 管 公 共 语 言 运 行 库 提 供 许 多 标 准 运 行 库 服 务 , 但 是 它 从 不 解 释 托 管 代 码 。 一 种 称 为 实 时 (JIT) 编 译 的 功 能 使 所 有 托 管 代 码 能 够 以 它



在其上执行的系统的本机语言运行。同时,内存管理器排除了出现零碎内存的可能性,并增大了内存引用区域以进一步提高性能。



最 后 , 运 行 库 可 由 高 性 能 的 服 务 器 端 应 用 程 序 ( 如 Microsoft® SQL Server™ 和 Internet 信 息 服 务 (IIS)) 承 载 。 此 基 础 结 构 使 您 在 享 受 支 持 运 行 库 宿 主 的



行业最佳企业服务器的优越性能的同时,能够使用托管代码编写业务逻辑。



.NET Framework 类 库



.NET Framework 类 库 是 一 个 与 公 共 语 言 运 行 库 紧 密 集 成 的 可 重 用 的 类 型 集 合 。 该 类 库 是 面 向 对 象 的 , 并 提 供 您 自 己 的 托 管 代 码 可 从 中 导 出 功 能 的 类 型 。 这 不



但 使 .NET Framework 类 型 易 于 使 用 , 而 且 还 减 少 了 学 习 .NET Framework 的 新 功 能 所 需 要 的 时 间 。 此 外 , 第 三 方 组 件 可 与 .NET Framework 中 的 类 无 缝 集



成。







2. 托管执行过程

托管执行过程包括下列步骤:



1.选 择 编 译 器 。



为获得公共语言运行库提供的优点,必须使用一个或多个针对运行库的语言编译器。



2.将 代 码 编 译 为 Microsoft 中 间 语 言 (MSIL)。



编 译 将 源 代 码 翻 译 为 MSIL 并 生 成 所 需 的 元 数 据 。



3.将 MSIL 编 译 为 本 机 代 码 。



在 执 行 时 , 实 时 (JIT) 编 译 器 将 MSIL 翻 译 为 本 机 代 码 。 在 此 编 译 过 程 中 , 代 码 必 须 通 过 验 证 过 程 , 该 过 程 检 查 MSIL 和 元 数 据 以 查 看 是 否 可 以 将 代 码 确



定为类型安全。



4.运 行 代 码 。



公共语言运行库提供使执行能够发生以及可在执行期间使用的各种服务的结构。







3. 扩展方法

扩 展 方 法 使 您 能 够 向 现 有 类 型 “ 添 加 ” 方 法 ,而 无 需 创 建 新 的 派 生 类 型 、重 新 编 译 或 以 其 他 方 式 修 改 原 始 类 型 。扩 展 方 法 是 一 种 特 殊 的 静 态 方 法 ,但 可 以 像 扩



展 类 型 上 的 实 例 方 法 一 样 进 行 调 用 。 对 于 用 C# 和 Visual Basic 编 写 的 客 户 端 代 码 , 调 用 扩 展 方 法 与 调 用 在 类 型 中 实 际 定 义 的 方 法 之 间 没 有 明 显 的 差 异 。



最 常 见 的 扩 展 方 法 是 LINQ 标 准 查 询 运 算 符 , 这 些 运 算 符 在 现 有 System.Collections.IEnumerable 和 System.Collections.Generic.IEnumerable 类 型



中添加了查询功能。若要使用这些标准查询运算符,请先使用 using System.Linq 指 令 将 它 们 纳 入 范 围 中 。 然 后 , 任 何 实 现 了 IEnumerable 的 类 型

看 起 来 都 具 有 GroupBy、 OrderBy、 Average 等 实 例 方 法 。 在 IEnumerable)>) 类 型 的 实 例 ( 如 List 或 Array) 后 键 入 “ 点 ” 时 , 可 以 在



IntelliSense 语 句 结 束 中 看 到 这 些 附 加 方 法 。

2



可 以 使 用 扩 展 方 法 来 扩 展 类 或 接 口 ,但 不 能 重 写 扩 展 方 法 。与 接 口 或 类 方 法 具 有 相 同 名 称 和 签 名 的 扩 展 方 法 永 远 不 会 被 调 用 。编 译 时 ,扩 展 方 法 的 优 先 级 总 是



比类型本身中定义的实例方法低。换句话说,如果某个类型具有一个名为 Process(int i) 的方法,而您有一个具有相同签名的扩展方法,则编译器



总 是 绑 定 到 该 实 例 方 法 。当 编 译 器 遇 到 方 法 调 用 时 ,它 首 先 在 该 类 型 的 实 例 方 法 中 寻 找 匹 配 的 方 法 。如 果 未 找 到 任 何 匹 配 方 法 ,编 译 器 将 搜 索 为 该 类 型 定 义 的



任何扩展方法,并且绑定到它找到的第一个扩展方法。下面的示例演示编译器如何确定要绑定到哪个扩展方法或实例方法。







定义和调用扩展方法



定义一个静态 类以包含扩展方法。



该 类 必 须 对 客 户 端 代 码 可 见 。 有 关 可 访 问 性 规 则 的 更 多 信 息 , 请 参 见 访 问 修 饰 符 ( C# 编 程 指 南 ) 。



将该扩展方法实现为静态方法,并使其至少具有与包含类相同的可见性。



该 方 法 的 第 一 个 参 数 指 定 方 法 所 操 作 的 类 型 ; 该 参 数 必 须 以 this 修 饰 符 开 头 。



在 调 用 代 码 中 , 添 加 一 条 using 指 令 以 指 定 包 含 扩 展 方 法 类 的 命 名 空 间 。



按照与调用类型上的实例方法一样的方式调用扩展方法。



请注意,第一个参数不是由调用代码指定的,因为它表示正应用运算符的类型,并且编译器已经知道对象的类型。您只需通过 n 为这两个形参提供实参。

下面的示例在 MyExtensions.StringExtension 类中实现了一个名为 WordCount 的 扩 展 方 法 。该 方 法 对 String 类 进 行 操 作 ,而 该 类被



指 定 为 第 一 个 方 法 参 数 。 MyExtensions 命 名 空 间 被 导 入 到 应 用 程 序 命 名 空 间 中 , 并 且 该 方 法 是 在 Main 方法内调用的。



using System.Linq;



using System.Text;



using System;



namespace CustomExtensions



{



//Extension methods must be defined in a static class



public static class StringExtension



{



// This is the extension method.



// The first parameter takes the "this" modifier



// and specifies the type for which the method is defined.



public static int WordCount(this String str)



{



return str.Split(new char[] {' ', '.','?'}, StringSplitOptions.RemoveEmptyEntries).Length;



}



}



}



namespace Extension_Methods_Simple



{



//Import the extension method namespace.



using CustomExtensions;



class Program



{



static void Main(string[] args)



{



string s = "The quick brown fox jumped over the lazy dog." ;



// Call the method as if it were an



// instance method on the type. Note that the first



// parameter is not specified by the calling code.



int i = s.WordCount();



System.Console.WriteLine("Word count of s is {0}", i);



}



}

3









4. A 类是 B 类的基类,并且都有自己的构造,析构函数,请举例证明 B 类从实例化到消亡过程中构

造,析构函数的执行过程.请附 code

父 类 若 没 有 默 认 构 造 ,子 类 必 须 在 其 构 造 函 数 内 显 式 调 用 父 类 的 带 参 构 造 才 能 保 证 子 类 对 象 的 顺 利 实 例 化 .



类 层 层 派 生 ,在 实 例 化 的 时 候 构 造 函 数 的 调 用 顺 序 是 怎 样 的 ? --从 顶 层 基 类 开 始 向 子 类 方 向 顺 序 调 用 无 参 构 造 .



默 认 构 造 (无 参 构 造 )和 带 参 构 造 什 么 时 候 调 用 ?--默 认 将 从 顶 层 父 类 的 默 认 构 造 一 直 调 用 到 当 前 类 的 默 认 构 造 .



下 面 是 C#继 承 构 造 函 数 实 现 及 调 用 示 例 :



/*



若 希 望 类 能 够 有 派 生 类 ,必 须 为 其 实 现 默 认 构 造 函 数 .



若 类 没 有 实 现 带 参 构 造 ,编 译 器 将 自 动 创 建 默 认 构 造 函 数 .



若 类 实 现 了 带 参 构 造 ,则 编 译 器 不 会 自 动 生 成 默 认 构 造 .



*/



using System; //C#继 承 构 造 函 数 实 现 及 调 用



namespace xumh



{



public class MyClass



{



public MyClass()



{



Console.WriteLine("MyClass:默 认 构 造 函 数 ");



}



public MyClass(int a, int b)



{



Console.WriteLine("MyClass 带 参 构 造 :a={0}, b={1}.", a, b);



}



}







public class MyClass2 : MyClass



{



public MyClass2()



{



Console.WriteLine("MyClass2:默 认 构 造 函 数 ");



}



public MyClass2(int a, int b)



{



Console.WriteLine("MyClass2 带 参 构 造 :a={0}, b={1}.", a, b);



}



}



//C#继 承 构 造 函 数 实 现 及 调 用



public class MyClass3 : MyClass2



{







public MyClass3()



{



Console.WriteLine("MyClass3:默 认 构 造 函 数 ");



}



public MyClass3(int a, int b)



{

4



Console.WriteLine("MyClass3 带 参 构 造 :a={0}, b={1}.", a, b);



}



}







public class runMyApp



{



static void Main()



{



MyClass3 my = new MyClass3(3,4);



}



}



} //C#继 承 构 造 函 数 实 现 及 调 用



/**//*--===------------------------------------------ ===---



输出如下:



MyClass:默 认 构 造 函 数



MyClass2:默 认 构 造 函 数



MyClass3 带 参 构 造 :a=3, b=4.



--===------------------------------------------===---*/







5. 什么叫应用程序域?

在 .NET 平 台 下 , 程 序 集 并 没 有 直 接 承 载 在 进 程 中 ( 而 传 统 的 win32 程 序 是 直 接 承 载 的 ) 。 实 际 上 .NET 可 执 行 程 序 承 载 在 进 程 的 一 个 逻 辑 分 区 中 , 术 语 称 为 应



用 程 序 域 ( 也 称 AppDomain)。 可 见 , 一 个 进 程 可 以 包 含 多 个 应 用 程 序 域 , 每 一 个 应 用 程 序 域 中 承 载 一 个 .NET 可 执 行 程 序 , 这 样 的 好 处 如 下 :



应 用 程 序 域 是 .NET 平 台 操 作 系 统 独 立 性 的 关 键 特 性 。 这 种 逻 辑 分 区 将 不 同 操 作 系 统 加 载 可 执 行 程 序 的 差 异 抽 象 化 了 。



和 一 个 完 整 的 进 程 相 比 , 应 用 程 序 域 的 CPU 和 内 存 占 用 要 小 的 多 。 因 此 CLR 加 载 和 卸 载 应 用 程 序 域 比 起 完 整 的 进 程 来 说 也 快 的 多 。



应用程序域为承载的应用程序提供了深度隔离。如果进程中一个应用程序域 失败了,其他的应用程序域也能保持正常 。



AppDomain 的 主 要 成 员 :



CreateDomain():该 静 态 方 法 在 当 前 进 程 中 创 建 一 个 新 的 应 用 程 序 域 。 由 于 CLR 能 够 根 据 需 要 创 建 应 用 程 序 域 , 所 以 必 须 调 用 这 个 方 法 的 机 会 很 少 。



GetCurrentThreadId():该 静 态 方 法 返 回 当 前 应 用 程 序 域 上 活 动 的 线 程 ID。



UnLoad():该 静 态 方 法 在 进 程 中 卸 载 指 定 的 应 用 程 序 域 。



BaseDirectory:获 取 基 目 录 , 该 目 录 用 于 探 测 相 关 的 程 序 集 。



CreateInstance():在 指 定 程 序 集 文 件 中 创 建 指 定 类 型 的 新 实 例 。



ExecuteAssembly():根 据 文 件 名 在 应 用 程 序 域 中 执 行 程 序 集 。



GetAssemblies():获 取 已 加 载 到 此 应 用 程 序 域 中 的 .NET 程 序 集 ( 基 于 COM 和 C 的 二 进 制 文 件 除 外 ) 。



Load():动 态 加 载 程 序 集 到 当 前 应 用 程 序 域 。







6. 进程与线程的关系

进 程 是 具 有 一 定 独 立 功 能 的 程 序 关 于 某 个 数 据 集 合 上 的 一 次 运 行 活 动 ,进 程 是 系 统 进 行 资 源 分 配 和 调 度 的 一 个 独 立 单 位 。



线 程 是 CPU 调 度 和 分 派 的 基 本 单 位 ,它 是 比 进 程 更 小 的 能 独 立 运 行 的 基 本 单 位 .线 程 自 己 基 本 上 不 拥 有 系 统 资 源 ,只 拥 有 一 点 在 运 行 中 必 不 可 少 的 资 源 (如 程 序 计



数 器 ,一 组 寄 存 器 和 栈 ),但 是 它 可 与 同 属 一 个 进 程 的 其 他 的 线 程 共 享 进 程 所 拥 有 的 全 部 资 源 。



一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。







进 程 和 线 程 的 主 要 差 别 在 于 它 们 是 不 同 的 操 作 系 统 资 源 管 理 方 式 。进 程 有 独 立 的 地 址 空 间 ,一 个 进 程 崩 溃 后 ,在 保 护 模 式 下 不 会 对 其 它 进 程 产 生 影 响 ,而 线 程



只 是 一 个 进 程 中 的 不 同 执 行 路 径 。线 程 有 自 己 的 堆 栈 和 局 部 变 量 ,但 线 程 之 间 没 有 单 独 的 地 址 空 间 ,一 个 线 程 死 掉 就 等 于 整 个 进 程 死 掉 ,所 以 多 进 程 的 程 序 要



比 多 线 程 的 程 序 健 壮 ,但 在 进 程 切 换 时 ,耗 费 资 源 较 大 ,效 率 要 差 一 些 。但 对 于 一 些 要 求 同 时 进 行 并 且 又 要 共 享 某 些 变 量 的 并 发 操 作 ,只 能 用 线 程 ,不 能 用 进



程。









7. 进程间通信的几种方式

1 文件映射



文 件 映 射 (Memory-Mapped Files)能 使 进 程 把 文 件 内 容 当 作 进 程 地 址 区 间 一 块 内 存 那 样 来 对 待 。 因 此 , 进 程 不 必 使 用 文 件 I/O 操 作 , 只 需 简 单 的 指 针 操 作

5



就可读取和修改文件的内容。



Win32 API 允 许 多 个 进 程 访 问 同 一 文 件 映 射 对 象 , 各 个 进 程 在 它 自 己 的 地 址 空 间 里 接 收 内 存 的 指 针 。 通 过 使 用 这 些 指 针 , 不 同 进 程 就 可 以 读 或 修 改 文 件 的



内容,实现了对文件中数据的共享。



应用程序有三种方法来使多个进程共享一个文件映射对象。



(1)继 承 : 第 一 个 进 程 建 立 文 件 映 射 对 象 , 它 的 子 进 程 继 承 该 对 象 的 句 柄 。



(2)命 名 文 件 映 射 : 第 一 个 进 程 在 建 立 文 件 映 射 对 象 时 可 以 给 该 对 象 指 定 一 个 名 字 (可 与 文 件 名 不 同 )。 第 二 个 进 程 可 通 过 这 个 名 字 打 开 此 文 件 映 射 对 象 。



另 外 , 第 一 个 进 程 也 可 以 通 过 一 些 其 它 IPC 机 制 (有 名 管 道 、 邮 件 槽 等 )把 名 字 传 给 第 二 个 进 程 。



(3)句 柄 复 制 : 第 一 个 进 程 建 立 文 件 映 射 对 象 , 然 后 通 过 其 它 IPC 机 制 (有 名 管 道 、 邮 件 槽 等 )把 对 象 句 柄 传 递 给 第 二 个 进 程 。 第 二 个 进 程 复 制 该 句 柄 就 取



得对该文件映射对象的访问权限。



文 件 映 射 是 在 多 个 进 程 间 共 享 数 据 的 非 常 有 效 方 法 ,有 较 好 的 安 全 性 。但 文 件 映 射 只 能 用 于 本 地 机 器 的 进 程 之 间 ,不 能 用 于 网 络 中 ,而 开 发 者 还 必 须 控 制



进程间的同步。



2 共享内存



Win32 API 中 共 享 内 存 (Shared Memory)实 际 就 是 文 件 映 射 的 一 种 特 殊 情 况 。 进 程 在 创 建 文 件 映 射 对 象 时 用 0xFFFFFFFF 来 代 替 文 件 句 柄 (HANDLE), 就 表 示



了 对 应 的 文 件 映 射 对 象 是 从 操 作 系 统 页 面 文 件 访 问 内 存 ,其 它 进 程 打 开 该 文 件 映 射 对 象 就 可 以 访 问 该 内 存 块 。由 于 共 享 内 存 是 用 文 件 映 射 实 现 的 ,所 以 它 也 有



较好的安全性,也只能运行于同一计算机上的进程之间。



3 匿名管道



管 道 (Pipe)是 一 种 具 有 两 个 端 点 的 通 信 通 道 : 有 一 端 句 柄 的 进 程 可 以 和 有 另 一 端 句 柄 的 进 程 通 信 。 管 道 可 以 是 单 向 - 一 端 是 只 读 的 , 另 一 端 点 是 只 写 的 ;



也可以是双向的一管道的两端点既可读也可写。



匿 名 管 道 (Anonymous Pipe)是 在 父 进 程 和 子 进 程 之 间 , 或 同 一 父 进 程 的 两 个 子 进 程 之 间 传 输 数 据 的 无 名 字 的 单 向 管 道 。 通 常 由 父 进 程 创 建 管 道 , 然 后 由 要



通 信 的 子 进 程 继 承 通 道 的 读 端 点 句 柄 或 写 端 点 句 柄 ,然 后 实 现 通 信 。父 进 程 还 可 以 建 立 两 个 或 更 多 个 继 承 匿 名 管 道 读 和 写 句 柄 的 子 进 程 。这 些 子 进 程 可 以 使 用



管道直接通信,不需要通过父进程。



匿 名 管 道 是 单 机 上 实 现 子 进 程 标 准 I/O 重 定 向 的 有 效 方 法 , 它 不 能 在 网 上 使 用 , 也 不 能 用 于 两 个 不 相 关 的 进 程 之 间 。



4 命名管道



命 名 管 道 (Named Pipe)是 服 务 器 进 程 和 一 个 或 多 个 客 户 进 程 之 间 通 信 的 单 向 或 双 向 管 道 。 不 同 于 匿 名 管 道 的 是 命 名 管 道 可 以 在 不 相 关 的 进 程 之 间 和 不 同 计



算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。



使 不

命名管道提供了相对简单的编程接口, 通过网络传输数据并不比同一计算机上两进程之间通信更困难, 过如果要同时和多个进程通信它就力不从心了。



5 邮件槽



邮 件 槽 (Mailslots)提 供 进 程 间 单 向 通 信 能 力 , 任 何 进 程 都 能 建 立 邮 件 槽 成 为 邮 件 槽 服 务 器 。 其 它 进 程 , 称 为 邮 件 槽 客 户 , 可 以 通 过 邮 件 槽 的 名 字 给 邮 件



槽 服 务 器 进 程 发 送 消 息 。进 来 的 消 息 一 直 放 在 邮 件 槽 中 ,直 到 服 务 器 进 程 读 取 它 为 止 。一 个 进 程 既 可 以 是 邮 件 槽 服 务 器 也 可 以 是 邮 件 槽 客 户 ,因 此 可 建 立 多 个



邮件槽实现进程间的双向通信。



通 过 邮 件 槽 可 以 给 本 地 计 算 机 上 的 邮 件 槽 、其 它 计 算 机 上 的 邮 件 槽 或 指 定 网 络 区 域 中 所 有 计 算 机 上 有 同 样 名 字 的 邮 件 槽 发 送 消 息 。广 播 通 信 的 消 息 长 度 不



能 超 过 400 字 节 , 非 广 播 消 息 的 长 度 则 受 邮 件 槽 服 务 器 指 定 的 最 大 消 息 长 度 的 限 制 。



邮 件 槽 与 命 名 管 道 相 似 , 不 过 它 传 输 数 据 是 通 过 不 可 靠 的 数 据 报 (如 TCP/IP 协 议 中 的 UDP 包 )完 成 的 , 一 旦 网 络 发 生 错 误 则 无 法 保 证 消 息 正 确 地 接 收 , 而



命 名 管 道 传 输 数 据 则 是 建 立 在 可 靠 连 接 基 础 上 的 。不 过 邮 件 槽 有 简 化 的 编 程 接 口 和 给 指 定 网 络 区 域 内 的 所 有 计 算 机 广 播 消 息 的 能 力 ,所 以 邮 件 槽 不 失 为 应 用 程



序发送和接收消息的另一种选择。



6 剪贴板



剪 贴 板 (Clipped Board)实 质 是 Win32 API 中 一 组 用 来 传 输 数 据 的 函 数 和 消 息 , 为 Windows 应 用 程 序 之 间 进 行 数 据 共 享 提 供 了 一 个 中 介 , Windows 已 建 立 的



剪 切 (复 制 )- 粘 贴 的 机 制 为 不 同 应 用 程 序 之 间 共 享 不 同 格 式 数 据 提 供 了 一 条 捷 径 。当 用 户 在 应 用 程 序 中 执 行 剪 切 或 复 制 操 作 时 ,应 用 程 序 把 选 取 的 数 据 用 一 种



或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择适合自己的格式。



剪 贴 板 是 一 个 非 常 松 散 的 交 换 媒 介 ,可 以 支 持 任 何 数 据 格 式 ,每 一 格 式 由 一 无 符 号 整 数 标 识 ,对 标 准 (预 定 义 )剪 贴 板 格 式 ,该 值 是 Win32 API 定 义 的 常 量 ;



对 非 标 准 格 式 可 以 使 用 Register Clipboard Format 函 数 注 册 为 新 的 剪 贴 板 格 式 。 利 用 剪 贴 板 进 行 交 换 的 数 据 只 需 在 数 据 格 式 上 一 致 或 都 可 以 转 化 为 某 种 格 式



就 行 。 但 剪 贴 板 只 能 在 基 于 Windows 的 程 序 中 使 用 , 不 能 在 网 络 上 使 用 。



7 动态数据交换



动 态 数 据 交 换 (DDE)是 使 用 共 享 内 存 在 应 用 程 序 之 间 进 行 数 据 交 换 的 一 种 进 程 间 通 信 形 式 。 应 用 程 序 可 以 使 用 DDE 进 行 一 次 性 数 据 传 输 , 也 可 以 当 出 现 新



数据时,通过发送更新值在应用程序间动态交换数据。



DDE 和 剪 贴 板 一 样 既 支 持 标 准 数 据 格 式 (如 文 本 、 位 图 等 ), 又 可 以 支 持 自 己 定 义 的 数 据 格 式 。 但 它 们 的 数 据 传 输 机 制 却 不 同 , 一 个 明 显 区 别 是 剪 贴 板 操 作



几 乎 总 是 用 作 对 用 户 指 定 操 作 的 一 次 性 应 答 - 如 从 菜 单 中 选 择 Paste 命 令 。 尽 管 DDE 也 可 以 由 用 户 启 动 , 但 它 继 续 发 挥 作 用 一 般 不 必 用 户 进 一 步 干 预 。 DDE 有



三种数据交换方式:



(1) 冷 链 : 数 据 交 换 是 一 次 性 数 据 传 输 , 与 剪 贴 板 相 同 。

6



(2) 温 链 : 当 数 据 交 换 时 服 务 器 通 知 客 户 , 然 后 客 户 必 须 请 求 新 的 数 据 。



(3) 热 链 : 当 数 据 交 换 时 服 务 器 自 动 给 客 户 发 送 数 据 。



DDE 交 换 可 以 发 生 在 单 机 或 网 络 中 不 同 计 算 机 的 应 用 程 序 之 间 。 开 发 者 还 可 以 定 义 定 制 的 DDE 数 据 格 式 进 行 应 用 程 序 之 间 特 别 目 的 IPC, 它 们 有 更 紧 密 耦



合 的 通 信 要 求 。 大 多 数 基 于 Windows 的 应 用 程 序 都 支 持 DDE。



8 对象连接与嵌入



应 用 程 序 利 用 对 象 连 接 与 嵌 入 (OLE)技 术 管 理 复 合 文 档 (由 多 种 数 据 格 式 组 成 的 文 档 ),OLE 提 供 使 某 应 用 程 序 更 容 易 调 用 其 它 应 用 程 序 进 行 数 据 编 辑 的 服 务 。



例 如 , OLE 支 持 的 字 处 理 器 可 以 嵌 套 电 子 表 格 , 当 用 户 要 编 辑 电 子 表 格 时 OLE 库 可 自 动 启 动 电 子 表 格 编 辑 器 。 当 用 户 退 出 电 子 表 格 编 辑 器 时 , 该 表 格 已 在 原 始



字 处 理 器 文 档 中 得 到 更 新 。 在 这 里 电 子 表 格 编 辑 器 变 成 了 字 处 理 器 的 扩 展 , 而 如 果 使 用 DDE, 用 户 要 显 式 地 启 动 电 子 表 格 编 辑 器 。



同 DDE 技 术 相 同 , 大 多 数 基 于 Windows 的 应 用 程 序 都 支 持 OLE 技 术 。



9 动态连接库



Win32 动 态 连 接 库 (DLL)中 的 全 局 数 据 可 以 被 调 用 DLL 的 所 有 进 程 共 享 , 这 就 又 给 进 程 间 通 信 开 辟 了 一 条 新 的 途 径 , 当 然 访 问 时 要 注 意 同 步 问 题 。



虽 然 可 以 通 过 DLL 进 行 进 程 间 数 据 共 享 , 但 从 数 据 安 全 的 角 度 考 虑 , 我 们 并 不 提 倡 这 种 方 法 , 使 用 带 有 访 问 权 限 控 制 的 共 享 内 存 的 方 法 更 好 一 些 。



10 远 程 过 程 调 用



Win32 API 提 供 的 远 程 过 程 调 用 (RPC)使 应 用 程 序 可 以 使 用 远 程 调 用 函 数 , 这 使 在 网 络 上 用 RPC 进 行 进 程 通 信 就 像 函 数 调 用 那 样 简 单 。 RPC 既 可 以 在 单 机 不



同进程间使用也可以在网络中使用。



由 于 Win32 API 提 供 的 RPC 服 从 OSF-DCE(Open Software Foundation Distributed Computing Environment) 标 准 。 所 以 通 过 Win32 API 编 写 的 RPC 应 用



程 序 能 与 其 它 操 作 系 统 上 支 持 DEC 的 RPC 应 用 程 序 通 信 。 使 用 RPC 开 发 者 可 以 建 立 高 性 能 、 紧 密 耦 合 的 分 布 式 应 用 程 序 。



11 NetBios 函 数



Win32 API 提 供 NetBios 函 数 用 于 处 理 低 级 网 络 控 制 , 这 主 要 是 为 IBM NetBios 系 统 编 写 与 Windows 的 接 口 。 除 非 那 些 有 特 殊 低 级 网 络 功 能 要 求 的 应 用 程



序 , 其 它 应 用 程 序 最 好 不 要 使 用 NetBios 函 数 来 进 行 进 程 间 通 信 。



12 Sockets



Windows Sockets 规 范 是 以 U.C.Berkeley 大 学 BSD UNIX 中 流 行 的 Socket 接 口 为 范 例 定 义 的 一 套 Windows 下 的 网 络 编 程 接 口 。 除 了 Berkeley Socket 原 有



的 库 函 数 以 外 , 还 扩 展 了 一 组 针 对 Windows 的 函 数 , 使 程 序 员 可 以 充 分 利 用 Windows 的 消 息 机 制 进 行 编 程 。



现 在 通 过 Sockets 实 现 进 程 通 信 的 网 络 应 用 越 来 越 多 ,这 主 要 的 原 因 是 Sockets 的 跨 平 台 性 要 比 其 它 IPC 机 制 好 得 多 ,另 外 WinSock 2.0 不 仅 支 持 TCP/IP



协 议 , 而 且 还 支 持 其 它 协 议 (如 IPX)。 Sockets 的 唯 一 缺 点 是 它 支 持 的 是 底 层 通 信 操 作 , 这 使 得 在 单 机 的 进 程 间 进 行 简 单 数 据 传 递 不 太 方 便 , 这 时 使 用 下 面 将



介 绍 的 WM_COPYDATA 消 息 将 更 合 适 些 。



13 WM_COPYDATA 消 息



当 发 参

WM_COPYDATA 是 一 种 非 常 强 大 却 鲜 为 人 知 的 消 息 。 一 个 应 用 向 另 一 个 应 用 传 送 数 据 时 , 送 方 只 需 使 用 调 用 SendMessage 函 数 , 数 是 目 的 窗 口 的 句 柄 、



传 递 数 据 的 起 始 地 址 、 WM_COPYDATA 消 息 。 接 收 方 只 需 像 处 理 其 它 消 息 那 样 处 理 WM_COPY DATA 消 息 , 这 样 收 发 双 方 就 实 现 了 数 据 共 享 。



WM_COPYDATA 是 一 种 非 常 简 单 的 方 法 , 它 在 底 层 实 际 上 是 通 过 文 件 映 射 来 实 现 的 。 它 的 缺 点 是 灵 活 性 不 高 , 并 且 它 只 能 用 于 Windows 平 台 的 单 机 环 境 下 。









8. 什么是托管代码?

托 管 代 码 (managed code)由 公 共 语 言 运 行 库 环 境 CLR( 而 不 是 直 接 由 操 作 系 统 )执 行 的 代 码 。托 管 代 码 应 用 程 序 可 以 获 得 公 共 语 言 运 行 库 服 务 ,例 如 自 动 垃 圾



回收、运行库类型检查和安全支持等。



托 管 代 码 由 .net runtime 来 执 行 。 反 之 就 是 非 托 管 。



.net runtime 有 很 多 好 处 :



1。 安 全 性 ,像 数 组 越 界 , 类 型 安 全 什 么 的 。



2。 语 言 互 操 作 性 , 可 以 使 用 多 种 语 言 。



3。 易 用 性 , 垃 圾 收 集 。



托 管 代 码 在 Framework 下 运 行 , 底 层 的 交 互 由 framework 做 。



非 托 管 代 码 就 是 脱 离 Framework 的 控 制 , 同 底 层 交 互 , 资 源 则 需 要 手 动 去 释 放







9. 什么是强类型系统?

C# 是 强 类 型 语 言 ; 因 此 每 个 变 量 和 对 象 都 必 须 具 有 声 明 类 型 。







10. 什么是装箱拆箱?

.NET 的 所 有 类 型 都 是 由 基 类 System.Object 继 承 过 来 的 , 包 括 最 常 用 的 基 础 类 型 : int, byte, short, bool 等 等 , 就 是 说 所 有 的 事 物 都 是 对 象 。 如 果 申 明 这

7





些 类 型 得 时 候 都 在 堆 (HEAP)中 分 配 内 存 , 造 成 极 低 的 效 率 .正 是 通 过 将 类 型 分 成 值 型 (value)和 引 用 型 (regerencetype), (

C#中 定 义 的 值 类 型 包 括 原 类 型 Sbyte、



Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚 举 (enum)、结 构 (struct),引 用 类 型 包 括 :类 、数 组 、接 口 、



委托、字符串等。



值 型 就 是 在 栈 中 分 配 内 存 , 在 申 明 的 同 时 就 初 始 化 , 以 确 保 数 据 不 为 NULL;



引 用 型 是 在 堆 中 分 配 内 存 , 初 始 化 为 null, 引 用 型 是 需 要 GC 来 回 收 内 存 的 , 值 型 不 用 , 超 出 了 值 类 型 作 用 范 围 , 系 统 就 会 自 动 释 放 .



下面就来说装箱和拆箱的定义!



装箱就是隐式的将一个值型转换为引用型对象。比如:



int i=0;



Syste.Object obj=i;



拆箱就是将一个引用型对象转换成任意值型!比如:



int i=0;



System.Object obj=i;



int j=(int)obj;



再写个代码,看看进行了几次装拆箱!



int i=0;



System.Object obj=i;



Console.WriteLine(i+","+(int)obj);



其中共发生了 3 次装箱和一次拆箱.



第 一 次 是 将 i 装 箱 ,第 2 次 是 输 出 的 时 候 将 i 转 换 成 string 类 型 ,而 string 类 型 为 引 用 类 型 ,即 又 是 装 箱 ,第 三 次 装 箱 就 是 (int)obj 的 转 换 成 string 类 型 ,



装箱!



拆 箱 就 是 (int)obj, 将 obj 拆 箱 .







11. Lambda 表达式

“Lambda 表 达 式 ”是 一 个 匿 名 函 数 , 它 可 以 包 含 表 达 式 和 语 句 , 并 且 可 用 于 创 建 委 托 或 表 达 式 目 录 树 类 型 。



所 有 Lambda 表 达 式 都 使 用 Lambda 运 算 符 =>, 该 运 算 符 读 为 “ goes to” 。 该 Lambda 运 算 符 的 左 边 是 输 入 参 数 ( 如 果 有 ) , 右 边 包 含 表 达 式 或 语 句 块 。



Lambda 表 达 式 x => x * x 读 作 “ x goes to x times x” 。 可 以 将 此 表 达 式 分 配 给 委 托 类 型 , 如 下 所 示 :



delegate int del(int i);



del myDelegate = x => x * x;



int j = myDelegate(5); //j = 25







创建表达式目录树类型:



using System.Linq.Expressions;



// ...



Expression = x => x * x;



=> 运 算 符 具 有 与 赋 值 运 算 符 (=) 相 同 的 优 先 级 , 并 且 是 右 结 合 运 算 符 。



Lambda 用 在 基 于 方 法 的 LINQ 查 询 中 , 作 为 诸 如 Where 和 Where 等 标 准 查 询 运 算 符 方 法 的 参 数 。



使 用 基 于 方 法 的 语 法 在 Enumerable 类 中 调 用 Where 方 法 时 ( 像 在 LINQ to Objects 和 LINQ to XML 中 那 样 ) , 参 数 是 委 托 类 型 System. Func。 使 用 Lambda 表 达 式 创 建 委 托 最 为 方 便 。



在 前 面 的 示 例 中 ,请 注 意 委 托 签 名 具 有 一 个 int 类 型 的 隐 式 类 型 输 入 参 数 ,并 返 回 int。可 以 将 Lambda 表 达 式 转 换 为 该 类 型 的 委 托 ,因 为 该 表 达 式 也 具 有



一 个 输 入 参 数 ( x ), 以 及 一 个 编 译 器 可 隐 式 转 换 为 int 类 型 的 返 回 值 。 使 用 输 入 参 数 5 调 用 委 托 时 , 它 将 返 回 结 果 25。



在 is 或 as 运 算 符 的 左 侧 不 允 许 使 用 Lambda。



适 用 于 匿 名 方 法 的 所 有 限 制 也 适 用 于 Lambda 表 达 式 。



Lambda 表 达 式



表 达 式 在 右 边 的 Lambda 表 达 式 称 为 “ Lambda 表 达 式 ” 。 Lambda 表 达 式 在 构 造 表 达 式 目 录 树 时 广 泛 使 用 。 Lambda 表 达 式 返 回 表 达 式 的 结 果 , 并 采 用 以 下



基本形式:



(input parameters) => expression



只 有 在 Lambda 有 一 个 输 入 参 数 时 , 括 号 才 是 可 选 的 ; 否 则 括 号 是 必 需 的 。 两 个 或 更 多 输 入 参 数 由 括







在括号中的逗号分隔:

8



(x, y) => x == y



有时,编译器难于或无法推断输入类型。如果出现这种情况,您可以按以下示例中所示方式显式指定类型:



(int x, string s) => s.Length > x







使用空括号指定零个输入参数:



() => SomeMethod()



在 上 一 个 示 例 中 ,请 注 意 Lambda 表 达 式 的 主 体 可 以 包 含 方 法 调 用 。但 是 ,如 果 要 创 建 将 在 另 一 个 域( 比 如 SQL Server)中 使 用 的 表 达 式 目 录 树 ,则 不 应



在 Lambda 表 达 式 中 使 用 方 法 调 用 。 方 法 在 .NET 公 共 语 言 运 行 时 上 下 文 的 外 部 将 没 有 意 义 。







Lambda 语 句



Lambda 语 句 与 Lambda 表 达 式 类 似 , 只 是 语 句 括 在 大 括 号 中 :



(input parameters) => {statement;}



Lambda 语 句 的 主 体 可 以 包 含 任 意 数 量 的 语 句 ; 但 是 , 实 际 上 通 常 不 会 多 于 两 个 或 三 个 语 句 。



delegate void TestDelegate(string s);







TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };



myDel("Hello");



像 匿 名 方 法 一 样 , Lambda 语 句 无 法 用 于 创 建 表 达 式 目 录 树 。







Lambda 中 的 类 型 推 理



在 编 写 Lambda 时 , 通 常 不 必 为 输 入 参 数 指 定 类 型 , 因 为 编 译 器 可 以 基 于 Lambda 主 体 、 基 础 委 托 类 型 以 及 C# 3.0 语 言 规 范 中 描 述 的 其 他 因 素 推 断 类



型 。 对 于 大 多 数 标 准 查 询 运 算 符 , 第 一 个 输 入 是 源 序 列 中 的 元 素 的 类 型 。 因 此 , 如 果 要 查 询 IEnumerable , 则 输 入 变 量 将 被 推 断 为



Customer 对象,这意味着您可以访问其方法和属性:



customers.Where(c => c.City == "London");



Lambda 的 一 般 规 则 如 下 :



Lambda 包 含 的 参 数 数 量 必 须 与 委 托 类 型 包 含 的 参 数 数 量 相 同 。



Lambda 中 的 每 个 输 入 参 数 必 须 都 能 够 隐 式 转 换 为 其 对 应 的 委 托 参 数 。



Lambda 的 返 回 值 ( 如 果 有 ) 必 须 能 够 隐 式 转 换 为 委 托 的 返 回 类 型 。



请 注 意 ,Lambda 表 达 式 本 身 没 有 类 型 ,因 为 通 用 类 型 系 统 没 有 “ Lambda 表 达 式 ” 这 一 内 部 概 念 。但 是 ,有 时 会 不 正 式 地 论 及 Lambda 表 达 式 的 “ 类 型 ” 。在



这 些 情 况 下 , 类 型 是 指 委 托 类 型 或 Lambda 表 达 式 所 转 换 为 的 Expression 类 型 。



Lambda 表 达 式 中 的 变 量 范 围



Lambda 可 以 引 用 “外 部 变 量 ”,这 些 变 量 位 于 在 其 中 定 义 Lambda 的 封 闭 方 法 或 类 型 的 范 围 内 。将 会 存 储 通 过 这 种 方 法 捕 获 的 变 量 以 供 在 Lambda 表 达 式



中 使 用 ,即 使 变 量 将 以 其 他 方 式 超 出 范 围 或 被 作 为 垃 圾 回 收 。必 须 明 确 地 分 配 外 部 变 量 ,然 后 才 能 在 Lambda 表 达 式 中 使 用 该 变 量 。下 面 的 示 例 演 示 这 些规



则:



delegate bool D();



delegate bool D2(int i);







class Test



{



D del;



D2 del2;



public void TestMethod(int input)



{



int j = 0;



// Initialize the delegates with lambda expressions.



// Note access to 2 outer variables.



// del will be invoked within this method.



del = () => { j = 10; return j > input; };

9



// del2 will be invoked after TestMethod goes out of scope.



del2 = (x) => {return x == j; };







// Demonstrate value of j:



// Output: j = 0



// The delegate has not been invoked yet.



Console.WriteLine("j = {0}", j);







// Invoke the delegate.



bool boolResult = del();







// Output: j = 10 b = True



Console.WriteLine("j = {0}. b = {1}", j, b oolResult);



}







static void Main()



{



Test test = new Test();



test.TestMethod(5);







// Prove that del2 still has a copy of



// local variable j from TestMethod.



bool result = test.del2(10);







// Output: True



Console.WriteLine(result);







Console.ReadKey();



}



}



下 列 规 则 适 用 于 Lambda 表 达 式 中 的 变 量 范 围 :



捕获的变量将不会被作为垃圾回收,直至引用变量的委托超出范围为止。



在 外 部 方 法 中 看 不 到 Lambda 表 达 式 内 引 入 的 变 量 。



Lambda 表 达 式 无 法 从 封 闭 方 法 中 直 接 捕 获 ref 或 out 参 数 。



Lambda 表 达 式 中 的 返 回 语 句 不 会 导 致 封 闭 方 法 返 回 。



Lambda 表 达 式 不 能 包 含 其 目 标 位 于 所 包 含 匿 名 函 数 主 体 外 部 或 内 部 的 goto 语 句 、 break 语 句 或 continue 语 句 。









12. 匿名方法

在 2.0 之 前 的 C# 版 本 中 ,声 明 委 托 的 唯 一 方 法 是 使 用 命 名 方 法 。C# 2.0 引 入 了 匿 名 方 法 ,而 在 C# 3.0 及 更 高 版 本 中 ,Lambda 表 达 式 取 代 了 匿 名 方 法 ,



作 为 编 写 内 联 代 码 的 首 选 方 式 。不 过 ,本 主 题 中 有 关 匿 名 方 法 的 信 息 同 样 也 适 用 于 Lambda 表 达 式 。有 一 种 情 况 下 ,匿 名 方 法 提 供 了 Lambda 表 达 式 中 所 没 有



的 功 能 。匿 名 方 法 使 您 能 够 省 略 参 数 列 表 ,这 意 味 着 可 以 将 匿 名 方 法 转 换 为 带 有 各 种 签 名 的 委 托 。这 对 于 Lambda 表 达 式 来 说 是 不 可 能 的 。要 将 代 码 块 传 递为



委托参数,创建匿名方法则是唯一的方法。这里是两个示例:



// Create a handler for a click event



button1.Click += delegate(System.Object o, System.EventArgs e)



{ System.Windows.Forms.MessageBox.Show( "Click!"); };







// Create a delegate instance



delegate void Del(int x);

10







// Instantiate the delegate using an anonymous method



Del d = delegate(int k) { /* ... */ };



通过使用匿名方法,由于您不必创建单独的方法,因此减少了实例化委托所需的编码系统开销。



例如,如果创建方法所需的系统开销是不必要的,则指定代码块(而不是委托)可能非常有用。启动新线程即是一个很 好 的 示 例 。无 需 为 委 托 创建 更 多 方法 ,



线程类即可创建一个线程并且包含该线程执行的代码。



void StartThread()



{



System.Threading.Thread t1 = new System.Threading.Thread



(delegate()



{



System.Console.Write("Hello, ");



System.Console.WriteLine("World!");



});



t1.Start();



}







匿 名 方 法 的 参 数 的 范 围 是 “匿 名 方 法 块 ”。



如 果 目 标 在 块 外 部 , 那 么 , 在 匿 名 方 法 块 内 使 用 跳 转 语 句 ( 如 goto、 break 或 continue) 是 错 误 的 。 如 果 目 标 在 块 内 部 , 在 匿 名 方 法 块 外 部 使 用 跳 转 语



句 ( 如 goto、 break 或 continue) 也 是 错 误 的 。



如 果 局 部 变 量 和 参 数 的 范 围 包 含 匿 名 方 法 声 明 , 则 该 局 部 变 量 和 参 数 称 为 该 匿 名 方 法 的 “外 部 ”变 量 。 例 如 , 下 面 代 码 段 中 的 n 即是一个外部变量:



int n = 0;



Del d = delegate() { System.Console.WriteLine( "Copy #:{0}", ++n); };



与局部变量不同,捕获变量的生命周期一直持续到引用该匿名方法的委托符合垃圾回收的条件为止。对 n 的引用是在创建该委托时捕获的。



匿 名 方 法 不 能 访 问 外 部 范 围 的 ref 或 out 参 数 。



在 “匿 名 方 法 块 ”中 不 能 访 问 任 何 不 安 全 代 码 。



在 is 运 算 符 的 左 侧 不 允 许 使 用 匿 名 方 法 。







下面的示例演示实例化委托的两种方法:



使委托与匿名方法关联。



使 委 托 与 命 名 方 法 ( DoWork ) 关 联 。



两种方法都会在调用委托时显示一条消息。



// Declare a delegate



delegate void Printer(string s);



class TestClass



{



static void Main()



{



// Instatiate the delegate type using an anonymous method:



Printer p = delegate(string j)



{



System.Console.WriteLine(j);



};



// Results from the anonymous delegate call:



p("The delegate using the anonymous method is called.");



// The delegate instantiation using a named method "DoWork":



p = new Printer(TestClass.DoWork);



// Results from the old style dele gate call:



p("The delegate using the named method is called.");

11



}







// The method associated with the named delegate:



static void DoWork(string k)



{



System.Console.WriteLine(k);



}



}



/* Output:



The delegate using the anonymous method is called.



The delegate using the named method is called.



*/







13. C++ 模板和 C# 泛型之间的区别

C# 泛 型 和 C++ 模 板 都 是 用 于 提 供 参 数 化 类 型 支 持 的 语 言 功 能 。 然 而 , 这 两 者 之 间 存 在 许 多 差 异 。 在 语 法 层 面 上 , C# 泛 型 是 实 现 参 数 化 类 型 的 更 简 单 方



法 , 不 具 有 C++ 模 板 的 复 杂 性 。 此 外 , C# 并 不 尝 试 提 供 C++ 模 板 所 提 供 的 所 有 功 能 。 在 实 现 层 面 , 主 要 区 别 在 于 , C# 泛 型 类 型 替 换 是 在 运 行 时 执 行



的,从而为实例化的对象保留了泛型类型信息。



以 下 是 C# 泛 型 和 C++ 模 板 之 间 的 主 要 差 异 :



C# 泛 型 未 提 供 与 C++ 模 板 相 同 程 度 的 灵 活 性 。 例 如 , 尽 管 在 C# 泛 型 类 中 可 以 调 用 用 户 定 义 的 运 算 符 , 但 不 能 调 用 算 术 运 算 符 。



C# 不 允 许 非 类 型 模 板 参 数 , 如 template C {} 。

C# 不 支 持 显 式 专 用 化 , 即 特 定 类 型 的 模 板 的 自 定 义 实 现 。



C# 不 支 持 部 分 专 用 化 : 类 型 参 数 子 集 的 自 定 义 实 现 。



C# 不 允 许 将 类 型 参 数 用 作 泛 型 类 型 的 基 类 。



C# 不 允 许 类 型 参 数 具 有 默 认 类 型 。



在 C# 中 , 尽 管 构 造 类 型 可 用 作 泛 型 , 但 泛 型 类 型 参 数 自 身 不 能 是 泛 型 。 C++ 确 实 允 许 模 板 参 数 。



C++ 允 许 那 些 可 能 并 非 对 模 板 中 的 所 有 类 型 参 数 都 有 效 的 代 码 , 然 后 将 检 查 该 代 码 中 是 否 有 用 作 类 型 参 数 的 特 定 类 型 。 C# 要 求 相 应 地 编 写 类 中 的 代 码 , 使



之 能 够 使 用 任 何 满 足 约 束 的 类 型 。 例 如 , 可 以 在 C++ 中 编 写 对 类 型 参 数 的 对 象 使 用 算 术 运 算 符 + 和 - 的 函 数 , 这 会 在 使 用 不 支 持 这 些 运 算 符 的 类 型 来 实



例 化 模 板 时 产 生 错 误 。 C# 不 允 许 这 样 ; 唯 一 允 许 的 语 言 构 造 是 那 些 可 从 约 束 推 导 出 来 的 构 造 。









14. 身份验证和授权的区别

身份验证 :身份验证就是一个解决谁有权力进入系统的问题,通常的做法就是跟系统维护的用户名单进行核对,这样转化为一个实际的技术问题:如果有效



的 判 断 一 个 用 户 是 不 是 系 统 的 有 效 用 户 。 这 个 过 程 就 是 — Authentication( 身 份 验 证 )



专业说法:接收用户凭据,并根据指定的颁发机构来验证凭据的过程成为身份验证



Asp.net 提 供 三 种 身 份 验 证 方 式 : Windows 验 证 、 Forms 验 证 、 Passport 验 证 .



身 份 验 证 的 使 用 是 通 过 配 置 Web.config 文 件 的 配 置 节 来 实 现 的 。







授 权 : 授 权 就 是 确 认 用 户 拥 有 足 够 的 权 限 来 访 问 请 求 的 资 源 .Asp.net 提 供 两 类 授 权 服 务 : 文 件 授 权 服 务 、 URL 授 权 服 务



ASP.NET 授 权 决 定 了 是 否 应 授 予 某 个 标 识 对 特 定 资 源 的 访 问 权 限 。 在 ASP.NET 中 , 有 两 种 方 式 来 授 予 对 给 定 资 源 的 访 问 权 限 :



文件授权 它

文 件 授 权 由 FileAuthorizationModule 执 行 。 检 查 .aspx 或 .asmx 处 理 程 序 文 件 的 访 问 控 制 列 表 (ACL) 以 确 定 用 户 是 否 应 该 具 有 对 文 件



的 访 问 权 限 。ACL 权 限 用 于 验 证 用 户 的 Windows 标 识( 如 果 已 启 用 Windows 身 份 验 证 )或 ASP.NET 进 程 的 Windows 标 识 。有 关 更 多 信 息 ,请 参 见 ASP.NET



模拟。



URL 授 权 URL 授 权 由 UrlAuthorizationModule 执 行 , 它 将 用 户 和 角 色 映 射 到 ASP.NET 应 用 程 序 中 的 URL。 这 个 模 块 可 用 于 有 选 择 地 允 许 或 拒 绝 特 定



用户或角色对应用程序的任意部分(通常为目录)的访问权限。



使 用 URL 授 权



您 为 请 若

通 过 URL 授 权 , 可 以 显 式 允 许 或 拒 绝 某 个 用 户 名 或 角 色 对 特 定 目 录 的 访 问 权 限 。 此 , 在 该 目 录 的 配 置 文 件 中 创 建 一 个 authorization 节 。 要 启 用 URL



授 权 , 请 在 配 置 文 件 的 authorization 节 中 的 allow 或 deny 元 素 中 指 定 一 个 用 户 或 角 色 列 表 。 为 目 录 建 立 的 权 限 也 会 应 用 到 其 子 目 录 , 除 非 子 目 录 中 的



配置文件重写这些权限。



下 面 显 示 了 authorization 节 的 语 法 :





12











allow 或 deny 元 素 是 必 需 的 。 必 须 指 定 users 或 roles 属 性 。 可 以 同 时 包 含 二 者 , 但 这 不 是 必 需 的 。 verbs 属 性 可 选 。







15. ASP.net 的身份验证方式有哪些?分别是什么原理?

身 份 验 证 是 从 用 户 处 获 取 标 识 凭 据( 如 用 户 名 和 密 码 )并 通 过 某 些 授 权 机 构 验 证 那 些 凭 据 的 过 程 。如 果 这 些 凭 据 有 效 ,则 将 提 交 这 些 凭 据 的 实 体 视 为 通 过 身 份



验证。在身份得到验证后,授权进程将确定该身份是否可以访问给定资源。







Windows 身 份 验 证 将 Microsoft Internet 信 息 服 务 (IIS) 所 提 供 的 用 户 标 识 视 为 已 经 过 身 份 验 证 的 用 户 。 IIS 提 供 了 大 量 用 于 验 证 用 户 标 识 的 身 份 验 证 机



制 , 其 中 包 括 匿 名 身 份 验 证 、 Windows 集 成 的 (NTLM) 身 份 验 证 、Windows 集 成 的 (Kerberos) 身 份 验 证 、 基 本 ( base64 编 码 ) 身 份 验 证 、 摘 要 式 身 份 验 证



以及基于客户端证书的身份验证。



在 ASP.NET 中 , 使 用 WindowsAuthenticationModule 模 块 来 实 现 Windows 身 份 验 证 。 该 模 块 根 据 IIS 所 提 供 的 凭 据 构 造 一 个 WindowsIdentity, 并 将 该



标 识 设 置 为 该 应 用 程 序 的 当 前 User 属 性 值 。



Windows 身 份 验 证 是 ASP.NET 应 用 程 序 的 默 认 身 份 验 证 机 制 , 并 指 定 作 为 使 用 authentication 配 置 元 素 的 应 用 程 序 的 身 份 验 证 模 式 , 如 下 面 的 示 例 所 示 。



















Forms 身 份 验 证 提 供 了 一 种 方 法 , 使 您 可 以 使 用 自 己 的 代 码 对 用 户 进 行 身 份 验 证 , 然 后 将 身 份 验 证 标 记 保 留 在 Cookie 或 页 的 URL 中 。 Forms 身 份 验 证 通 过



FormsAuthenticationModule 参 与 ASP.NET 页 的 生 命 周 期 。 可 以 通 过 FormsAuthentication 类 访 问 Forms 身 份 验 证 信 息 和 功 能 。



若 要 使 用 Forms 身 份 验 证 , 可 以 创 建 一 个 登 录 页 。 该 登 录 页 既 收 集 了 用 户 的 凭 据 , 又 包 括 验 证 这 些 凭 据 时 所 需 的 代 码 。 如 果 这 些 凭 据 有 效 , 可 以 调 用



FormsAuthentication 类 的 方 法 , 以 便 使 用 适 当 的 身 份 验 证 票 证 (Cookie) 将 请 求 重 定 向 到 最 初 请 求 的 资 源 。 如 果 不 需 要 进 行 重 定 向 , 只 需 获 取 Forms 身 份



验 证 Cookie 或 对 其 进 行 设 置 即 可 。



使 用 authentication 配 置 元 素 对 Forms 身 份 验 证 进 行 配 置 。 最 简 单 的 情 况 是 , 在 Web.config 文 件 或 单 独 的 文 件 中 , 可 以 通 过 指 定 URL 将 未 经 身 份 验 证



的 请 求 重 定 向 到 某 个 登 录 页 ,然 后 提 供 该 登 录 页 的 最 小 实 现 ,并 提 供 有 效 的 凭 据 。下 面 的 示 例 演 示 配 置 文 件 的 一 部 分 。该 配 置 文 件 为 Authenticate 方 法 指 定



了 登 录 页 和 身 份 验 证 凭 据 。 密 码 已 经 使 用 HashPasswordForStoringInConfigFile 方 法 进 行 加 密 。



































成 功 通 过 身 份 验 证 之 后 , FormsAuthenticationModule 模 块 将 会 使 用 经 过 身 份 验 证 的 用 户 信 息 填 充 当 前 的 User 属 性 。



使 用 Forms 身 份 验 证 的 简 便 方 法 是 , 使 用 ASP.NET 成 员 资 格 和 ASP.NET 登 录 控 件 。ASP.NET 成 员 资 格 提 供 了 一 种 存 储 和 管 理 信 息 的 方 法 , 它 包 括 各 种 用 户



身 份 验 证 方 法 。 ASP.NET 登 录 控 件 使 用 ASP.NET 成 员 资 格 并 封 装 提 示 用 户 输 入 凭 据 时 所 需 的 逻 辑 、 验 证 用 户 或 恢 复 或 替 换 密 码 等 。 事 实 上 , ASP.NET 成 员 资



格 和 ASP.NET 登 录 控 件 在 Forms 身 份 验 证 中 提 供 了 一 个 抽 象 层 , 可 以 替 换 使 用 Forms 身 份 验 证 时 必 须 执 行 的 大 部 分 或 全 部 工 作 。







Passport 身 份 验 证 是 由 Microsoft 提 供 的 集 中 身 份 验 证 服 务 , 该 服 务 为 成 员 站 点 提 供 单 一 登 录 和 核 心 配 置 文 件 服 务 。 Passport 之 所 以 让 用 户 受 益 匪 浅 , 原



因 在 于 用 户 不 必 登 录 访 问 受 到 限 制 的 新 资 源 或 站 点 。 如 果 希 望 您 的 站 点 与 Passport 身 份 验 证 及 授 权 兼 容 , 则 应 该 使 用 该 提 供 程 序 。









16. 解释一下 UDDI、WSDL、SOAP 的意义及其作用.

UDDI 是 一 种 目 录 服 务 , 企 业 可 以 使 用 它 对 Web services 进 行 注 册 和 搜 索 。



UDDI, 英 文 为 "Universal Description, Discovery and Integration", 可 译 为 “ 通 用 描 述 、 发 现 与 集 成 服 务 ” 。



WSDL( 网 络 服 务 描 述 语 言 , Web Services Description Language ) 是 一 门 基 于 XML 的 语 言 , 用 于 描 述 Web Services 以 及 如 何 对 它 们 进 行 访 问 。



Web Services 服 务 提 供 方 通 过 WSDL(Web Services Description Language) 描 述 所 提 供 的 服 务 ,并 将 这 一 描 述 告 知 Web Services 注 册 服 务 器 。 注 册 服 务 器 依

13



据 WSDL 的 描 述 ,依 照 UDDI (Universal Description Discovery and Integration) 的 协 定 更 新 服 务 目 录 并 在 Internet 上 发 布 。 用 户 在 使 用 Web Services 前



先 向 注 册 服 务 器 发 出 请 求 ,获 得 Web Services 提 供 者 的 地 址 和 服 务 接 口 信 息 ,之 后 使 用 SOAP 协 议 (Simple Object Access Protocol) 与 Web Services 提 供



者 建 立 连 接 ,进 行 通 信 。 Web Services 的 技 术 主 要 建 立 在 XML 的 规 范 之 上 ,这 保 证 了 这 一 体 系 结 构 的 平 台 无 关 性 、 语 言 无 关 性 和 人 机 交 互 性 能 。









17. 什么是 SOAP,有哪些应用.

( SOAP,全 写 为 Simple Object Access Protocol)是 一 种 标 准 化 的 通 讯 规 范 ,主 要 用 于 Web 服 务( web service) 中 。SOAP 的 出 现 是 为 了 简 化 网 页 服 务 器( Web



Server) 在 从 XML 数 据 库 中 提 取 数 据 时 , 无 需 花 时 间 去 格 式 化 页 面 , 并 能 够 让 不 同 应 用 程 序 之 间 透 过 HTTP 通 讯 协 定 , 以 XML 格 式 互 相 交 换 彼 此 的 数 据 , 使 其



与编程语言、平台和硬件无关。



SOAP 使 用 因 特 网 应 用 层 协 议 作 为 其 传 输 协 议 。SMTP 以 及 HTTP 协 议 都 可 以 用 来 传 输 SOAP 消 息 ,但 是 由 于 HTTP 在 如 今 的 因 特 网 结 构 中 工 作 得 很 好 ,特 别 是 在网



络 防 火 墙 下 仍 然 工 作 流 畅 , 所 以 其 更 为 广 泛 地 被 采 纳 。 SOAP 亦 可 以 在 HTTPS 上 进 行 传 输 。



SOAP 的 消 息 格 式 采 用 XML。



SOAP 消 息 实 例



请求















classifieds















回应















http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous











http://localhost:8080/axis2/services/MyService







ECE5B3F187F29D28BC11433905662036















classifieds



















18. 常用的调用 webservice 方法有哪些?

一 、 WebService 在 cs 后 台 程 序 中 的 调 用



A、 通 过 命 名 空 间 和 类 名 直 接 调 用



示例: WebService ws = new WebService();



string s = ws.HelloWorld();



B、 通 过 添 加 WEB 引 用 的 方 式 调 用 , 首 先 添 加 WEB 引 用 , 通 过 URL 指 向 WEBSERVICE,

14



指 定 WEB 引 用 名 , 假 设 为 KK;



示例: kk.WebService n = new kk.WebService();



string ss=n.HelloWorld();



二 、 WebService 在 前 台 页 面 的 JS 调 用 方 法



1、 首 先 通 过 下 面 的 方 法 把 Webservice 在 前 台 引 用 进 来























2、 然 后 就 可 以 通 过 JS 程 序 进 行 调 用 , 示 例 如 下 :







function a()



{



WebService.HelloWorld(onresult);



}



//这 里 的 onresult 是 回 调 函 数



function onresult(result)



{



alert(result);



}



function b()



{



WebService.add(1,2,onreturn)



}



function onreturn(result)



{



alert(result);



}



//下 面 的 'context'是 上 下 文 , 可 以 通 过 回 到 函 数 通 过 重 载 的 方 式 获 得 ;



function c()



{



WebService.div(1,1,onresultC,onerror,'context');



}



function onresultC(res,c)



{



alert(res);



alert(c);



}



//onerror 是 获 得 异 常 信 息 的 回 调 函 数 , 下 面 给 出 了 获 得 异 常 信 息 的 方 法



function onerror(error)



{



var a="";



a=String.format("获 取 服 务 器 端 异 常 的 具 体 类 型 :



{0}\t\n 获 取 详 细 的 异 常 描 述 信 息 :



{1}\t\n 获 取 造 成 异 常 的 :



{2}\t\n 获 取 服 务 器 端 异 常 的 堆 栈



跟踪信息:



{3}\t\n 获 取 一 个 布 尔 值 , 表 示 异 常 是 否 是 由 于 网 络 连 接 超 时 造 成 的 {4}",

15



error.get_exceptionType(),



error.get_message(),



error.get_statusCode(),



error.get_stackTrace(),



error.get_timedOut())



alert(a);



}



a();



b();



c();











19. C#可否对内存进行直接的操作?,如果能,如何操作;如果不能,为什么?

可以使用指针



在 这 篇 文 章 中 将 描 述 C#的 一 个 特 性 指 针 和 所 谓 的 不 安 全 代 码 。 非 安 全 代 码 就 是 不 在 CLR 完 全 控 制 下 执 行 的 代 码 , 它 有 可 能 会 导 致 一 些 问 题 , 因 此 他 们 必



须 用 “ unsafe” 进 行 表 明 :



unsafe



{



...



// unsafe context: can use pointers here



...



}



在 其 他 一 些 地 方 也 可 以 使 用 关 键 字 ‘ unsafe’ , 例 如 我 们 可 以 将 类 或 方 法 表 明 为 非 安 全 的 :



unsafe class Class1 {}



static unsafe void FastMove ( int* pi, int* pdi, int length) {...}



‘ unsafe’ 关 键 字 的 必 要 性 是 它 可 以 防 止 程 序 员 的 一 些 意 外 的 用 法 。 你 可 能 会 问 既 然 是 不 安 全 的 为 什 么 还 有 人 要 用 它 。 答 案 就 是 有 时 候 , 在 有 些 情 况 下 , 还



需要用到指针。还有一些与指针紧密联系的操作符,那就是 & 操作符,& 返回它所操作对象的地址。



例如:



unsafe



{



int* pi;



int x = 1;



pi = &x;



System.Console.WriteLine("Value of x is: " + *pi);



}



在 这 个 例 子 中 我 们 创 建 了 2 个 变 量 , ’ pi’ 是 指 向 int 的 指 针 , ’ x’ 是 int,然 后 我 们 将 ’ x’ 在 内 存 中 的 地 址 赋 予 ’ pi’ , 理 解 我 们 放 在 ’ pi’ 变 量 中 的



是 ’ x’ 的 地 址 而 不 是 ’ x’ 的 值 非 常 重 要 (使 用 : pi = x 将 返 回 错 误 "Cannot implicitly convert type 'int' to 'int*'")



编译后执行将会输出:



Value of x is: 1



指 针 可 以 接 受 null 值 , 也 可 能 使 用 void 指 针 类 型 , 下 面 的 代 码 可 以 正 常 编 译 :



unsafe



{



nt x = 10;



void* px = &x;



double *pd = (double*)px;



}



fixed 关 键 字 和 垃 圾 回 收 :在 C# 中 使 用 指 针 需 要 比 在 C++种 更 加 注 意 。 这 是 因 为 垃 圾 回 收 器 (g.c.)会 运 行 内 存 清 理 , 在 清 理 的 过 程 中 , g.c.会 改 变 对 象 的 物



理 内 存 位 置 , 如 果 g.c.改 变 了 对 象 的 位 置 指 针 将 指 向 错 误 的 内 存 位 置 。 为 了 避 免 这 样 的 问 题 ( 已 经 与 垃 圾 回 收 器 连 接 ) , C# 包 含 'fixed' 关 键 字 . 它 通 知



系统不要让垃圾回收器重新部署对象。

16



如 果 我 们 忘 了 ’ fixed’ 关 键 字 编 译 器 会 给 我 们 相 应 的 警 告 , 但 它 没 有 智 能 到 在 下 面 的 情 况 中 也 会 警 告 我 们 。







20. 描述一下 C#中索引器的实现过程,是否只能根据数字进行索引?

索 引 器 (Indexer)是 C#引 入 的 一 个 新 型 的 类 成 员 , 它 使 得 对 象 可 以 像 数 组 那 样 被 方 便 , 直 观 的 引 用 。 索 引 器 非 常 类 似 于 我 们 前 面 讲 到 的 属 性 , 但 索 引 器 可 以 有



参数列表,且只能作用在实例对象上,而不能在类上直接作用。



class MyClass



{



public object this [int index]



{



get



{



// 取 数 据



}



set



{



// 存 数 据



}



}



}







索 引 器 没 有 像 属 性 和 方 法 那 样 的 名 字 ,关 键 字 this 清 楚 地 表 达 了 索 引 器 引 用 对 象 的 特 征 。和 属 性 一 样 ,value 关 键 字 在 set 后 的 语 句 块 里 有 参 数 传 递 意 义 。实



际 上 从 编 译 后 的 IL 中 间 语 言 代 码 来 看 , 上 面 这 个 索 引 器 被 实 现 为 :



class MyClass



{



public object get_Item(int index)



{



// 取 数 据



}



public void set_Item(int index, object value)



{



//存 数 据



}



}



由 于 我 们 的 索 引 器 在 背 后 被 编 译 成 get_Item(int index)和 set_Item(int index, object value)两 个 方 法 , 我 们 甚 至 不 能 再 在 声 明 实 现 索 引 器 的 类 里 面 声 明



实现这两个方法,编译器会对这样的行为报错。这样隐含实现的方法同样可以被我们进行调用,继承等操作,和我们自己实现的方法别无二致。



索 和 以 这 唯 (

和 方 法 一 样 , 引 器 有 5 种 存 取 保 护 级 别 , 4 种 继 承 行 为 修 饰 , 及 外 部 索 引 器 。 些 行 为 同 方 法 没 有 任 何 差 别 。 一 不 同 的 是 索 引 器 不 能 为 静 态 static),



这 在 对 象 引 用 的 语 义 下 很 容 易 理 解 。 值 得 注 意 的 是 在 覆 盖 ( override) 实 现 索 引 器 时 , 应 该 用 base[E]来 存 取 父 类 的 索 引 器 。



和 属 性 的 实 现 一 样 , 索 引 器 的 数 据 类 型 同 时 为 get 语 句 块 的 返 回 类 型 和 set 语 句 块 中 value 关 键 字 的 类 型 。



索 引 器 的 参 数 列 表 也 是 值 得 注 意 的 地 方 。 “ 索 引 ” 的 特 征 使 得 索 引 器 必 须 具 备 至 少 一 个 参 数 , 该 参 数 位 于 this 关 键 字 之 后 的 中 括 号 内 。 索 引 器 的 参 数 也 只 能



是 传 值 类 型 ,不 可 以 有 ref( 引 用 )和 out( 输 出 )修 饰 。参 数 的 数 据 类 型 可 以 是 C#中 的 任 何 数 据 类 型 。C#根 据 不 同 的 参 数 签 名 来 进 行 索 引 器 的 多 态 辨 析 。中 括



号 内 的 所 有 参 数 在 get 和 set 下 都 可 以 引 用 , 而 value 关 键 字 只 能 在 set 下 作 为 传 递 参 数 。



索 引 器 通 常 用 于 对 象 容 器 中 为 其 内 的 对 象 提 供 友 好 的 存 取 界 面 --这 也 是 为 什 么 C#将 方 法 包 装 成 索 引 器 的 原 因 所 在 。



C# 并 不 将 索 引 类 型 限 制 为 整 数 。例 如 ,对 索 引 器 使 用 字 符 串 可 能 是 有 用 的 。通 过 搜 索 集 合 内 的 字 符 串 并 返 回 相 应 的 值 ,可 以 实 现 此 类 索 引 器 。由 于 访 问 器 可



被重载,字符串和整数版本可以共存。







示例 1



说明



下 面 的 示 例 说 明 如 何 声 明 私 有 数 组 字 段 、 temps 和 索 引 器 。 使 用 索 引 器 可 直 接 访 问 实 例 tempRecord[i] 。 另 一 种 使 用 索 引 器 的 方 法 是 将 数 组 声 明 为

public 成 员 并 直 接 访 问 它 的 成 员 tempRecord.temps[i] 。

17



请 注 意 , 当 计 算 索 引 器 的 访 问 时 ( 例 如 , 在 Console.Write 语 句 中 ) , 将 调 用 get 访 问 器 。 因 此 , 如 果 get 访 问 器 不 存 在 , 将 发 生 编 译 时 错 误 。







class TempRecord



{



// Array of temperature values



private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,



61.3F, 65.9F, 62.1F, 59.2F, 57.5F };







// To enable client code to validate input



// when accessing your indexer.



public int Length



{



get { return temps.Length; }



}



// Indexer declaration.



// If index is out of range, the temps array will throw the exception.



public float this[int index]



{



get



{



return temps[index];



}







set



{



temps[index] = value;



}



}



}







class MainClass



{



static void Main()



{



TempRecord tempRecord = new TempRecord();



// Use the indexer's set accessor



tempRecord[3] = 58.3F;



tempRecord[5] = 60.1F;







// Use the indexer's get accessor



for (int i = 0; i 对象,并向该对象添加 Circle、Triangle 和 Rectangle。

若要更新绘图图面,请使用 foreach 循环对该列表进行循环访问,并对其中的每个 Shape 对象调用 Draw 方法。虽然列表中的每个对象都具有



声明类型 Shape,但调用的将是运行时类型(该方法在每个派生类中的重写版本)

public class Shape



{



// A few example members



public int X { get; private set; }



public int Y { get; private set; }



public int Height { get; set; }



public int Width { get; set; }



// Virtual method



public virtual void Draw()



{



Console.WriteLine("Performing base class drawing tasks");



}



}







class Circle : Shape



{



public override void Draw()



{



// Code to draw a circle...



Console.WriteLine("Drawing a circle");



base.Draw();



}



}



class Rectangle : Shape



{



public override void Draw()



{



// Code to draw a rectangle...



Console.WriteLine("Drawing a rectangle");



base.Draw();



}



}



class Triangle : Shape

21



{



public override void Draw()



{



// Code to draw a triangle...



Console.WriteLine("Drawing a triangle");



base.Draw();



}



}







class Program



{



static void Main(string[] args)



{



// Polymorphism at work #1: a Rectangle, Triangle and Circle



// can all be used whereever a Shape is expected. No cast is



// required because an implicit conversion exists from a derived



// class to its base class.



System.Collections.Generic.List shapes = new System.Collections.Generic.List();



shapes.Add(new Rectangle());



shapes.Add(new Triangle());



shapes.Add(new Circle());







// Polymorphism at work #2: the virtual method Draw is



// invoked on each of the derived classes, not the base class.



foreach (Shape s in shapes)



{



s.Draw();



}







// Keep the console open in debug mode.



Console.WriteLine("Press any key to exit.");



Console.ReadKey();



}







}







/* Output:



Drawing a rectangle



Performing base class drawing tasks



Drawing a triangle



Performing base class drawing tasks



Drawing a circle



Performing base class drawing tasks



*/



在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。



多态性概述



虚拟成员



当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。派生类的设计器可以选择是否







重写基类中的虚拟成员。

22







继承最接近的基类方法而不重写它







定义隐藏基类实现的成员的新非虚实现



仅当基类成员声明为 virtual 或 abstract 时,派生类才能重写基类成员。派生成员必须使用 override 关键字显式指示该方法将参与虚调用。以下代码提供了一个示例:



public class BaseClass



{



public virtual void DoWork() { }



public virtual int WorkProperty



{



get { return 0; }



}



}



public class DerivedClass : BaseClass



{



public override void DoWork() { }



public override int WorkProperty



{



get { return 0; }



}



}



字段不能是虚拟的,只有方法、属性、事件和索引器才可以是虚拟的。当派生类重写某个虚拟成员时,即使该派生类的实例被当作基类的实例访问,也会调用该成员。以下代码提供了一个示例:







DerivedClass B = new DerivedClass();



B.DoWork(); // Calls the new method.







BaseClass A = (BaseClass)B;



A.DoWork(); // Also calls the new method.



虚方法和属性允许派生类扩展基类,而无需使用方法的基类实现。有关更多信息,请参见 使用 Override 和 New 关键字进行版本控制(C# 编程指南)。接口提供另一种方式来定义将实现留给派



生类的方法或方法集。有关更多信息,请参见 接口(C# 编程指南)和 在类和接口之间选择。







使用新成员隐藏基类成员



new

如果希望派生成员具有与基类中的成员相同的名称,但又不希望派生成员参与虚调用,则可以使用 new 关键字。 关键字放置在要替换的类成员的返回类型之前。以下代码提供了一个示例:





public class BaseClass



{



public void DoWork() { WorkField++; }



public int WorkField;



public int WorkProperty



{



get { return 0; }



}



}







public class DerivedClass : BaseClass



{



public new void DoWork() { WorkField++; }



public new int WorkField;



public new int WorkProperty



{



get { return 0; }



}



}

23



通过将派生类的实例强制转换为基类的实例,仍然可以从客户端代码访问隐藏的基类成员。例如:



DerivedClass B = new DerivedClass();



B.DoWork(); // Calls the new method.







BaseClass A = (BaseClass)B;



A.DoWork(); // Calls the old method.



阻止派生类重写虚拟成员



无论在虚拟成员和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员永远都是虚拟的。如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以



选择重写它,而不管类 B 是否为该成员声明了重写。以下代码提供了一个示例:



public class A



{



public virtual void DoWork() { }



}



public class B : A



{



public override void DoWork() { }



}



override sealed

派生类可以通过将重写声明为 sealed 来停止虚拟继承。这需要在类成员声明中的 关键字前面放置 关键字。以下代码提供了一个示例:





public class C : B



{



public sealed override void DoWork() { }



}

在上面的示例中,方法 DoWork 对从 C 派生的任何类都不再是虚方法,但它仍是 C 的实例的虚方法 -- 即使将这些实例强制转换为类型 B 或类型 A 也是如此。派生类可以通过使用

new 关键字

替换密封的方法,如下面的示例所示:



public class D : C



{



public new void DoWork() { }



}



在此情况下,如果在 D 中使用类型为 D 的变量调用 DoWork,被调用的将是新的 DoWork。如果使用类型为 C、B 或 A 的变量访问 D 的实例,对 DoWork 的调用将遵循虚拟继承的规则,



即把这些调用传送到类 C 的 DoWork 实现。







从派生类访问基类虚拟成员



已替换或重写某个方法或属性的派生类仍然可以使用基关键字访问基类的该方法或属性。以下代码提供了一个示例:







public class Base



{



public virtual void DoWork() {/*...*/ }



}



public class Derived : Base



{



public override void DoWork()



{



//Perform Derived's work here



//...



// Call DoWork on base class



base.DoWork();



}



}







使用 Override 和 New 关键字进行版本控制

24



C# 语言经过专门设计,以便不同库中的 基类与派生类之间的版本控制可以不断向前发展,同时保持向后兼容。这具有多方面的意义。例如,这意味

着在基 类中引入与派生类中的某个成员具有相同名称的新成员在 C# 中是完全支持的,不会导致意外行为。它还意味着类必须显式声明某方法是要

重写一个继承方法,还是一个隐藏具有类似名称的继承方法的新方法。



在 C# 中,派生类可以包含与基类方法同名的方法。





 基类方法必须定义为 virtual。





 如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将有如存在 new 关键字一样执行操作。





 如果派生类中的方法前面带有 new 关键字,则该方法被定义为独立于基类中的方法。





 如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法,而不是调用基类方法。





 可以从派生类中使用 base 关键字调用基类方法。





 override、virtual 和 new 关键字还可以用于属性、索引器和事件中。



默认情况下,C# 方法为非虚方法。如果某个方法被声明为虚方法,则继承该方法的任何类都可以实现它自己的版本。若要使方法成为虚方法,必须

在基类的方法声明中使用 virtual 修饰符。然后,派生类可以使用 override 关键字重写基虚方法,或使用 new 关键字隐藏基类中的虚方法。如果



override 关键字和 new 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。有关更多信息,请参见 编译器警告(等

级 2)CS0108。



为了在实践中演示上述情况,我们暂时假定公司 A 创建了一个名为 GraphicsClass 的类,您的程序将使用此类。GraphicsClass 如下所示:



C#





复制代码

class GraphicsClass





{





public virtual void DrawLine() { }





public virtual void DrawPoint() { }





}





您的公司使用此类,并且您在添加新方法时将其用来派生自己的类:



C#





复制代码

class YourDerivedGraphicsClass : GraphicsClass





{





public void DrawRectangle() { }





}





您的应用程序运行正常,直到公司 A 发布了 GraphicsClass 的新版本,类似于下面的代码:



C#





复制代码

25



class GraphicsClass





{





public virtual void DrawLine() { }





public virtual void DrawPoint() { }





public virtual void DrawRectangle() { }





}





现在,GraphicsClass 的新版本中包含一个名为 DrawRectangle 的方法。开始时,没有出现任何问题。新版本仍然与旧版本保持二进制兼容。

已经部署的任何软件都将继续正常工作,即使新类已安装到这些软件所在的计算机系统上。在您的派生类中,对方法 DrawRectangle 的任何现有

调用将继续引用您的版本。



但是,一旦您使用 GraphicsClass 的新版本重新编译应用程序,

就会收到来自编译器的警告。 请参见 编译器警告

有关更多信息, (等级 2)CS0108。



此警告提示您必须考虑希望 DrawRectangle 方法在应用程序中的工作方式。



如果您希望自己的方法重写新的基类方法,请使用 override 关键字:



C#





复制代码

class YourDerivedGraphicsClass : GraphicsClass





{





public override void DrawRectangle() { }





}





override 关键字可确保派生自 YourDerivedGraphicsClass 的任何对象都将使用 DrawRectangle 的派生类版本。派生自

YourDerivedGraphicsClass 的对象仍可以使用基关键字访问 DrawRectangle 的基类版本:



C#





复制代码

base.DrawRectangle();





如果您不希望自己的方法重写新的基类方法,则需要注意以下事项。为了避免这两个方法之间发生混淆,可以重命名您的方法。这可能很耗费时间且

容易出错,而且在某些情况下并不可行。但是,如果您的项目相对较小,则可以使用 Visual Studio 的重构选项来重命名方法。有关更多信息,请参

见 重构类和类型。



或者,也可以通过在派生类定义中使用关键字 new 来防止出现该警告:



C#





复制代码

class YourDerivedGraphicsClass : GraphicsClass





{





public new void DrawRectangle() { }

26



}





使用 new 关键字可告诉编译器您的定义将隐藏基类中包含的定义。这是默认行为。



重写和方法选择

当在类中指定方法时,如果有多个方法与调用兼容(例如,存在两种同名的方法,并且其参数与传递的参数兼容),则 C# 编译器将选择最佳方法进

行调用。下面的方法将是兼容的:



C#





复制代码

public class Derived : Base





{





public override void DoWork(int param) { }





public void DoWork(double param) { }





}





在 Derived 的一个实例中调用 DoWork 时,C# 编译器将首先尝试使该调用与最初在 Derived 上声明的 DoWork 版本兼容。重写方法不被视

为是在类上进行声明的,而是在基类上声明的方法的新实现。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调

用与具有相同名称和兼容参数的重写方法匹配。例如:



C#





复制代码

int val = 5;





Derived d = new Derived();





d.DoWork(val); // Calls DoWork(double).





由于变量 val 可以隐式转换为 double 类型,因此 C# 编译器将调用 DoWork(double),而不是 DoWork(int)。有两种方法可以避免此情况。

首先,避免将新方法声明为与虚方法同名。其次,可以通过将 Derived 的实例强制转换为 Base 来使 C# 编译器搜索基类方法列表,从而使其调

用虚方法。由于是虚方法,因此将调用 Derived 上的 DoWork(int) 的实现。例如:



((Base)d).DoWork(val); // Calls DoWork(int) on Derived.





23. 面向对象的设计原则

一 、 单 一 职 责 原 则 ( The Single Responsiblity Principle, S R P )



就 一 个 类 而 言 ,应 该 仅 有 一 个 引 起 它 变 化 的 原 因 。软 件 设 计 真 正 要 做 的 许 多 内 容 ,就 是 发 现 职 责 并 把 那 些 职 责 相 互 分 离 。测 试 驱 动 的 开 发 实 践 常 常 会 在 设 计 出



现臭味之前就迫使我们分离职责。



二 、 开 闭 原 则 ( The Open- Close Principle, O C P )



软 件 实 体 ( 类 、 模 块 、函 数 ) 应 该 是 可 扩 展 的 , 但 是 不 可 修 改 的 。 也 就 是 说 : 对 于 扩 展 是 开 放 的 ,对 于 更 改 是 封 闭 的 。 怎 样 可 能 在 不 改 动 模 块 源 代 码 的 情 况 下



去 更 改 它 的 行 为 呢 ? 怎 样 才 能 在 无 需 对 模 块 进 行 改 动 的 情 况 下 就 改 变 它 的 功 能 呢 ? 关 键 是 抽 象 !因 此 在 进 行 面 向 对 象 设 计 时 要 尽 量 考 虑 接 口 封 装 机 制 、抽 象 机



制和多态技术。该原则同样适合于非面向对象设计的方法,是软件工程设计方法的重要原则之一。



三 、 替 换 原 则 ( The Liskov Substitution Principle, L S P )



子 类 应 当 可 以 替 换 父 类 并 出 现 在 父 类 能 够 出 现 的 任 何 地 方 。 这 个 原 则 是 Liskov 于 1987 年 提 出 的 设 计 原 则 。 它 同 样 可 以 从 Bertrand Meyer 的 DBC (Design by



Contract〔 基 于 契 约 设 计 〕 ) 的 概 念 推 出 。



四 、 依 赖 倒 置 原 则 ( The Dependency Inversion Pricinple, D I P )



1、高层模块不应该依赖于低层模块。二者都应该依赖于抽象。

27



2 、抽 象 不 应 该 依 赖 于 细 节 。细 节 应 该 依 赖 于 抽 象 。在 进 行 业 务 设 计 时 ,与 特 定 业 务 有 关 的 依 赖 关 系 应 该 尽 量 依 赖 接 口 和 抽 象 类 ,而 不 是 依 赖 于 具 体 类 。具 体



类 只 负 责 相 关 业 务 的 实 现 ,修 改 具 体 类 不 影 响 与 特 定 业 务 有 关 的 依 赖 关 系 。在 结 构 化 设 计 中 ,我 们 可 以 看 到 底 层 的 模 块 是 对 高 层 抽 象 模 块 的 实 现( 高 层 抽 象 模



块 通 过 调 用 底 层 模 块 ),这 说 明 ,抽 象 的 模 块 要 依 赖 具 体 实 现 相 关 的 模 块 ,底 层 模 块 的 具 体 实 现 发 生 变 动 时 将 会 严 重 影 响 高 层 抽 象 的 模 块 ,显 然 这 是 结 构 化 方



法 的 一 个 "硬 伤 "。 面 向 对 象 方 法 的 依 赖 关 系 刚 好 相 反 , 具 体 实 现 类 依 赖 于 抽 象 类 和 接 口 。



五 、 接 口 分 离 原 则 ( The Interface Segregation Principle, I S P )



采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。 ISP 原 则 是 另 外 一 个 支 持 诸 如 COM 等 组 件 化 的 使 能 技 术 。 缺 少 ISP,



组 件 、类 的 可 用 性 和 移 植 性 将 大 打 折 扣 。这 个 原 则 的 本 质 相 当 简 单 。如 果 你 拥 有 一 个 针 对 多 个 客 户 的 类 ,为 每 一 个 客 户 创 建 特 定 业 务 接 口 ,然 后 使 该 客 户 类 继



承多个特定业务接口将比直接加载客户所需所有方法有效。



以 上 五 个 原 则 是 面 向 对 象 中 常 常 用 到 的 原 则 。此 外 ,除 上 述 五 原 则 外 ,还 有 一 些 常 用 的 经 验 诸 如 类 结 构 层 次 以 三 到 四 层 为 宜 、类 的 职 责 明 确 化( 一 个 类 对 应 一



个 具 体 职 责 )等 可 供 我 们 在 进 行 面 向 对 象 设 计 参 考 。但 就 上 面 的 几 个 原 则 看 来 ,我 们 看 到 这 些 类 在 几 何 分 布 上 呈 现 树 型 拓 扑 的 关 系 ,这 是 一 种 良 好 、开 放 式 的



线性关系、具有较低的设计复杂度。一般说来,在软件设计中我们应当尽量避免出现带有闭包、循环的设计关系,它们 反 映 的 是 较大 的 耦 合 度 和设 计 复 杂化 。







24. 用户控件和自定义控件的定义及区别

一 ).用 户 控 件 和 自 定 义 控 件 概 述



用 户 控 件 (UserControl): 扩 展 名 为 *.ascx,跟 *.aspx 在 结 构 上 相 似 , 是 指 页 面 中 加 载 的 功 能 块 ,只 是 用 户 控 件 不 能 单 独 作 为 页 面 运 行 ,必 须 嵌 入



到 *.aspx 页 面 或 其 它 用 户 控 件 中 使 用 .



自 定 义 控 件 , 跟 HtmlControl 或 WebControl 相 似 , 编 译 后 可 以 添 加 引 用 到 工 具 栏 里 面 , 直 接 用 鼠 标 拖 动 使 用 .



(二 ).使 用



在 一 个 大 系 统 中 ,有 时 候 会 只 能 几 个 *.aspx 页 面 ,其 余 的 都 是 做 成 *.ascx 页 面 ,这 样 可 以 增 强 页 面 之 间 的 藕 合 性 ,一 个 用 户 控 件 *.ascx 都 作 为 一 个



独立的功能块.



自 定 义 控 件 是 指 编 译 后 直 接 可 以 放 到 工 具 箱 中 用 , 就 像 TextBox,DataGrid 一 样 在 设 计 器 中 可 以 用 鼠 标 拖 动 到 页 面 上 使 用 .



自定义服务器控件分为两种:



1.一 种 是 用 *.aspx 代 码 和 *.cs 代 码 编 译 后 生 成 DLL,再 添 加 引 用 到 工 具 箱 使 用 .一 般 用 于 WebForm 中 .



2.另 一 种 是 只 用 *.cs 实 现 ,再 编 译 生 成 DLL,添 加 到 工 具 箱 使 用 . 一 般 用 于 WinForm 中 .



自定义控件生成步骤:



比如:



1. 将 一 个 Button 从 设 计 器 拖 到 页 面 中 , 对 按 钮 大 小 ,颜 色 或 文 本 设 置 一 个 固 定 值 ,保 存 文 件 名 为 : a.cs



2. csc /r:System.dll /t:library/out:..\..\A.dll a.cs



3. 右 击 工 具 箱 空 白 处 , 弹 出 右 击 菜 单 后 , 选 “ 添 加 移 除 项 ” ,将 刚 生 成 的 DLL 添 加 到 工 具 箱 里 面 来 , 这 样 , 就 可 以 像 一 般 的 Button 一 样 拖



动使用了







25. CTS、CLS 和 CLR 分别作何解释?

.NET 定 义 了 一 个 称 为 通 用 类 型 系 统 Common Type System(CTS)的 类 型 标 准 。 任 何 以 .NET 平 台 作 为 目 标 的 语 言 必 须 建 立 它 的 数 据 类 型 与 CTS 的 类 型 间 的 映 射 。所



有 .NET 语 言 共 享 这 一 类 型 系 统 , 实 现 它 们 之 间 无 缝 的 互 操 作 。 该 方 案 还 提 供 了 语 言 之 间 的 继 承 性 。 例 如 , 用 户 能 够 在 VB.NET 中 派 生 一 个 由 C#编 写 的 类 。



很显然,编程语言的区别不仅仅在于类型。例如,一些语言支持多继承性,一些语言支持无符号数据类型,一些语言支 持 运 算 符 重载 。 用 户 应 认识 到 这 一点 ,



因 此 .NET 通 过 定 义 公 共 语 言 规 范 (CLS: Common Language Specification), 限 制 了 由 这 些 不 同 引 发 的 互 操 作 性 问 题 。 CLS 制 定 了 一 种 以 .NET 平 台 为 目 标 的 语 言



所 必 须 支 持 的 最 小 特 征 ,以 及 该 语 言 与 其 他 .NET 语 言 之 间 实 现 互 操 作 性 所 需 要 的 完 备 特 征 。认 识 到 这 点 很 重 要 ,这 里 讨 论 的 特 征 问 题 已 不 仅 仅 是 语 言 间 的 简 单



语 法 区 别 。 例 如 , CLS 并 不 去 关 心 一 种 语 言 用 什 么 关 键 字 实 现 继 承 , 只 是 关 心 该 语 言 如 何 支 持 继 承 。



CLS 是 CTS 的 一 个 子 集 。这 就 意 味 着 一 种 语 言 特 征 可 能 符 合 CTS 标 准 ,但 又 超 出 CLS 的 范 畴 。例 如 :C#支 持 无 符 号 数 字 类 型 ,该 特 征 能 通 过 CTS 的 测 试 ,但 CLS



却 仅 仅 识 别 符 号 数 字 类 型 。 因 此 , 如 果 用 户 在 一 个 组 件 中 使 用 C#的 无 符 号 类 型 , 就 可 能 不 能 与 不 使 用 无 符 号 类 型 的 语 言 (如 VB.NET)设 计 的 .NET 组 件 实 现 互 操



作 。 这 里 用 的 是 “ 可 能 不 ” , 而 不 是 “ 不 可 能 ” , 因 为 这 一 问 题 实 际 依 赖 于 对 non-CLS-compliant 项 的 可 见 性 。 事 实 上 , CLS 规 则 只 适 用 于 或 部 分 适 用 于 那 些



与 其 他 组 件 存 在 联 系 的 组 件 中 的 类 型 。 实 际 上 , 用 户 能 够 安 全 实 现 含 私 有 组 件 的 项 目 , 而 该 组 件 使 用 了 用 户 所 选 择 使 用 的 .NET 语 言 的 全 部 功 能 , 且 无 需 遵 守



CLS 的 规 范 。 另 一 方 面 , 如 果 用 户 需 要 .NET 语 言 的 互 操 作 性 , 那 么 用 户 的 组 件 中 的 公 共 项 必 须 完 全 符 合 CLS 规 范 。



最 后 一 个 C 是 公 共 语 言 运 行 库 Common Language Runtime(CLR)。 简 单 地 说 , CLR 是 CTS 的 实 现 , 也 就 是 说 , CLR 是 应 用 程 序 的 执 行 引 擎 和 功 能 齐 全 的 类 库 , 该



类 库 严 格 按 照 CTS 规 范 实 现 。 作 为 程 序 执 行 引 擎 , CLR 负 责 安 全 地 载 入 和 运 行 用 户 程 序 代 码 , 包 括 对 不 用 对 象 的 垃 圾 回 收 和 安 全 检 查 。 在 CLR 监 控 之 下 运 行 的



代 码 , 称 为 托 管 代 码 (managed code)。 作 为 类 库 , CLR 提 供 上 百 个 可 用 的 有 用 类 型 , 而 这 些 类 型 可 通 过 继 承 进 行 扩 展 。 对 于 文 件 I/O、 创 建 对 话 框 、 启 动 线 程





等 类 型 — 基 本 上 能 使 用 Windows API 来 完 成 的 操 作 , 都 可 由 其 完 成 。



.NET 语 言 编 译 器 生 成 与 平 台 无 关 的 代 码 ,称 为 通 用 中 间 语 言 (CIL,简 称 IL)。从 概 念 上 来 讲 ,这 一 点 非 常 类 似 Java 中 的 字 节 码 。但 与 Java 字 节 码 所 不 同 的 是 ,



Microsoft 设 计 的 IL 可 以 由 任 一 语 言 编 译 器 很 容 易 地 生 成 。



前 面 提 到 的 , 程 序 集 包 含 IL, 而 不 是 源 代 码 。 在 运 行 过 程 中 , 当 第 一 次 调 用 一 个 方 法 , 为 了 更 快 的 执 行 程 序 , 实 时 Just-In-Time (JIT)编 译 器 将 该 方 法 的 IL 代

28



码 转 换 成 源 代 码 (与 平 台 有 关 )。 .NET 只 编 译 需 要 在 运 行 库 中 使 用 的 那 部 分 IL。 当 然 , 它 将 会 把 转 换 得 到 的 源 代 码 放 入 缓 存 中 , 以 便 以 后 的 调 用 能 够 直 接 使 用



该源代码。









26. 列举一下你所了解的 XML 技术及其应用

1、 数 据 交 换



用 XML 在 应 用 程 序 和 公 司 之 间 作 数 据 交 换 。 XML 使 用 元 素 和 属 性 来 描 述 数 据 。 在 数 据 传 送 过 程 中 , XML 始 终 保 留 了 诸 如 父 /子 关 系 这 样 的 数 据 结 构 。 几 个 应



用 程 序 可 以 共 享 和 解 析 同 一 个 XML 文 件 ,不 必 使 用 传 统 的 字 符 串 解 析 或 拆 解 过 程 。相 反 ,普 通 文 件 不 对 每 个 数 据 段 做 描 述 (除 了 在 头 文 件 中 ),也 不 保 留 数 据 关



系 结 构 。 使 用 XML 做 数 据 交 换 可 以 使 应 用 程 序 更 具 有 弹 性 , 因 为 可 以 用 位 置 (与 普 通 文 件 一 样 )或 用 元 素 名 (从 数 据 库 )来 存 取 XML 数 据 。



2、 Web 服 务



Web 服 务 让 使 用 不 同 系 统 和 不 同 编 程 语 言 的 人 们 能 够 相 互 交 流 和 分 享 数 据 。 其 基 础 在 于 Web 服 务 器 用 XML 在 系 统 之 间 交 换 数 据 。 交 换 数 据 通 常 用 XML 标 记 ,



能 使 协 议 取 得 规 范 一 致 , 比 如 在 简 单 对 象 处 理 协 议 (Simple Object Access Protocol, SOAP) 平 台 上 。 SOAP 可 以 在 用 不 同 编 程 语 言 构 造 的 对 象 之 间 传 递 消 息 。



这 意 味 着 一 个 C#对 象 能 够 与 一 个 Java 对 象 进 行 通 讯 。 这 种 通 讯 甚 至 可 以 发 生 在 运 行 于 不 同 操 作 系 统 上 的 对 象 之 间 。 DCOM, CORBA 或 Java RMI 只 能 在 紧 密



耦 合 的 对 象 之 间 传 递 消 息 , SOAP 则 可 在 松 耦 合 对 象 之 间 传 递 消 息 。



3、 内 容 管 理



XML 只 用 元 素 和 属 性 来 描 述 数 据 , 而 不 提 供 数 据 的 显 示 方 法 。 这 样 , XML 就 提 供 了 一 个 优 秀 的 方 法 来 标 记 独 立 于 平 台 和 语 言 的 内 容 。 使 用 象 XSLT 这 样 的 语



言 能 够 轻 易 地 将 XML 文 件 转 换 成 各 种 格 式 文 件 , 比 如 HTML,WML, PDF, flat file, EDI 等 等 。 XML 具 有 的 能 够 运 行 于 不 同 系 统 平 台 之 间 和 转 换 成 不 同 格 式 目 标



文件的能力使得它成为内容管理应用系统中的优秀选择。



4、 Web 集 成



使 为

现 在 有 越 来 越 多 的 设 备 也 支 持 XML 了 。 得 Web 开 发 商 可 以 在 个 人 电 子 助 理 和 浏 览 器 之 间 用 XML 来 传 递 数 据 。 什 么 将 XML 文 本 直 接 送 进 这 样 的 设 备 去 呢 ?



这 样 作 的 目 的 是 让 用 户 更 多 地 自 己 掌 握 数 据 显 示 方 式 ,更 能 体 验 到 实 践 的 快 乐 。常 规 的 客 户 /服 务 (C/S)方 式 为 了 获 得 数 据 排 序 或 更 换 显 示 格 式 ,必 须 向 服 务 器



发 出 申 请 ; 而 XML 则 可 以 直 接 处 理 数 据 , 不 必 经 过 向 服 务 器 申 请 查 询 -返 回 结 果 这 样 的 双 向 “ 旅 程 ” , 同 时 在 设 备 也 不 需 要 配 制 数 据 库 。 甚 至 还 可 以 对 设 备 上



的 XML 文 件 进 行 修 改 并 将 结 果 返 回 给 服 务 器 。



5、 配 制



XML 以 更 为 优 秀 的 方 式 为 应 用 程 序 标 记 配 制 数 据 。使 用 .NET 里 的 类 ,如 XmlDocument 和 XmlTextReader,将 配 制 数 据 标 记 为 XML 格 式 ,能 使 其 更 具 可 读 性 ,



并 能 方 便 地 集 成 到 应 用 系 统 中 去 。 使 用 XML 配 制 文 件 的 应 用 程 序 能 够 方 便 地 处 理 所 需 数 据 , 不 用 象 其 他 应 用 那 样 要 经 过 重 新 编 译 才 能 修 改 和 维 护 应 用 系



统。









27. 值类型和引用类型的区别?写出 C#的样例代码.

1. 通 用 类 型 系 统



C#中 , 变 量 是 值 还 是 引 用 仅 取 决 于 其 数 据 类 型 。



C#的 基 本 数 据 类 型 都 以 平 台 无 关 的 方 式 来 定 义 。 C#的 预 定 义 类 型 并 没 有 内 置 于 语 言 中 , 而 是 内 置 于 .NET Framework 中 。 .NET 使 用 通 用 类 型 系 统 ( CTS) 定 义 了



可 以 在 中 间 语 言 ( IL) 中 使 用 的 预 定 义 数 据 类 型 , 所 有 面 向 .NET 的 语 言 都 最 终 被 编 译 为 IL, 即 编 译 为 基 于 CTS 类 型 的 代 码 。



例 如 , 在 C#中 声 明 一 个 int 变 量 时 , 声 明 的 实 际 上 是 CTS 中 System.Int32 的 一 个 实 例 。 这 具 有 重 要 的 意 义 :



确 保 IL 上 的 强 制 类 型 安 全 ;



实 现 了 不 同 .NET 语 言 的 互 操 作 性 ;



所有的数据类型都是对象。它们可以有方法,属性,等。例如:



int i;



i = 1;



string s;



s = i.ToString();







2. 值 类 型



C#的 所 有 值 类 型 均 隐 式 派 生 自 System.ValueType:



结 构 体 : struct( 直 接 派 生 于 System.ValueType) ;



数值类型:



整型: ( , ( , (

sbyte System.SByte 的 别 名 ) short System.Int16) int System.Int32) long ( System.Int64) byte System.Byte) ushort System.UInt16)

, , ( , ( ,

29



uint ( System.UInt32) , ulong( System.UInt64) , char( System.Char) ;



浮 点 型 : float( System.Single) , double( System.Double) ;



用 于 财 务 计 算 的 高 精 度 decimal 型 : decimal( System.Decimal) 。



bool 型 : bool( System.Boolean 的 别 名 ) ;



用 户 定 义 的 结 构 体 ( 派 生 于 System.ValueType) 。



枚 举 : enum( 派 生 于 System.Enum) ;



可 空 类 型 ( 派 生 于 System.Nullable泛 型 结 构 体 , T?实 际 上 是 System.Nullable的 别 名 ) 。



每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。例如:



int i = new int();







等价于:



Int32 i = new Int32();







等价于:



int i = 0;







等价于:



Int32 i = 0;







使 用 new 运 算 符 时 , 将 调 用 特 定 类 型 的 默 认 构 造 函 数 并 对 变 量 赋 以 默 认 值 。 在 上 例 中 , 默 认 构 造 函 数 将 值 0 赋 给 了 i。 MSDN 上 有 完整的默认值表 。



关 于 int 和 Int32 的 细 节 , 在 我 的 另 一 篇 文 章 中 有 详 细 解 释 : 《 理解 C#中的 System.Int32 和 int 》 。

所 有 的 值 类 型 都 是 密 封 ( seal) 的 , 所 以 无 法 派 生 出 新 的 值 类 型 。



值得注意的是, 即 而 其

System.ValueType 直 接 派 生 于 System.Object。 System.ValueType 本 身 是 一 个 类 类 型 , 不 是 值 类 型 。 关 键 在 于 ValueType 重 写 了 Equals()



方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。



可 以 用 Type.IsValueType 属 性 来 判 断 一 个 类 型 是 否 为 值 类 型 :



TestType testType = new TestType ();



if (testTypetype.GetType().IsValueType)



{



Console.WriteLine("{0} is value type.", testType.ToString());



}







3. 引 用 类 型



C#有 以 下 一 些 引 用 类 型 :



数 组 ( 派 生 于 System.Array)



用户用定义的以下类型:



类 : class( 派 生 于 System.Object) ;



接 口 : interface( 接 口 不 是 一 个 “ 东 西 ” , 所 以 不 存 在 派 生 于 何 处 的 问 题 。 Anders 在 《 C# Programming Language 》 中 说 , 接 口 只 是 表 示 一 种

约 定 [contract]) ;



委 托 : delegate( 派 生 于 System.Delegate) 。



object( System.Object 的 别 名 ) ;



字 符 串 : string( System.String 的 别 名 ) 。



可以看出:



引用类型与值类型相同的是,结构体也可以实现接口;



引用类型可以派生出新的类型,而值类型不能;



引 用 类 型 可 以 包 含 null 值 , 值 类 型 不 能 ( 可 空 类 型 功 能 允 许 将 null 赋 给 值 类 型 ) ;



引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。



对 于 最 后 一 条 ,经 常 混 淆 的 是 string。我 曾 经 在 一本书 的 一 个 早 期 版 本 上 看 到 String 变 量 比 string 变 量 效 率 高 ;我 还 经 常 听 说 String 是 引 用 类 型 ,string



是值类型,等等。例如:



string s1 = "Hello, ";

30



string s2 = "world!";



string s3 = s1 + s2;//s3 is "Hello, world!"







这确实看起来像一个值类型的赋值。再如 :



string s1 = "a";



string s2 = s1;



s1 = "b";//s2 is still "a"







改 变 s1 的 值 对 s2 没 有 影 响 。 这 更 使 string 看 起 来 像 值 类 型 。 实 际 上 , 这 是 运 算 符 重 载 的 结 果 , 当 s1 被 改 变 时 , .NET 在 托 管 堆 上 为 s1 重 新 分 配 了 内 存 。 这



样 的 目 的 , 是 为 了 将 做 为 引 用 类 型 的 string 实 现 为 通 常 语 义 下 的 字 符 串 。



4. 值 类 型 和 引 用 类 型 在 内 存 中 的 部 署



经常听说,并且经常在书上看到:值类型部署在栈上,引用类型部署在托管堆上。实际上并没有这么简单。



MSDN 上说 : 托 管 堆 上 部 署 了 所 有 引 用 类 型 。 这 很 容 易 理 解 。 当 创 建 一 个 应 用 类 型 变 量 时 :

object reference = new object();







关 键 字 new 将 在 托 管 堆 上 分 配 内 存 空 间 ,并 返 回 一 个 该 内 存 空 间 的 地 址 。左 边 的 reference 位 于 栈 上 ,是 一 个 引 用 ,存 储 着 一 个 内 存 地 址 ;而 这 个 地 址 指 向 的



内 存 ( 位 于 托 管 堆 ) 里 存 储 着 其 内 容 ( 一 个 System.Object 的 实 例 ) 。 下 面 为 了 方 便 , 简 称 引 用 类 型 部 署 在 托 管 推 上 。



再 来 看 值 类 型 。 《 C#语言规范 》 上 的 措 辞 是 “ 结 构 体 不 要 求 在 堆 上 分 配 内 存 ( However, unlike classes, structs are value types and do not require



heap allocation) ” 而 不 是 “ 结 构 体 在 栈 上 分 配 内 存 ” 。 这 不 免 容 易 让 人 感 到 困 惑 : 值 类 型 究 竟 部 署 在 什 么 地 方 ?



4.1 数 组



考虑数组:



int[] reference = new int[100];







根 据 定 义 , 数 组 都 是 引 用 类 型 , 所 以 int 数 组 当 然 是 引 用 类 型 ( 即 reference.GetType().IsValueType 为 false) 。



而 int 数 组 的 元 素 都 是 int,根 据 定 义 ,int 是 值 类 型( 即 reference[i].GetType().IsValueType 为 true)。那 么 引 用 类 型 数 组 中 的 值 类 型 元 素 究 竟 位 于 栈 还



是堆?



如 果 用 WinDbg 去 看 reference[i]在内存中的具体位置 , 就 会 发 现 它 们 并 不 在 栈 上 , 而 是 在 托 管 堆 上 。

实际上,对于数组:



TestType[] testTypes = new TestType[100];







如 果 TestType 是 值 类 型 , 则 会 一 次 在 托 管 堆 上 为 100 个 值 类 型 的 元 素 分 配 存 储 空 间 , 并 自 动 初 始 化 这 100 个 元 素 , 将 这 100 个 元 素 存 储 到 这 块 内 存 里 。



如 果 TestType 是 引 用 类 型 ,则 会 先 在 托 管 堆 为 testTypes 分 配 一 次 空 间 ,并 且 这 时 不 会 自 动 初 始 化 任 何 元 素( 即 testTypes[i]均 为 null)。等 到 以 后 有 代 码



初始化某个元素的时候,这个引用类型元素的存储空间才会被分配在托管堆上。



4.2 类 型 嵌 套



更容易让人困惑的是引用类型包含值类型,以及值类型包含引用类型的情况:



public class ReferenceTypeClass



{



private int _valueTypeField;



public ReferenceTypeClass()



{



_valueTypeField = 0;



}



public void Method()



{



int valueTypeLocalVariable = 0;



}



}



ReferenceTypeClass referenceTypeClassInstance = new ReferenceTypeClass();//Where is _valueTypeField?



referenceTypeClassInstance.Method();//Where is valueTypeLocalVariable?

31







public struct ValueTypeStruct



{



private object _referenceTypeField;



public ValueTypeStruct()



{



_referenceTypeField = new object();



}



public void Method()



{



object referenceTypeLocalVariable = new object();



}



}



ValueTypeStruct valueTypeStructInstance = new ValueTypeStruct();//Where is _referenceTypeField?



valueTypeStructInstance.Method(); //Where is referenceTypeLocalVariable?







单 看 valueTypeStructInstance, 这 是 一 个 结 构 体 实 例 , 感 觉 似 乎 是 整 块 扔 到 栈 上 的 。 但 是 字 段 _referenceTypeField 是 引 用 类 型 , 局 部 变 量



referenceTypeLocalVarible 也 是 引 用 类 型 。



referenceTypeClassInstance 也 有 同 样 的 问 题 ,referenceTypeClassInstance 本 身 是 引 用 类 型 ,似 乎 应 该 整 块 部 署 在 托 管 堆 上 。但 字 段 _valueTypeField 是 值



类 型 , 局 部 变 量 valueTypeLocalVariable 也 是 值 类 型 , 它 们 究 竟 是 在 栈 上 还 是 在 托 管 堆 上 ?



规律是:



引用类型部署在托管堆上;



值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。



我 们 来 分 析 一 下 上 面 的 代 码 。 对 于 引 用 类 型 实 例 , 即 referenceTypeClassInstance:



从 上 下 文 看 , referenceTypeClassInstance 是 一 个 局 部 变 量 , 所 以 部 署 在 托 管 堆 上 , 并 被 栈 上 的 一 个 引 用 所 持 有 ;



值 类 型 字 段 _valueTypeField 属 于 引 用 类 型 实 例 referenceTypeClassInstance 的 一 部 分 , 所 以 跟 随 引 用 类 型 实 例 referenceTypeClassInstance 部 署 在 托 管 堆



上(有点类似于数组的情形);



valueTypeLocalVariable 是 值 类 型 局 部 变 量 , 所 以 部 署 在 栈 上 。



而 对 于 值 类 型 实 例 , 即 valueTypeStruct:



根 据 上 下 文 , 值 类 型 实 例 valueTypeStructInstance 本 身 是 一 个 局 部 变 量 而 不 是 字 段 , 所 以 位 于 栈 上 ;



其 引 用 类 型 字 段 _referenceTypeField 不 存 在 跟 随 的 问 题 , 必 然 部 署 在 托 管 堆 上 , 并 被 一 个 引 用 所 持 有 ( 该 引 用 是 valueTypeStruct 的 一 部 分 , 位 于 栈 ) ;



其 引 用 类 型 局 部 变 量 referenceTypeLocalVariable 显 然 部 署 在 托 管 堆 上 , 并 被 一 个 位 于 栈 的 引 用 所 持 有 。



所以,简单地说“值类型存储在栈上,引用类型存储在托管堆上 ”是不对的。必须具体情况具体分析。



5. 正 确 使 用 值 类 型 和 引 用 类 型



这 一 部 分 主 要 参 考 《 Effective C# 》 , 并 非 本 人 原 创 , 希 望 能 让 你 加 深 对 值 类 型 和 引 用 类 型 的 理 解 。

5.1 辨 明 值 类 型 和 引 用 类 型 的 使 用 场 合



C#中 , 我 们 用 struct/class 来 声 明 一 个 类 型 为 值 类 型 /引 用 类 型 。



考虑下面的例子:



TestType[] testTypes = new TestType[100];







则 大 而 刚 分

如 果 TestTye 是 值 类 型 , 只 需 要 一 次 分 配 , 小 为 TestTye 的 100 倍 。 如 果 TestTye 是 引 用 类 型 , 开 始 需 要 100 次 分 配 , 配 后 数 组 的 各 元 素 值 为 null,



然 后 再 初 始 化 100 个 元 素 ,结 果 总 共 需 要 进 行 101 次 分 配 。这 将 消 耗 更 多 的 时 间 ,造 成 更 多 的 内 存 碎 片 。所 以 ,如 果 类 型 的 职 责 主 要 是 存 储 数 据 ,值 类 型 比 较



合适。



一 般 来 说 , 值 类 型 ( 不 支 持 多 态 ) 适 合 存 储 供 C#应 用 程 序 操 作 的 数 据 , 而 引 用 类 型 ( 支 持 多 态 ) 应 该 用 于 定 义 应 用 程 序 的 行 为 。



通 常 我 们 创 建 的 引 用 类 型 总 是 多 于 值 类 型 。 如 果 以 下 问 题 的 回 答 都 为 yes, 那 么 我 们 就 应 该 创 建 为 值 类 型 :



该类型的主要职责是否用于数据存储?



该类型的共有借口是否完全由一些数据成员存取属性定义?



是否确信该类型永远不可能有子类?



是否确信该类型永远不可能具有多态行为?

32



5.2 将 值 类 型 尽 可 能 实 现 为 具 有 常 量 性 和 原 子 性 的 类 型



具有常量性的类型很简单:



如果构造的时候验证了参数的有效性,之后就一直有效;



省去了许多错误检查,因为禁止更改;



确 保 线 程 安 全 , 因 为 多 个 reader 访 问 到 同 样 的 内 容 ;



可以安全地暴露给外界,因为调用者不能更改对象的内部状态。



具有原子性的类型都是单一的实体, 我们通常会直接替换一个原子类型的整个内容。



下面是一个典型的可变类型:



public struct Address



{



private string _city;



private string _province;



private int _zipCode;



public string City



{



get { return _city; }



set { _city = value; }



}



public string Province



{



get { return _province; }



set



{



ValidateProvince(value);



_province = value;



}



}



public int ZipCode



{



get { return _zipCode; }



set



{



ValidateZipCode(value);



_zipCode = value;



}



}



}







下面创建一个实例:



Address address = new Address();



address.City = "Chengdu";



address.Province = "Sichuan";



address.ZipCode = 610000;







然后更改这个实例:



address.City = "Nanjing"; //Now Province and ZipCode are invalid



address.ZipCode = 210000; //Now Province is still invalid



address.Province = "Jiangsu";

33



可 见 ,内 部 状 态 的 改 变 意 味 着 可 能 违 反 对 象 的 不 变 式( invariant),至 少 是 临 时 的 违 反 。如 果 上 面 是 一 个 多 线 程 的 程 序 ,那 么 在 City 更 改 的 过 程 中 ,另 一 个



线程可能看到不一致的数据视图。如果不是多线程的程序,也有问题:



当 ZipCode 的 值 无 效 而 抛 出 异 常 时 , 对 象 仅 作 了 一 部 分 改 变 , 因 此 处 于 无 效 的 状 态 , 为 了 修 复 这 个 问 题 , 需 要 在 Address 中 添 加 相 当 多 的 内 部 校 验 代 码 ;



为了实现异常安全,我们需要在所有改变多个字段的客户代码处放上防御性的代码;



线程安全也要求我们在每一个属性的访问器上添加线程同步检查。



显 然 , 这 是 一 个 相 当 可 观 的 工 作 量 。 下 面 我 们 把 Address 实 现 为 常 量 类 型 :



public struct Address



{



private string _city;



private string _province;



private int _zipCode;



public Address (string city, string province, int zipCode)



{



_city = city;



_province = province;



_zipCode = zipCode;



ValidateProvince(province);



ValidateZipCode(zipCode);



}



public string City



{



get { return _city; }



}



public string Province



{



get { return _province; }



}



public int ZipCode



{



get { return _zipCode; }



}



}







如 果 要 改 变 Address, 不 能 修 改 现 有 的 实 例 , 只 能 创 建 一 个 新 的 实 例 :



Address address = new Address("Chengdu", "Sichuan", 610000);//create a instance



address = new Address("Nanjing", "Jiangsu", 210000);//modify the instance







address 将 不 存 在 任 何 无 效 的 临 时 状 态 。 那 些 临 时 状 态 只 存 在 于 Address 的 构 造 函 数 执 行 过 程 中 。 这 样 一 来 , Address 是 异 常 安 全 的 , 也 是 线 程 安 全 的 。



5.3 确 保 0 为 值 类 型 的 有 效 状 态



.NET 的 默 认 初 始 化 机 制 会 将 引 用 类 型 设 置 为 二 进 制 意 义 上 的 0,即 null。而 对 于 值 类 型 ,不 论 我 们 是 否 提 供 构 造 函 数 ,都 会 有 一 个 默 认 的 构 造 函 数 ,将 其 设 置



为 0。



一种典型的情况是枚举:



public enum Sex



{



Male = 1;



Female = 2;



}







然后用做值类型的成员:

34



public struct Employee



{



private Sex _sex;



//other



}







创 建 Employee 结 构 体 将 得 到 一 个 无 效 的 Sex 字 段 :



Employee employee = new Employee ();







employee 的 _sex 是 无 效 的 , 因 为 其 为 0。 我 们 应 该 将 0 作 为 一 个 为 初 始 化 的 值 明 确 表 示 出 来 :



public Sex



{



None = 0;



Male = 1;



Female = 2;



}







如果值类型中包含引用类型,会出现另一种初始化问题:



public struct ErrorLog



{



private string _message;



//other



}







然 后 创 建 一 个 ErrorLog:



ErrorLog errorLog = new ErrorLog ();







errorLog 的 _message 字 段 将 是 一 个 空 引 用 。 我 们 应 该 通 过 一 个 属 性 来 将 _message 暴 露 给 客 户 代 码 , 从 而 使 该 问 题 限 定 在 ErrorLog 的 内 部 :



public struct ErrorLog



{



private string _message;



public string Message



{



get



{



return (_message ! = null) ? _message : string.Empty;



}



set { _message = value; }



}



//other



}







5.4 尽 量 减 少 装 箱 和 拆 箱



装箱指把一个值类型放入一个未具名类型的引用类型中,比如:



int valueType = 0;



object referenceType = i;//boxing







拆箱则是从前面的装箱对象中取出值类型:



object referenceType;

35



int valueType = (int)referenceType;//unboxing







装 箱 和 拆 箱 是 比 较 耗 费 性 能 的 , 还 会 引 入 一 些 诡 异 的 bug, 我 们 应 当 避 免 装 箱 和 拆 箱 。



装箱和拆箱最大的问题是会自动发生。比如:



Console.WriteLine("A few numbers: {0}, {1}.", 25, 32);







其 中 , Console.WriteLine()接 收 的 参 数 类 型 是 (string, object, object)。 因 此 , 实 际 上 会 执 行 以 下 操 作 :



int i = 25;



obeject o = i;//boxing







然 后 把 o 传 给 WriteLine()方 法 。 在 WriteLine()方 法 的 内 部 , 为 了 调 用 i 上 的 ToString()方 法 , 又 会 执 行 :



int i = (int)o;//unboxing



string output = i,ToString();







所以正确的做法应该是:



Console.WriteLine("A few numbers: {0}, {1}.", 25.ToString(), 32.ToString());







25.ToString()只 是 执 行 一 个 方 法 并 返 回 一 个 引 用 类 型 , 不 存 在 装 箱 /拆 箱 的 问 题 。



另 一 个 典 型 的 例 子 是 ArryList 的 使 用 :



public struct Employee



{



private string _name;



public Employee(string name)



{



_name = name;



}



public string Name



{



get { return _name; }



set { _name = value; }



}



public override string ToString()



{



return _name;



}



}



ArrayList employees = new ArrayList();



employees.Add(new Employee("Old Name"));//boxing



Employee ceo = (Employee)employees[0];//unboxing



ceo.Name = "New Name";//employees[0].ToString() is still "Old Name"







上面的代码不仅存在性能的问题,还容易导致错误发生。



在这种情况下,更好的做法是使用泛型集合:



List employees = new List();







由 于 List是 强 类 型 的 集 合 , employees.Add()方 法 不 进 行 类 型 转 换 , 所 以 不 存 在 装 箱 /拆 箱 的 问 题 。



6. 总 结



C#中 , 变 量 是 值 还 是 引 用 仅 取 决 于 其 数 据 类 型 。



C#的 值 类 型 包 括 : 结 构 体 ( 数 值 类 型 , bool 型 , 用 户 定 义 的 结 构 体 ) , 枚 举 , 可 空 类 型 。

36



C#的 引 用 类 型 包 括 : 数 组 , 用 户 定 义 的 类 、 接 口 、 委 托 , object, 字 符 串 。



数组的元素,不管是引用类型还是值类型,都存储在托管堆上。



引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。为了方便,本文简称引用类型部署在托管推上。



值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。



值类型在内存管理方面具有更好的效率,并且不支持多态,适合用作存储数据的载体;引用类型支持多态,适合用于定义应用程序的行为。



应该尽可能地将值类型实现为具有常量性和原子性的类型。



应该尽可能地确保 0 为值类型的有效状态。



应该尽可能地减少装箱和拆箱。









1. ADO.net 中常用的对象有哪些?分别描述一下.

Command 对 象



1.Command 对 象 概 述



Command 对 象 可 以 使 用 数 据 库 命 令 直 接 与 数 据 源 进 行 通 信 。 它 的 属 性 如 下 :



Name: Command 对 象 的 程 序 化 名 称 。 在 代 码 中 使 用 此 名 称 来 引 用 Command 对 象 。



Connection: 对 Connection 对 象 的 引 用 , Command 对 象 将 使 用 该 对 象 与 数 据 库 通 信 。



CommandType: Text | StoreProduce | TableDirect 。



CommandText: SQL 语 句 | StoreProduce。



Parameters: 命 令 对 象 包 含 的 参 数 。



可 以 通 过 一 下 三 种 方 式 来 创 建 Command 对 象 :



方式一



string strConn, strSQL;



strConn = "Provider=SQLOLEDB;Data Source=(local) \\NetSDK;Initial Catalog=Northwind;Trusted_Connection=Yes;";



strSQL = "SELECT CustomerID,CompanyName FROM Customers";



OleDbConnection cn = new OleDbConnection(strConn);



cn.Open();



OleDbCommand cmd;







cmd = cn.CreateCommand();



cmd.CommandText = strSQL;



方式二



string strConn, strSQL;



strConn = "Provider=SQLOLEDB;Data Source=(local) \\NetSDK;Initial Catalog=Northwind;Trusted_Connection=Yes;";



strSQL = "SELECT CustomerID,CompanyName FROM Customers";



OleDbConnection cn = new OleDbConnection(strConn);



cn.Open();



OleDbCommand cmd;







cmd = new OleDbCommand();



cmd.CommandText = strSQl;



cmd.Connection = cn;方 式 三



string strConn, strSQL;



strConn = "Provider=SQLOLEDB;Data Source=(local) \\NetSDK;Initial Catalog=Northwind;Trusted_Connection=Yes;";



strSQL = "SELECT CustomerID,CompanyName FROM Customers";



OleDbConnection cn = new OleDbConnection(strConn);



cn.Open();



OleDbCommand cmd;







cmd = new OleDbCommand(strSQL, cn);

37







2.Command 对 象 参 数



常用属性如下:



ParameterName: 参 数 名 称 , 如 "@CatID"。



DbType, SqlType, OleDbType: 参 数 的 数 据 类 型 。



Direction: ParameterDirection 枚 举 值 。



ParameterDirection.Input( 默 认 值 ) | ParameterDirection.InputOutput |



ParameterDirection.Output | ParameterDirection.ReturnValue







3.执 行



四种执行方式:



ExecuteNonQuery() 返 回 受 命 令 影 响 的 行 数 。



ExecuteScalar() 返 回 第 一 行 第 一 列 ( 使 用 与 集 函 数 ) 。 如 Count(*),Sum,Avg 等 聚 合 函 数 。



如 则 (

ExecuteReader() 返 回 一 个 DataReader 对 象 。 果 SQL 不 是 查 询 Select, 返 回 一 个 没 有 任 何 数 据 的 System.Data.SqlClient.SqlDataReader 类 型 的 集 合 EOF)。



ExecuteXmlReader()返 回 一 个 XmlReader 对 象 。



4.参 数 化 查 询



使用示例



string strConn, strSQL;



strConn = "Provider=SQLOLEDB;Data Source=(local) \\NetSDK;Initial Catalog=Northwind;Trusted_Connection=Yes;";



OleDbConnection cn = new OleDbConnection(strConn);



cn.Open();



strSQl = "SELECT OrderID,CustomerID,EmployeeID,OrderDate FROM Order WHERE CustomerID = ?";



OleDbCommand cmd = new OleDbCommand(strSQl,cn);



cmd.Parameters.Add("@CustomerID",OleDbType.Wchar,5);



cmd.Parameters[0].Value = "ALFKI";



OleDbDataReader rdr = cmd.ExecuteReader();



----------------------------------------------------------------------------------------------------------------------------



Connection 对 象 可 用 于 连 接 到 数 据 库 管 理 数 据 库 的 事 务 。



1.Connection 对 象 的 创 建



4 种数据库连接方式:



OLEDB:System.Data.Oledb.OledbConnection



SQL:System.Data.SqlCilent.SqlConnection



ODBC:System.Data.Odbc.OdbcConnection



Oracle:System.Data.OracleClient.OracleConnection







我 们 来 看 一 个 OleDbConnection 创 建 的 实 例 , 一 共 有 2 种 方 法 。



第 一 种 : 创 建 一 个 没 有 初 始 化 的 OleDbConnection 对 象 , 然 后 用 ConnectionString 来 初 始 化 。



创 建 Connection 对 象



String strConn;



strConn = "Provider=SQLOLEDB;Data Source={local) \\NETSDK;Initial Catalog=Northwind;Trusted_Connection=Yes;";



OleDbConnection cn;



cn = new OleDbConnection();



cn.ConnectionString = strConn;







第 二 种 : 使 用 构 造 函 数 来 初 始 化 OleDbConnection 对 象 。



创 建 Connection 对 象



String strConn;



strConn = "Provider=SQLOLEDB;Data Source={local) \\NETSDK;Initial Catalog=Northwind;Trusted_Connection=Yes;" ;



OleDbConnection cn;

38



cn = new OleDbConnection(strConn);







2.连 接 字 符 串



连接字符串的几个常用参数:



Provider: 这 个 属 性 用 于 设 置 或 返 回 连 接 提 供 程 序 的 名 称 , 仅 用 于 OleDbConnection 对 象 。



Connection Timeout 或 Connect Timeout: 在 中 止 尝 试 并 产 生 异 常 前 , 等 待 连 接 到 服 务 器 的 连 接 时 间 长 度 ( 以 秒 为 单 位 ) 。 默 认 是 15 秒 。



Initail Catalog: 数 据 库 的 名 称 。



Data Source: 连 接 打 开 时 使 用 的 SQL Server 名 称 , 或 者 是 Microsoft Access 数 据 库 的 文 件 名 。



Password: SQL Server 帐 户 的 登 录 密 码 。



User ID: SQL Server 登 录 帐 户 。



Integrated Security 或 Trusted Connection: 此 参 数 决 定 是 否 是 安 全 连 接 。 可 能 的 值 有 True、 False 和 SSPI( SSPI 是 True 的 同 义 词 ) 。



Persist Security Info: 当 设 置 为 False 时 , 如 果 连 接 是 打 开 的 或 曾 经 处 于 打 开 状 态 , 那 么 安 全 敏 感 信 息 ( 如 密 码 ) 不 会 作 为 连 接 的 一 部 分 返 回 。 设 置 属 性



值 为 True 可 能 有 安 全 风 险 。 False 是 默 认 值 。







3.打 开 和 关 闭 连 接



Open 方 法 : 打 开 连 接



Close 方 法 : 关 闭 连 接



注:如果使用了连接池,关闭连接对象不会真正地关闭对数据源地连接。



如 果 只 用 DataAdapter, 就 不 必 显 式 地 打 开 和 关 闭 连 接 。 当 调 用 这 些 对 象 的 方 法 时 ( Fill 方 法 、 Update 方 法 ) 会 自 动 检 查 连 接 是 否 打 开 。



----------------------------------------------------------------------------------- -----------------------------------------



DataReader 对 象



1.DataReader 对 象 概 述



但 如 可 但

DataReader 对 象 只 能 对 查 询 获 得 的 数 据 集 进 行 自 上 而 下 的 访 问 , 效 率 很 高 。 果 仅 仅 是 访 问 数 据 的 话 , 以 使 用 DataReader。 DataReader 要 求 一 直 连 接 ,



读 相 这 带

所以将结果的一小部分先放在内存中, 完后再从数据库中读取一部分, 当于一个缓存机制。 对于查询结果百万级的情况来说, 来的好处是显而易见的。



DataReader 对 象 有 如 下 几 个 特 点 :



快 速 访 问 数 据 。 由 于 DataReader 对 象 是 只 进 和 只 读 的 , 所 以 开 销 相 对 较 小 , 速 度 比 DataSet 快 。



只进和只读。不能处理数据,只能显示数据。



自 己 管 理 连 接 。 DataAdapter 对 象 可 以 自 动 地 打 开 和 关 闭 连 接 , DataReader 对 象 必 须 显 式 地 打 开 和 关 闭 连 接 。



使用较少地服务器资源。







2.创 建 DataReader 对 象



具体步骤:



( 1) 创 建 和 打 开 数 据 库 连 接 。



( 2) 创 建 一 个 Command 对 象 。



( 3) 从 Command 对 象 中 创 建 DataReader。



( 4) 执 行 ExecuteReader 对 象 。



( 5) 使 用 DataReader 对 象 。



( 6) 关 闭 DataReader 对 象 。



( 7) 关 闭 Connection 对 象 。







DataReader 对 象 创 建 示 例



DataReader 对 象 使 用 示 例



//打 开 Connection 并 创 建 Command



SqlConnection conn = new SqlConnection("data source=localhost;integrated security=true;initial catalog=pubs;");



conn.Open();



SqlCommand cmdAuthors = new SqlCommand("select * from Authors", conn);







//创 建 DataReader 对 象 并 读 取 数 据



SqlDataReader dr;

39



dr = cmdAuthors.ExecuteReader();



while(dr.Read())



{



ListBox.Items.Add(dr["au_lname"]+","+dr["au_fname"]);



}







//关 闭 DataReader 和 Connection



dr.Close();



conn.Close();







当 使 用 DataReader 对 象 进 行 连 接 时 , 需 要 使 用 Try...Catch...Finally 语 句 , 这 样 可 以 确 保 若 在 某 方 面 失 败 , 连 接 将 会 关 闭 。 否 则 , 连 接 会 无 限 期 保 持 打



开状态。







捕捉错误



try



{



conn.Open();



dr = cmdAuthors.ExecuteReader();



//使 用 DataReader 中 返 回 的 数 据



}



catch



{



//错 误 处 理



}



finally



{



dr.Close();



conn.Close();



}



3.从 DataReader 读 取 数 据



为 每 个 记 录 调 用 Read 方 法 :可 以 调 用 Read 方 法 来 访 问 DataRead 对 象 中 的 一 个 记 录 ,因 为 DataReader 对 象 中 的 默 认 位 置 是 在 第 一 个 记 录 的 前 面 ,所 以 必 须



在 访 问 任 何 数 据 之 前 调 用 Read 方 法 。 当 不 再 有 可 用 记 录 时 , Read 方 法 就 返 回 一 个 空 值 。



调 用 Read 方 法 示 例



while (dr.Read())



{



lbName.Text += dr["au_name"];



}



访 问 字 段 : 可 以 通 过 顺 序 位 置 , 名 字 或 者 调 用 适 当 的 Get 方 法 来 访 问 一 个 字 段 , Get 方 法 包 括 GetDateTime, GetDouble, GetInt32 或 GetString 等 。



调 用 Get 方 法 来 访 问 数 据



dr.Read();



lbName.Text =dr.GetString(1) + "," +dr.GetString(2);



通过名字引用当前记录的数据字段



dr["au_fname"];







----------------------------------------------------------------------------------- -----------------------------------------



在 ADO.NET 中 DataSet 的 作 用 是 为 数 据 源 提 供 一 个 断 开 式 的 存 储 , 而 不 必 关 心 数 据 源 , 操 作 只 用 在 DataSet 中 进 行 就 行 了 。



DataSet 中 的 几 个 重 要 对 象 :



TablesCollection 对 象 : 一 这

DataSet 里 的 表 用 DataTable 来 表 示 , 个 DataSet 里 面 可 以 包 含 多 个 DataTable, 些 DataTable 就 构 成 了 TablesCollection 对 象 。



每 个 DataTable 中 都 包 含 一 个 ColumnsColleciton 和 一 个 RowsCollection 对 象 。

40



RelationsCollection 对 象 : 各 个 DataTable 之 间 的 关 系 通 过 DataRelation 来 表 达 , 这 些 DataRelation 构 成 的 集 合 就 是 RelationsCollection 对 象 。



ExtendedProperties 对 象 : 这 个 对 象 用 来 定 义 特 定 的 信 息 , 比 如 密 码 、 更 新 时 间 等 。







1.DataTable 对 象



创 建 一 个 DataTable:



DataTable MyTable;



MyTable = new DataTable ("Test");



MyTable.CaseSensitive = False;// 是 否 区 分 大 小 写



MyTable.MinimumCapacity = 100;// 数 据 库 的 最 小 记 录 空 间







创建表列



DataTable MyTable;



DataColumn MyColumn;



MyTable = new DataTable ("表 名 ");



MyColumn = MyTable.Columns.Add(" 列 名 ", typeof(string));



MyColumn = MyTable.Columns.Add(" 列 名 ", typeof(int));



创建表达式列



示例



//方 法 一



DataColumn tax = new DataColumn();



tax.DataType = typeof(Currency);



tax.Expression = "total*rate*0.20";



//方 法 二



MyTable.Columns.Add("tax", typeof(Currency), "total*rate*0.20");







2.DataView 对 象



DataView 就 时 数 据 视 图 , 为 数 据 库 结 构 提 供 了 外 模 式 的 实 现 。 同 时 DataView 也 可 以 为 窗 体 控 件 和 Web 控 件 提 供 数 据 绑 定 功 能 , 在 每 一 个 DataTable 中 内 建



了 一 个 DataView 为 : DataTable.DefaultView()。



创 建 DataView:



DataView sortedView=new DataView(dataTable);



对 DataView 进 行 排 序 :



dataTable.DefaultView.sort="lastName";



dataTable.DefaultView.sort="lastName,FirstName DESC";



对 DataView 进 行 筛 选 和 排 序 :



通 过 RowFilter 属 性 设 置 实 现 筛 选



DataView dv = new DataView(ds.Tables["Authors"]);



dv.RowFilter = "state = 'CA'";



dv.Sort = "au_lname";







3.DataColumn 对 象



示例



DataColumn colCustomerID = dtCustomers.Columns.Add("CustomerId",typeof(Int32));



colCustomerID.AllowDBNull = false;



colCustomerID.Unique = true;







4.DataRow 对 象



调 用 NewRow 方 法 来 创 建 一 个 新 的 DataRow 对 象



创 建 DataRow 对 象



DataRow drNewEmployee = dtEmployees.NewRow();

41



//使 用 索 引 或 列 名 操 作 新 行



drNewEmployee(0) = 11;



drNewEmployee(1) = "Smith";



//调 用 Add 方 法 将 行 添 加 到 DataRowCollection 中



dtEmployees.Rows.Add(drNewEmployee);



对行进行批处理更改:



BeginEdit()开 始 更 改 , EndEdit()结 束 更 改 , 同 时 将 更 改 结 果 写 入 DataSet, CancelEdit(), 取 消 更 改



例如:



row.BeginEdit();



对 row 进 行 更 改



row.EndEdit();







从 DataTable 中 删 除 DataRow 对 象 :



一 : DataRowCollection 对 象 的 Remove 方 法



示例



DataRow drEmployee = dtEmployees.Rows(3);



dtEmployees.Rows.Remove(drEmployee);



二 : DataRow 对 象 的 Delete 方 法



示例



drEmployee.Delete;



比 较 : Remove 方 法 时 从 DataRowCollection 中 删 除 DataRow, 而 Dalete 方 法 只 是 对 删 除 的 行 做 标 记 。



DataRow 类 包 括 RowState 属 性 。RowState 属 性 值 表 示 从 第 一 次 创 建 DataTable( 或 从 数 据 库 加 载 DataTable)开 始 ,行 是 否 发 生 更 改 ,如 何 更 改 以 及 通 过 何



种 方 式 更 改 。 属 性 的 可 选 值 : Modified | Detached | Added。







5.创 建 表 关 系



示例



//创 建 DataRelation



DataRelation dr;



DataColumn parentCol;



DataColumn childCol;







parentCol = ds.Tables["Customers"].Columns["CustomerID"];



childCol = ds.Tables["Orders"].Columns.["CustomerID"];



dr = new DataRelation("CustOrders", parentCol, childCol);



ds.Relations.Add(dr);







currentParentRow = ds.Tables["Customers"].Rows[DataGridName.SelectedIndex];



foreach(DataRow r in currentParentRow.GetChildRow("CustOrders"))



{



Lable1.Text += r["OrderID"] + ",";



}



6.绑 定 数 据



示例一



GridView.DataSource = ds;



GridView.DataMember = "Authors";



GridView.DataBind();示 例 二



GridView.DataSource = ds.Tables["Authors"];



GridView.DataBind();示 例 三



DataView dv = new DataView(ds.Tables["Authors"]);

42



dv.RowFilter = "state = 'CA'";



GridView.DataSource = dv;



GridView.DataBind();







DataAdapter( 即 数 据 适 配 器 ) 对 象 是 一 种 用 来 充 当 DataSet 对 象 与 实 际 数 据 源 之 间 桥 梁 的 对 象 。 DataSet 对 象 是 一 个 非 连 接 的 对 象 , 它 与 数 据 源 无 关 。 而



DataAdapter 则 正 好 负 责 填 充 它 并 把 它 的 数 据 提 交 给 一 个 特 定 的 数 据 源 , 它 与 DataSet 配 合 使 用 , 可 以 执 行 新 增 、 查 询 、 修 改 和 删 除 等 多 种 操 作 。



用 以 两

DataAdapter 对 象 是 一 个 双 向 通 道 , 来 把 数 据 从 数 据 源 中 读 到 一 个 内 存 表 中 , 及 把 内 存 中 的 数 据 写 回 到 一 个 数 据 源 中 。 种 情 况 下 使 用 的 数 据 源 可 能 相 同 ,



也 可 能 不 相 同 。 而 这 两 种 操 作 分 别 称 作 填 充 ( fill) 和 更 新 ( update) 。



DataAdapter 对 象 的 常 用 属 性 如 表 1 所 示 。



表1 SqlDataAdapter 对 象 的 属 性



属性 描述



DeleteCommand 获取或设置一个语句或存储过程,以从数据集删除记录



InsertCommand 获取或设置一个语句或存储过程,以在数据源中插入新记录



SelectCommand 获取或设置一个语句或存储过程,用于在数据源中选择记录



UpdateBatchSize 获取或设置每次到服务器的往返过程中处理的行数



UpdateCommand 获取或设置一个语句或存储过程,用于更新数据源中的记录



DataAdapter 对 象 的 常 用 方 法 如 表 2 所 示



表2 SqlDataAdapter 对 象 的 方 法



方法 描述



Dispose 删除该对象



Fill 用 从 源 数 据 读 取 的 数 据 行 填 充 至 DataSet 对 象 中



FillSchema 将 一 个 DataTable 加 入 到 指 定 的 DataSet 中 , 并 配 置 表 的 模 式



GetFillParameters 返 回 一 个 用 于 SELECT 命 令 的 DataParameter 对 象 组 成 的 数 组



Update 在 DataSet 对 象 中 的 数 据 有 所 改 动 后 更 新 数 据 源 。



关 于 DataAdapter 对 象 中 常 用 的 Fill 和 Update 方 法 , 下 面 以 SqlDataAdapter( DataSet 和 SQL Server 之 间 的 桥 接 器 ) 为 例 进 行 说 明 。



当 SqlDataAdapter 对 象 通 过 Fill 方 法 填 充 DataSet 对 象 时 , 它 为 返 回 的 数 据 创 建 必 需 的 表 和 列 ( 如 果 这 些 表 和 列 尚 不 存 在 ) 。 但 是 , 除 非 将



MissingSchemaAction 属 性 设 置 为 AddWithKey, 则 这 个 隐 式 创 建 的 架 构 中 不 包 括 主 键 信 息 。 可 以 使 用 FillSchema 方 法 , SqlDataAdapter 对 象 创 建 DataSet

否 也 让



对象的架构,并在用数据填充它之前就将主键信息包括进去









28. 如何理解委托和事件?

委 托 是 一 种 定 义 方 法 签 名 的 类 型 ,可 以 与 具 有 兼 容 签 名 的 任 何 方 法 关 联 。您 可 以 通 过 委 托 调 用 方 法 。委 托 用 于 将 方 法 作 为 参 数 传 递 给 其 他 方 法 。事 件 处 理 程 序



就 是 通 过 委 托 调 用 的 方 法 。 您 可 以 创 建 一 个 自 定 义 方 法 , 当 发 生 特 定 事 件 时 某 个 类 ( 例 如 Windows 控 件 ) 就 可 以 调 用 您 的 方 法 。 下 面 的 示 例 演 示 了 一 个 委



托声明:



public delegate int PerformCalculation(int x, int y);



与 委 托 的 签 名( 由 返 回 类 型 和 参 数 组 成 )匹 配 的 任 何 可 访 问 类 或 结 构 中 的 任 何 方 法 都 可 以 分 配 给 该 委 托 。方 法 可 以 是 静 态 方 法 ,也 可 以 是 实 例 方 法 。 这 样 就 可



以通过编程方式来更改方法调用,还可以向现有类中插入新代码。只要知道委托的签名,就可以分配您自己的方法。



在方法重载的上下文中,方法的签名不包括返回值。但 在委托的上下文中,签名的确包括返回值 。换句话说,方法和委托必须具有相同的返回值。







将 方 法 作 为 参 数 进 行 引 用 的 能 力 使 委 托 成 为 定 义 回 调 方 法 的 理 想 选 择 。例 如 ,可 以 向 排 序 算 法 传 递 对 比 较 两 个 对 象 的 方 法 的 引 用 。分 离 比 较 代 码 使 得 可 以 采 用



更通用的方式编写算法。







委托概述



委托具有以下特点:



委 托 类 似 于 C++ 函 数 指 针 , 但 它 们 是 类 型 安 全 的 。



委托允许将方法作为参数进行传递。



委托可用于定义回调方法。



委托可以链接在一起;例如,可以对一个事件调用多个方法。

43



方法不必与委托签名完全匹配。有关更多信息,请参见 Covariance and Contravariance 委托中 的 协变和逆 变( C# 编程 指南) 。

C# 2.0 版 引 入 了 匿 名方法 的 概 念 , 此 类 方 法 允 许 将 代 码 块 作 为 参 数 传 递 , 以 代 替 单 独 定 义 的 方 法 。 C# 3.0 引 入 了 Lambda 表 达 式 , 利 用 它 们 可 以 更

简 练 地 编 写 内 联 代 码 块 。 匿 名 方 法 和 Lambda 表 达 式 ( 在 某 些 上 下 文 中 ) 都 可 编 译 为 委 托 类 型 。 这 些 功 能 统 称 为 匿 名 函 数







委托的声明



public delegate void MyDelegate(string str);



1.委 托 的 定 义 和 方 法 的 定 义 类 似 , 只 是 在 前 面 加 了 一 个 delegate,但 委 托 不 是 方 法 , 它 是 一 种 类 型 。 是 一 种 特 殊 的 类 型 ,看 成 是 一 种 新 的 对 象 类 型 比 较 好 理 解 。



用于对与该委托有相同签名的方法调用。



2.委 托 相 当 于 C++中 的 函 数 指 针 , 但 它 是 类 型 安 全 的 。



3.委 托 是 从 System.Delegate 派 生 , 但 不 能 象 定 义 常 规 类 型 一 样 直 接 从 System.Delegate 派 生 , 对 委 托 的 声 明 只 能 通 过 上 面 的 声 明 格 式 进 行 定 义 。 关 键 字



delegate 通 知 编 译 器 这 是 一 个 委 托 类 型 , 从 而 在 编 译 的 时 候 对 该 类 进 行 封 装 ,对 这 一 过 程 C#定 义 了 专 门 的 语 法 来 处 理 这 一 过 程 。



4.不 能 从 一 个 委 托 类 型 进 行 派 生 , 因 为 它 也 是 默 认 sealed 的



5.委 托 即 可 以 对 静 态 方 法 进 行 调 用 也 可 以 对 实 例 方 法 进 行 调 用 。



6.每 个 委 托 类 型 包 含 一 个 自 己 的 调 用 列 表 , 当 组 合 一 个 委 托 或 从 一 个 委 托 中 删 除 一 个 委 托 时 都 将 产 生 个 新 的 调 用 列 表 。



7.两 个 不 同 类 型 的 委 托 即 使 它 们 有 相 同 的 签 名 和 返 回 值 , 但 还 是 两 个 不 同 类 型 的 委 托 。 但 其 实 在 使 用 中 可 看 作 是 相 同 的 。







委托的比较



C#中 对 委 托 定 义 了 两 个 操 作 符 == 和 !=



在以下情况下两个委托是相等的:



1.当 两 个 委 托 都 同 时 为 null 的 时 候



2.当 两 个 委 托 都 不 为 null 时 ,下 列 情 况 下 是 相 等 的 。



a.当 两 个 委 托 的 各 自 的 调 用 列 表 只 含 有 一 个 入 口 点 的 时 候 在 下 列 情 况 下 是 相 等 的



(1) 调 用 同 一 对 象 的 同 一 静 态 方 法



(2) 调 用 同 一 对 象 的 同 一 实 例 方 法



b.当 两 个 委 托 具 有 多 个 入 口 点 时



在下列情况下是相等的



(1)只 有 当 它 们 调 用 列 表 中 的 调 用 的 方 法 按 顺 序 都 一 一 对 应 相 同 的 对 象 及 对 象 的 同 一 方 法 的 时 候



如上所述的两个不同类型的委托但是它们具有相同的签名和返回值时,只要满足上述条件的,即使它们类型不同,但比较的结果也是相同的。







委托的异常处理



当 调 用 该 委 托 的 方 法 中 发 生 了 异 常 时 ,首 先 在 调 用 该 委 托 的 方 法 中 搜 寻 catch 语 句 块 。如 果 没 有 ,则 去 该 委 托 调 用 的 方 法 中 去 寻 找 有 没 有 catch 语 句 块 ,这 和



调用方法发生异常的处理是一样的。



当 调 用 一 个 为 null 的 委 托 即 委 托 中 列 表 中 不 存 在 调 用 方 法 时 , 将 发 生 NullRefrenceException



委托的注意点:



当 一 个 委 托 有 多 个 入 口 点 的 时 候 ,调 用 委 托 将 依 该 委 托 的 调 用 列 表 中 的 方 法 的 顺 序 依 次 调 用 .这 些 方 法 共 享 一 个 参 数 集 合 ,所 以 当 委 托 有 返 回 值 的 时 候 调 用 完 这



个 委 托 后 的 返 回 值 是 最 后 一 个 方 法 的 返 回 值 或 是 有 out 参 数 .如 果 该 委 托 的 参 数 为 ref(引 用 类 型 ),那 么 在 招 待 第 一 个 方 法 的 时 候 如 果 对 这 个 参 数 的 值 有 所 改 变 ,



那么这个改变将会影响到后面的方法调用.



委托的一个例子



using System;



using System.Collections.Generic;



using System.Text;



namespace ConsoleApplication1



{



class Program



{



static void Main(string[] args)



{



// 创 建 一 个 委 托 实 例 , 封 装 C 类 的 静 态 方 法 M1



MyDelegate d1 = new MyDelegate(C.M1);

44



d1("D1"); // M1



// 创 建 一 个 委 托 实 例 , 封 装 C 类 的 静 态 方 法 M2



MyDelegate d2 = new MyDelegate(C.M2);



d2("D2"); // M2



// 创 建 一 个 委 托 实 例 , 封 装 C 类 的 实 例 方 法 M3



MyDelegate d3 = new MyDelegate(new C().M3);



d3("D3"); // M3



// 从 一 个 委 托 d3 创 建 一 个 委 托 实 例



MyDelegate d4 = new MyDelegate(d3);



d4("D4"); // M3



// 组 合 两 个 委 托



MyDelegate d5 = d1 + d2;



d5 += d3;



d5("D5"); // M1,M2,M3



// 从 组 合 委 托 中 删 除 d3



MyDelegate d6 = d5 - d3;



d6("D6"); // M1,M2



d6 -= d3; // 虽 然 d6 调 用 列 表 中 已 经 没 有 d3 了 , 但 这 样 只 是 不 可 能 的 移 除 没 有 错 误 发 生



d6("D6"); // M1,M2



d6 -= d6;



//d6("D6"); 此 时 d6 的 调 用 列 表 为 空 , d6 为 null,所 以 引 发 System.NullReferenceException



MyDelegate d7 = new MyDelegate(C1.P1);



d7("D7"); // C1.P1



MyDelegate d8 = new MyDelegate(new C2().P1);



d8("D8"); // C2.P1



}



}



// 声 明 一 个 委 托 MyDelegate



public delegate void MyDelegate(string str);



public class C



{



public static void M1(string str)



{



Console.WriteLine("From:C.M1: {0}", str);



}



public static void M2(string str)



{



Console.WriteLine("From:C.M2: {0}", str);



}



public void M3(string str)



{



Console.WriteLine("From:C.M3: {0}", str);



}



}



public class C1



{



public static void P1(string str)



{



Console.WriteLine("From:C1.P1: {0}", str);

45



}



}



public class C2



{



public void P1(string str)



{



Console.WriteLine("From:C2.P1: {0}", str);



}



}



}







事件概述



事 件 就 是 当 对 象 或 类 状 态 发 生 改 变 时 ,对 象 或 类 发 出 的 信 息 或 通 知 。发 出 信 息 的 对 象 或 类 称 为 "事 件 源 ",对 事 件 进 行 处 理 的 方 法 称 为 "接 收 者 ",通 常 事 件 源 在 发



出 状 态 改 变 信 息 时 ,它 并 不 知 道 由 哪 个 事 件 接 收 者 来 处 理 .这 就 需 要 一 种 管 理 机 制 来 协 调 事 件 源 和 接 收 者 ,C++中 通 过 函 数 指 针 来 完 成 的 .在 C#中 事 件 使 用 委 托 来



为触发时将调用的方法提供类型安全的封装。







事件的声明



1.声 明 一 个 委 托



public delegate void EventHandler(object sender, System.EventArgs e);



2.声 明 一 个 事 件



public event EventHandler Changed;



3.引 发 一 个 事 件



public OnChanged(EnventArgs e)



{



if ( Changed != null)



{



Changed(this,e);



}



}



4.定 义 事 件 处 理 程 序



public MyText_OnChanged(Object sender,EventArgs e)



{



...



}



5.订 阅 事 件 (将 事 件 处 理 程 序 添 加 到 事 件 的 调 用 列 表 中 )



myText.Changed += EventHandler(MyText_OnChanged);



下面的一个小例子说明了怎样定义一个完整的事件机制:



using System;



using System.Collections.Generic;



using System.Text;



namespace ConsoleApplication1



{



class Program



{



static void Main(string[] args)



{



MyText myText = new MyText();



// 将 事 件 处 理 程 序 添 加 到 事 件 的 调 用 列 表 中 (即 事 件 布 线 )



myText.Changed += new MyText.ChangedEventHandler(myText_Ch anged);

46







string str = "";



while (str != "quit")



{



Console.WriteLine("please enter a string:");



str = Console.ReadLine();



myText.Text = str;



}



}



// 对 Change 事 件 处 理 的 程 序



private static void myText_Changed(object sender, EventArgs e)



{



Console.WriteLine("text has been changed :{0}\n" ,((MyText)sender).Text);



}



}



public class MyText



{



private string _text = "";



// 定 义 事 件 的 委 托



public delegate void ChangedEventHandler(object sender, EventArgs e);



// 定 义 一 个 事 件



public event ChangedEventHandler Changed;



// 用 以 触 发 Change 事 件



protected virtual void OnChanged(EventArgs e)



{



if (this.Changed != null)



this.Changed(this, e);



}



// Text 属 性



public string Text



{



get { return this._text; }



set



{



this._text = value;



// 文 本 改 变 时 触 发 Change 事 件



this.OnChanged(new EventArgs());



}



}



}



}







29. C#接口和抽象类.

一、抽象类:



抽 象 类 是 特 殊 的 类 , 只 是 不 能 被 实 例 化 ;除 此 以 外 , 具 有 类 的 其 他 特 性 ; 重 要 的 是 抽 象 类 可 以 包 括 抽 象 方 法 , 这 是 普 通 类 所 不 能 的 。 抽 象 方 法 只 能 声 明 于



抽 象 类 中 ,且 不 包 含 任 何 实 现 ,派 生 类 必 须 覆 盖 它 们 。 另 外 ,抽 象 类 可 以 派 生 自 一 个 抽 象 类 ,可 以 覆 盖 基 类 的 抽 象 方 法 也 可 以 不 覆 盖 ,如 果 不 覆 盖 ,则 其 派 生



类必须覆盖它们。



二、接口:



接 口 是 引 用 类 型 的 , 类 似 于 类 ,和 抽 象 类 的 相 似 之 处 有 三 点 :

47



1、 不 能 实 例 化 ;



2、 包 含 未 实 现 的 方 法 声 明 ;



3、 派 生 类 必 须 实 现 未 实 现 的 方 法 , 抽 象 类 是 抽 象 方 法 , 接 口 则 是 所 有 成 员 ( 不 仅 是 方 法 包 括 其 他 成 员 ) ;



另外,接口有如下特性:



接 口 除 了 可 以 包 含 方 法 之 外 , 还 可 以 包 含 属 性 、 索 引 器 、 事 件 ,而 且 这 些 成 员 都 被 定 义 为 公 有 的 。 除 此 之 外 , 不 能 包 含 任 何 其 他 的 成 员 , 例 如 : 常 量 、域 、 构



造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。



三、抽象类和接口的区别:



1.类 是 对 对 象 的 抽 象 , 可 以 把 抽 象 类 理 解 为 把 类 当 作 对 象 , 抽 象 成 的 类 叫 做 抽 象 类 .而 接 口 只 是 一 个 行 为 的 规 范 或 规 定 , 微 软 的 自 定 义 接 口 总 是 后



带 able 字 段 , 证 明 其 是 表 述 一 类 类 “ 我 能 做 。 。 。 ” .抽 象 类 更 多 的 是 定 义 在 一 系 列 紧 密 相 关 的 类 间 , 而 接 口 大 多 数 是 关 系 疏 松 但 都 实 现 某 一 功 能 的 类 中 .



2.接 口 基 本 上 不 具 备 继 承 的 任 何 具 体 特 点 ,它 仅 仅 承 诺 了 能 够 调 用 的 方 法 ;



3.一 个 类 一 次 可 以 实 现 若 干 个 接 口 ,但 是 只 能 扩 展 一 个 父 类



4.接 口 可 以 用 于 支 持 回 调 ,而 继 承 并 不 具 备 这 个 特 点 .



5.抽 象 类 不 能 被 密 封 。



6.抽 象 类 实 现 的 具 体 方 法 默 认 为 虚 的 , 但 实 现 接 口 的 类 中 的 接 口 方 法 却 默 认 为 非 虚 的 , 当 然 您 也 可 以 声 明 为 虚 的 .



7.( 接 口 )与 非 抽 象 类 类 似 ,抽 象 类 也 必 须 为 在 该 类 的 基 类 列 表 中 列 出 的 接 口 的 所 有 成 员 提 供 它 自 己 的 实 现 。但 是 ,允 许 抽 象 类 将 接 口 方 法 映 射 到



抽象方法上。



8.抽 象 类 实 现 了 oop 中 的 一 个 原 则 , 把 可 变 的 与 不 可 变 的 分 离 。 抽 象 类 和 接 口 就 是 定 义 为 不 可 变 的 , 而 把 可 变 的 座 位 子 类 去 实 现 。



9.好 的 接 口 定 义 应 该 是 具 有 专 一 功 能 性 的 ,而 不 是 多 功 能 的 ,否 则 造 成 接 口 污 染 。如 果 一 个 类 只 是 实 现 了 这 个 接 口 的 中 一 个 功 能 ,而 不 得 不 去 实 现



接口中的其他方法,就叫接口污染。



10.尽 量 避 免 使 用 继 承 来 实 现 组 建 功 能 ,而 是 使 用 黑 箱 复 用 ,即 对 象 组 合 。因 为 继 承 的 层 次 增 多 ,造 成 最 直 接 的 后 果 就 是 当 你 调 用 这 个 类 群 中 某 一 类 ,



就 必 须 把 他 们 全 部 加 载 到 栈 中 !后 果 可 想 而 知 .( 结 合 堆 栈 原 理 理 解 )。同 时 ,有 心 的 朋 友 可 以 留 意 到 微 软 在 构 建 一 个 类 时 ,很 多 时 候 用 到 了 对 象 组 合 的 方 法 。



比 如 asp.net 中 , Page 类 , 有 Server Request 等 属 性 , 但 其 实 他 们 都 是 某 个 类 的 对 象 。 使 用 Page 类 的 这 个 对 象 来 调 用 另 外 的 类 的 方 法 和 属 性 , 这 个 是 非 常



基本的一个设计原则。



11.如 果 抽 象 类 实 现 接 口 , 则 可 以 把 接 口 中 方 法 映 射 到 抽 象 类 中 作 为 抽 象 方 法 而 不 必 实 现 , 而 在 抽 象 类 的 子 类 中 实 现 接 口 中 方 法 .



四、抽象类和接口的使用:



1. 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本。



2.如 果 创 建 的 功 能 将 在 大 范 围 的 全 异 对 象 间 使 用 , 则 使 用 接 口 。 如 果 要 设 计 小 而 简 练 的 功 能 块 , 则 使 用 接 口 。



3.如 果 要 设 计 大 的 功 能 单 元 , 则 使 用 抽 象 类 .如 果 要 在 组 件 的 所 有 实 现 间 提 供 通 用 的 已 实 现 功 能 , 则 使 用 抽 象 类 。



4.抽 象 类 主 要 用 于 关 系 密 切 的 对 象 ; 而 接 口 适 合 为 不 相 关 的 类 提 供 通 用 功 能 。







30. UDP 连接和 TCP 连接的异同.

TCP 与 UDP 的 区 别 :



1、 TCP 是 面 向 连 接 的 , 比 如 你 给 别 人 打 电 话 , 就 必 须 等 线 路 接 通 后 , 对 方 拿 起 话 筒 才 能 相 互 通 话 。



UDP 是 面 向 非 连 接 的 , 与 手 机 短 信 非 常 相 似 , 你 在 发 短 信 的 时 候 , 只 需 要 输 入 对 方 手 机 号 就 行 了 。



2、 TCP 对 系 统 资 源 的 要 求 较 多 , UDP 对 系 统 资 源 的 要 求 少 。



3、 TCP 程 序 结 构 比 较 复 杂 , UDP 程 序 结 构 较 简 单



4、 TCP 保 证 数 据 可 靠 性 , UDP 适 用 于 一 次 只 传 送 少 量 数 据 , 对 可 靠 性 要 求 不 高 。



5、 TCP 保 证 数 据 顺 序 , UDP 不 保 证 数 据 顺 序 。







31. 什么是 code-Behind 技术.

Code Behind 就 是 所 谓 的 代 码 分 离 , 自 从 Microsoft 公 司 推 出 了 ASP.NET 以 后 , Code Behind 就 是 一 个 热 门 的 话 题 。 在 一 般 的 ASP.NET 文 件 中 , Code Behind



主 要 是 用 二 个 文 件 来 创 建 一 个 ASP.NET 的 页 面 , 其 中 一 个 是 设 计 文 件 , 一 般 以 .aspx 或 者 .ascx 做 为 扩 展 名 , 而 另 外 一 个 是 程 序 代 码 文 件 , 一 般 以 .vb 或 者 .cs



做 为 扩 展 名 , 其 程 序 设 计 语 言 主 要 是 VB.Net 或 者 是 C#。







32. 活动目录的作用.

Active Directory( 活 动 目 录 ) 是 Windows Server 2003 域 环 境 中 提 供 目 录 服 务 的 组 件 。 目 录 服 务 在 微 软 平 台 上 从 Windows Server 2000 开 始 引 入 , 所 以 我 们



可以理解为活动目录是目录服务在微软平台的一种实现方式。当然目录服务在非微软平台上都有相应的 实现。







33. .net 中读写 XML 的类都归属于哪些命名空间?

Asp.Net XML 操 作 基 类 (修 改 ,删 除 ,新 增 ,创 建 )

48



using System.Xml;



namespace EC



{



///



/// XML 操 作 基 类



///



public class XmlObject : IDisposable



{



//以 下 为 单 一 功 能 的 静 态 类







#region 读 取 XML 到 DataSet



/**************************************************



* 函 数 名 称 :GetXml(string XmlPath)



* 功 能 说 明 :读 取 XML 到 DataSet



* 参 数 :XmlPath:xml 文 档 路 径



* 使用示列:



* using EC; //引 用 命 名 空 间



* string xmlPath = Server.MapPath("/EBDnsConfig/DnsConfig.xml"); // 获 取 xml 路 径



* DataSet ds = EC.XmlObject.GetXml(xmlPath); // 读 取 xml 到 DataSet 中



************************************************/



///



/// 功 能 :读 取 XML 到 DataSet 中



///



/// xml 路 径



/// DataSet



public static DataSet GetXml(string XmlPath)



{



DataSet ds = new DataSet();



ds.ReadXml(@XmlPath);



return ds;



}



#endregion







#region 读 取 xml 文 档 并 返 回 一 个 节 点



/**************************************************



* 函 数 名 称 :ReadXmlReturnNode(string XmlPath,string Node)



* 功 能 说 明 :读 取 xml 文 档 并 返 回 一 个 节 点 :适 用 于 一 级 节 点



* 参 数 : XmlPath:xml 文 档 路 径 ;Node 返 回 的 节 点 名 称



* 适 应 用 Xml:



*



* ns1.everdns.com



*



* 使用示列:



* using EC; //引 用 命 名 空 间



* string xmlPath = Server.MapPath("/EBDnsConfig/DnsConfig.xml"); // 获 取 xml 路 径



* Response.Write(XmlObject.ReadXmlReturnNode(xmlPath, "mailmanager"));



************************************************/



///



/// 读 取 xml 文 档 并 返 回 一 个 节 点 :适 用 于 一 级 节 点

49



///



/// xml 路 径



/// 节 点



///



public static string ReadXmlReturnNode(string XmlPath,string Node)



{



XmlDocument docXml = new XmlDocument();



docXml.Load(@XmlPath);



XmlNodeList xn = docXml.GetElementsByTagName(Node);



return xn.Item(0).InnerText.ToString();



}



#endregion







#region 查 找 数 据 ,返 回 一 个 DataSet



/**************************************************



* 函 数 名 称 :GetXmlData(string xmlPath, string XmlPathNode)



* 功 能 说 明 :查 找 数 据 ,返 回 当 前 节 点 的 所 有 下 级 节 点 ,填 充 到 一 个 DataSet 中



* 参 数 :xmlPath:xml 文 档 路 径 ;XmlPathNode:当 前 节 点 的 路 径



* 使用示列:



* using EC; //引 用 命 名 空 间



* string xmlPath = Server.MapPath("/EBDomainConfig/DomainConfig.xml"); // 获 取 xml 路 径



* DataSet ds = new DataSet();



* ds = XmlObject.GetXmlData(xmlPath, "root/items");// 读 取 当 前 路 径



* this.GridView1.DataSource = ds;



* this.GridView1.DataBind();



* ds.Clear();



* ds.Dispose();



* Xml 示 例 :



************************************************/



///



/// 查 找 数 据 ,返 回 当 前 节 点 的 所 有 下 级 节 点 ,填 充 到 一 个 DataSet 中



///



/// xml 文 档 路 径



/// 节 点 的 路 径 :根 节 点 /父 节 点 /当 前 节 点



///



public static DataSet GetXmlData(string xmlPath, string XmlPathNode)



{



XmlDocument objXmlDoc = new XmlDocument();



objXmlDoc.Load(xmlPath);



DataSet ds = new DataSet();



StringReader read = new StringReader(objXmlDoc.SelectSingleNode(XmlPathNode).Outer Xml);



ds.ReadXml(read);



return ds;



}



#endregion







#region 更 新 Xml 节 点 内 容



/**************************************************



* 函 数 名 称 :XmlNodeReplace(string xmlPath,string Node,string Content)

50



* 功 能 说 明 :更 新 Xml 节 点 内 容



* 参 数 :xmlPath:xml 文 档 路 径 ;Node:当 前 节 点 的 路 径 ;Content:内 容



* 使用示列:



* using EC; //引 用 命 名 空 间



* string xmlPath = Server.MapPath("/EBDomainConfig/DomainConfig.xml"); // 获 取 xml 路 径



* XmlObject.XmlNodeReplace(xmlPath, "root/test", "56789"); // 更 新 节 点 内 容



************************************************/



///



/// 更 新 Xml 节 点 内 容



///



/// xml 路 径



/// 要 更 换 内 容 的 节 点 :节 点 路 径 根 节 点 /父 节 点 /当 前 节 点



/// 新 的 内 容



public static void XmlNodeReplace(string xmlPath,string Node,string Content)



{



XmlDocument objXmlDoc = new XmlDocument();



objXmlDoc.Load(xmlPath);



objXmlDoc.SelectSingleNode(Node).InnerText = Content;



objXmlDoc.Save(xmlPath);







}



#endregion







#region 删 除 XML 节 点 和 此 节 点 下 的 子 节 点



/**************************************************



* 函 数 名 称 :XmlNodeDelete(string xmlPath,string Node)



* 功 能 说 明 :删 除 XML 节 点 和 此 节 点 下 的 子 节 点



* 参 数 :xmlPath:xml 文 档 路 径 ;Node:当 前 节 点 的 路 径 ;



* 使用示列:



* using EC; //引 用 命 名 空 间



* string xmlPath = Server.MapPath("/EBDomainConfig/DomainConfig.xml"); // 获 取 xml 路 径



* XmlObject.XmlNodeDelete(xmlPath, "root/test"); // 删 除 当 前 节 点



************************************************/



///



/// 删 除 XML 节 点 和 此 节 点 下 的 子 节 点



///



/// xml 文 档 路 径



/// 节 点 路 径



public static void XmlNodeDelete(string x mlPath,string Node)



{



XmlDocument objXmlDoc = new XmlDocument();



objXmlDoc.Load(xmlPath);



string mainNode = Node.Substring(0, Node.LastIndexOf("/"));



objXmlDoc.SelectSingleNode(mainNode).RemoveChild(objXmlDoc.SelectSingleNode(Node));



objXmlDoc.Save(xmlPath);



}



#endregion

51





34. 如何理解.net 中的垃圾回收机制.

垃 圾 收 集 器 用 来 在 .NET 中 进 行 内 存 管 理 ,特 别 是 它 可 以 恢 复 正 在 运 行 中 的 应 用 程 序 需 要 的 内 存 。到 目 前 为 止 ,Windows 平 台 已 经 使 用 了 两 种 技 术 来 释 放 进 程向



系统动态请求的内存:



完全以手工方式使应用程序代码完成这些工作。



让对象维护引用计数。



让 应 用 程 序 代 码 负 责 释 放 内 存 是 低 级 、 高 性 能 的 语 言 使 用 的 技 术 , 例 如 C++。 这 种 技 术 很 有 效 , 且 可 以 让 资 源 在 不 需 要 时 就 释 放 (一 般 情 况 下 ), 但 其 最 大 的 缺



点是频繁出现错误。请求内存的代码还必须明确通知系统它什么时候不再需要该内存。但这是很容易被遗漏的,从而导致内存泄漏。



尽 管 现 代 的 开 发 环 境 提 供 了 帮 助 检 测 内 存 泄 漏 的 工 具 ,但 它 们 很 难 跟 踪 错 误 ,因 为 直 到 内 存 已 大 量 泄 漏 从 而 使 Windows 拒 绝 为 进 程 提 供 资 源 时 ,它 们 才 会 发 挥



作用。到那个时候,由于对内存的需求,会使整个计算机变得相当慢。



维 护 引 用 计 数 是 COM 对 象 采 用 的 一 种 技 术 ,其 方 法 是 每 个 COM 组 件 都 保 留 一 个 计 数 ,记 录 客 户 机 目 前 对 它 的 引 用 数 。当 这 个 计 数 下 降 到 0 时 ,组 件 就 会 删 除 自



己,并释放相应的内存和资源。它带来的问题是仍需要客户机通知组件它们已经完成了内存的使用。只要有一个客户机 没 有 这 么 做, 对 象 就 仍 驻留 在 内 存中 。



在 某 些 方 面 ,这 是 比 C++内 存 泄 漏 更 为 严 重 的 问 题 ,因 为 COM 对 象 可 能 存 在 于 它 自 己 的 进 程 中 ,从 来 不 会 被 系 统 删 除 (在 C++内 存 泄 漏 问 题 上 ,系 统 至 少 可 以 在



进 程 中 断 时 释 放 所 有 的 内 存 )。



.NET 运 行 库 采 用 的 方 法 是 垃 圾 收 集 器 , 这 是 一 个 程 序 , 其 目 的 是 清 理 内 存 , 方 法 是 所 有 动 态 请 求 的 内 存 都 分 配 到 堆 上 (这 对 所 有 的 语 言 都 一 样 , 但 在 .NET 中 ,



CLR 维 护 它 自 己 的 托 管 堆 , 以 供 .NET 应 用 程 序 使 用 ), 当 .NET 检 测 到 给 定 进 程 的 托 管 堆 已 满 , 需 要 清 理 时 , 就 调 用 垃 圾 收 集 器 。 垃 圾 收 集 器 处 理 目 前 代 码 中 的



所有变量,检查对存储在托管堆上的对象的引用,确定哪些对象可以从代码中访问 —— 即哪些对象有引用。没有引用的对象就不能再从代码中访问,因而被



删 除 。 Java 就 使 用 与 此 类 似 的 垃 圾 收 集 系 统 。



之 所 以 在 .NET 中 使 用 垃 圾 收 集 器 ,是 因 为 中 间 语 言 已 用 来 处 理 进 程 。其 规 则 要 求 ,第 一 ,不 能 引 用 已 有 的 对 象 ,除 非 复 制 已 有 的 引 用 。第 二 ,中 间 语 言 是 类型



安全的语言。在这里,其含义是如果存在对对象的任何引用,该引用中就有足够的信息来确定对象的类型。



垃 圾 收 集 器 机 制 不 能 和 诸 如 非 托 管 C++这 样 的 语 言 一 起 使 用 , 因 为 C++允 许 指 针 自 由 地 转 换 数 据 类 型 。



垃 圾 收 集 器 的 一 个 重 要 方 面 是 它 是 不 确 定 的 。 换 言 之 , 不 能 保 证 什 么 时 候 会 调 用 垃 圾 收 集 器 : .NET 运 行 库 决 定 需 要 它 时 , 就 可 以 调 用 它 (除 非 明 确 调 用 垃 圾 收



集 器 )。 但 可 以 重 写 这 个 过 程 , 在 代 码 中 调 用 垃 圾 收 集 器 。







GC 的 全 称 是 garbage collection, 中 文 名 称 垃 圾 回 收 , 是 .NET 中 对 内 存 管 理 的 一 种 功 能 。 垃 圾 回 收 器 跟 踪 并 回 收 托 管 内 存 中 分 配 的 对 象 , 定 期 执 行 垃 圾 回 收



以 回 收 分 配 给 没 有 有 效 引 用 的 对 象 的 内 存 。 当 使 用 可 用 内 存 不 能 满 足 内 存 请 求 时 , GC 会 自 动 进 行 。



在 进 行 垃 圾 回 收 时 ,垃 圾 回 收 器 会 首 先 搜 索 内 存 中 的 托 管 对 象 ,然 后 从 托 管 代 码 中 搜 索 被 引 用 的 对 象 并 标 记 为 有 效 ,接 着 释 放 没 有 被 标 记 为 有 效 的 对 象 并



收 回 内 存 , 最 后 整 理 内 存 将 有 效 对 象 挪 动 到 一 起 。 这 就 是 GC 的 四 个 步 骤 。



由 上 可 见 , GC 是 很 影 响 性 能 的 , 所 以 一 般 说 来 这 种 事 情 况 还 是 尽 量 少 发 生 为 好 。



为 了 减 少 一 些 性 能 影 响 ,.NET 的 GC 支 持 对 象 老 化 ,或 者 说 分 代 的 概 念 ,代 是 对 象 在 内 存 中 相 对 存 现 时 期 的 度 量 单 位 ,对 象 的 代 数 或 存 现 时 期 说 明 对 象 所



属 的 代 。目 前 .NET 的 垃 圾 回 收 器 支 持 三 代 。每 进 行 一 次 GC,没 有 被 回 收 的 对 象 就 自 动 提 升 一 代 。较 近 创 建 的 对 象 属 于 较 新 的 代 ,比 在 应 用 程 序 生 命 周 期 中 较 早



创 建 的 对 象 的 代 数 低 。 最 近 代 中 的 对 象 位 于 零 代 中 。 每 一 次 GC 的 时 候 , 都 首 先 回 收 零 代 中 的 对 象 , 只 有 在 较 低 代 数 的 对 象 回 收 完 成 后 仍 不 能 满 足 需 求 的 情 况



下才回收较高代数的对象。









35. using 几种用法

1. 导 入 命 名 空 间 ,如 using System;为 了 不 用 写 类 的 全 名 .



2. 用 来 对 一 个 命 名 空 间 重 命 名 ,如 using sysData=System.Data;引 用 时 sysData.DataSet.



3. 释 放 资 源 , using: 释 放 ()中 的 对 象 。 释 放 托 管 资 源 和 非 托 管 资 源 :



a) 连

非 托 管 资 源 : 和 外 部 物 理 设 备 连 接 的 资 源 如 :文 件 , 接 , 需 可

还 有 流 对 象 等 .CLR 不 会 释 放 非 托 管 资 源 , 要 自 己 释 放 , 以 使 用 Using()



b) 托 管 资 源 : CLR 会 自 己 释 放 , 但 不 是 立 刻 释 放 . 使 用 Using()会 立 刻 释 放 资 源







36. new 有几种用法

在 C# 中 , new 关 键 字 可 用 作 运 算 符 、 修 饰 符 或 约 束 。



1) new 运 算 符 : 用 于 创 建 对 象 和 调 用 构 造 函 数 。 这 种 大 家 都 比 较 熟 悉 , 没 什 么 好 说 的 了 。



2) new 修 饰 符 : 在 用 作 修 饰 符 时 , new 关 键 字 可 以 显 式 隐 藏 从 基 类 继 承 的 成 员 。



3) new 约 束 : 用 于 在 泛 型 声 明 中 约 束 可 能 用 作 类 型 参 数 的 参 数 的 类 型 。







关于第二种用法看下例:



using System;



namespace ConsoleApplication1

52



{



public class BaseA



{



public int x = 1;



public void Invoke()



{



Console.WriteLine(x.ToString());



}



public int TrueValue



{



get { return x; }



set { x = value; }



}



}



public class DerivedB : BaseA



{



new public int x = 2;



new public void Invoke()



{



Console.WriteLine(x.ToString());



}



new public int TrueValue



{



get { return x; }



set { x = value; }



}



}







class Test



{



static void Main(string[] args)



{



DerivedB b = new DerivedB();



b.Invoke();//调 用 DerivedB 的 Invoke 方 法 , 输 出 : 2



Console.WriteLine(b.x.ToString());// 输 出 DerivedB 的 成 员 x 值 : 2



BaseA a = b;



a.Invoke();//调 用 BaseA 的 Invoke 方 法 , 输 出 : 1



a.TrueValue = 3;//调 用 BaseA 的 属 性 TrueValue, 修 改 BaseA 的 成 员 x 的 值



Console.WriteLine(a.x.ToString());// 输 出 BaseA 的 成 员 x 的 值 : 3



Console.WriteLine(b.TrueValue.ToString());// 输 出 DerivedB 的 成 员 x 的 值 , 仍 然 是 : 1



//可 见 , 要 想 访 问 被 隐 藏 的 基 类 的 成 员 变 量 、 属 性 或 方 法 , 办 法 就 是 将 子 类 造 型 为 父 类 , 然



//后 通 过 基 类 访 问 被 隐 藏 的 成 员 变 量 、 属 性 或 方 法 。



}



}



}



new 约 束 指 定 泛 型 类 声 明 中 的 任 何 类 型 参 数 都 必 须 具 有 公 共 的 无 参 数 构 造 函 数 .请 看 下 例 :



using System;



using System.Collections.Generic;

53



namespace ConsoleApplication2



{



public class Employee



{



private string name;



private int id;



public Employee()



{



name = "Temp";



id = 0;



}







public Employee(string s, int i)



{



name = s;



id = i;



}







public string Name



{



get { return name; }



set { name = value; }



}







public int ID



{



get { return id; }



set { id = value; }



}



}







class ItemFactory where T : new()



{



public T GetNewItem()



{



return new T();



}



}







public class Test



{



public static void Main()



{



ItemFactory EmployeeFactory = new ItemFactory ();



////此 处 编 译 器 会 检 查 Employee 是 否 具 有 公 有 的 无 参 构 造 函 数 。



//若 没 有 则 会 有 The Employee must have a public parameterless constructor 错 误 。



Console.WriteLine("{0}'ID is {1}.", EmployeeFactory.GetNewItem().Name, EmployeeFactory.GetNewItem().ID);



}



}

54



}







37. datagrid.datasouse 可以连接什么数据源?

对 于 DataGrid 有 效 的 数 据 源 包 括 :



DataTable 类



DataView 类



DataSet 类



DataViewManager 类



如 果 数 据 源 是 数 据 集 , 则 它 可 能 是 窗 体 中 的 一 个 对 象 或 者 是 XML Web services 传 递 给 窗 体 的 对 象 。 可 以 绑 定 到 类 型 化 数 据 集 或 非 类 型 化 数 据 集 。 有 关 将 数 据



集 添 加 到 窗 体 的 详 细 信 息 , 请 参 见 用 于 创 建 数 据 集 的 Visual Studio 工 具 。 有 关 使 用 XML Web services 访 问 数 据 集 的 详 细 信 息 , 请 参 见 分 布 式 应 用 程 序



和数据集成介绍。



如 果 其 他 结 构 中 的 对 象( 例 如 ,数 组 中 的 元 素 )公 开 公 共 属 性 ,也 可 以 将 DataGrid 控 件 绑 定 到 这 些 结 构 。网 格 将 显 示 结 构 中 元 素 的 所 有 公 共 属 性 。例 如 ,如



果 将 DataGrid 控 件 绑 定 到 一 个 客 户 对 象 数 组 ,则 网 格 将 显 示 这 些 客 户 对 象 的 所 有 公 共 属 性 。在 某 些 情 况 下 ,这 意 味 着 虽 然 可 以 绑 定 到 结 构 ,但 可 能 没 有 实 用



的 应 用 程 序 。 例 如 , 可 以 绑 定 到 一 个 整 数 数 组 , 但 是 因 为 Integer 数 据 类 型 不 支 持 公 共 属 性 , 所 以 该 网 格 不 能 显 示 任 何 数 据 。



如果下列结构的元素公开公共属性,则可以绑定到这些结构:



任 何 实 现 IList 接 口 的 组 件 。 这 包 括 一 维 数 组 。



任 何 实 现 IListSource 接 口 的 组 件 。



任 何 实 现 IBindingList 接 口 的 组 件 。







38. 概述反射和序列化

反 射 的 定 义 :审 查 元 数 据 并 收 集 关 于 它 的 类 型 信 息 的 能 力 。元 数 据( 编 译 以 后 的 最 基 本 数 据 单 元 )就 是 一 大 堆 的 表 ,当 编 译 程 序 集 或 者 模 块 时 ,编 译 器 会 创 建



一个类定义表,一个字段定义表,和一个方法定义表等。



System.reflection 命 名 空 间 包 含 的 几 个 类 , 允 许 你 反 射 ( 解 析 ) 这 些 元 数 据 表 的 代 码 :



System.Reflection.Assembly



System.Reflection.MemberInfo



System.Reflection.EventInfo



System.Reflection.FieldInfo



System.Reflection.MethodBase



System.Reflection.ConstructorInfo



System.Reflection.MethodInfo



System.Reflection.PropertyInfo



System.Type



以下是上面几个类的使用方法:



( 1) 使 用 Assembly 定 义 和 加 载 程 序 集 , 加 载 在 程 序 集 清 单 中 列 出 模 块 , 以 及 从 此 程 序 集 中 查 找 类 型 并 创 建 该 类 型 的 实 例 。



( 2) 使 用 Module 了 解 包 含 模 块 的 程 序 集 以 及 模 块 中 的 类 等 , 还 可 以 获 取 在 模 块 上 定 义 的 所 有 全 局 方 法 或 其 他 特 定 的 非 全 局 方 法 。



( 3) 使 用 ConstructorInfo 了 解 构 造 函 数 的 名 称 、 参 数 、 访 问 修 饰 符 ( 如 pulic 或 private) 和 实 现 详 细 信 息 ( 如 abstract 或 virtual) 等 。 使 用 Type 的



GetConstructors 或 GetConstructor 方 法 来 调 用 特 定 的 构 造 函 数 。



( 4) 使 用 MethodInfo 了 解 方 法 的 名 称 、 返 回 类 型 、 参 数 、 访 问 修 饰 符 ( 如 pulic 或 private) 和 实 现 详 细 信 息 ( 如 abstract 或 virtual) 等 。 使 用 Type 的



GetMethods 或 GetMethod 方 法 来 调 用 特 定 的 方 法 。



( 5) 使 用 FiedInfo 了 解 字 段 的 名 称 、 访 问 修 饰 符 ( 如 public 或 private) 和 实 现 详 细 信 息 ( 如 static) 等 , 并 获 取 或 设 置 字 段 值 。



( 6) 使 用 EventInfo 了 解 事 件 的 名 称 、 事 件 处 理 程 序 数 据 类 型 、 自 定 义 属 性 、 声 明 类 型 和 反 射 类 型 等 , 添 加 或 移 除 事 件 处 理 程 序 。



( 7) 使 用 PropertyInfo 了 解 属 性 的 名 称 、 数 据 类 型 、 声 明 类 型 、 反 射 类 型 和 只 读 或 可 写 状 态 等 , 获 取 或 设 置 属 性 值 。



( 8) 使 用 ParameterInfo 了 解 参 数 的 名 称 、 数 据 类 型 、 是 输 入 参 数 还 是 输 出 参 数 , 以 及 参 数 在 方 法 签 名 中 的 位 置 等 。







反射的作用:



1、 可 以 使 用 反 射 动 态 地 创 建 类 型 的 实 例 , 将 类 型 绑 定 到 现 有 对 象 , 或 从 现 有 对 象 中 获 取 类 型



2、 应 用 程 序 需 要 在 运 行 时 从 某 个 特 定 的 程 序 集 中 载 入 一 个 特 定 的 类 型 , 以 便 实 现 某 个 任 务 时 可 以 用 到 反 射 。



3、 反 射 主 要 应 用 与 类 库 , 这 些 类 库 需 要 知 道 一 个 类 型 的 定 义 , 以 便 提 供 更 多 的 功 能 。



应用要点:



1、 现 实 应 用 程 序 中 很 少 有 应 用 程 序 需 要 使 用 反 射 类 型

55



2、 使 用 反 射 动 态 绑 定 需 要 牺 牲 性 能



3、 有 些 元 数 据 信 息 是 不 能 通 过 反 射 获 取 的



4、 某 些 反 射 类 型 是 专 门 为 那 些 clr 开 发 编 译 器 的 开 发 使 用 的 , 所 以 你 要 意 识 到 不 是 所 有 的 反 射 类 型 都 是 适 合 每 个 人 的 。







序列化就是把一个对象保存到一个文件或数据库字段中去,反序列化就是在适当的时候把这个文 件再转化成原来的对象使用。



在 C#中 常 见 的 序 列 化 的 方 法 主 要 也 有 三 个 : BinaryFormatter、 SoapFormatter、 XML 序 列 化









39. 概述 o/r mapping 的原理

ORM,即 Object-Relationl Mapping,它 的 作 用 是 在 关 系 型 数 据 库 和 对 象 之 间 作 一 个 映 射 ,这 样 ,我 们 在 具 体 的 操 作 数 据 库 的 时 候 ,就 不 需 要 再 去 和 复 杂 的 SQL



语句打交道,只要像平时操作对象一样操作它就可以了。下面是在网上转载的一段文章中对它的描述:



让 我 们 从 O/R 开 始 。 字 母 O 起 源 于 "对 象 "(Object),而 R 则 来 自 于 "关 系 "(Relational)。 几 乎 所 有 的 程 序 里 面 , 都 存 在 对 象 和 关 系 数 据 库 。 在 业 务 逻 辑



层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。



当 你 开 发 一 个 应 用 程 序 的 时 候 (不 使 用 O/R Mapping),你 可 能 会 写 不 少 数 据 访 问 层 的 代 码 ,用 来 从 数 据 库 保 存 ,删 除 ,读 取 对 象 信 息 ,等 等 。你 在 DAL 中 写 了 很



多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。



如 果 打 开 你 最 近 的 程 序 , 看 看 DAL 代 码 , 你 肯 定 会 看 到 很 多 近 似 的 通 用 的 模 式 。 我 们 以 保 存 对 象 的 方 法 为 例 , 你 传 入 一 个 对 象 , 为 SqlCommand 对 象 添



加 SqlParameter, 把 所 有 属 性 和 对 象 对 应 , 设 置 SqlCommand 的 CommandText 属 性 为 存 储 过 程 , 然 后 运 行 SqlCommand。 对 于 每 个 对 象 都 要 重 复 的 写 这 些 代 码 。



除 此 之 外 , 还 有 更 好 的 办 法 吗 ? 有 , 引 入 一 个 O/R Mapping。 实 质 上 , 一 个 O/R Mapping 会 为 你 生 成 DAL。 与 其 自 己 写 DAL 代 码 , 不 如 用 O/R Mapping。



你 用 O/R Mapping 保 存 , 删 除 , 读 取 对 象 , O/R Mapping 负 责 生 成 SQL, 你 只 需 要 关 心 对 象 就 好 。







40. 用 sealed 修饰的类有什么特点

sealed 修 饰 符 用 于 防 止 从 所 修 饰 的 类 派 生 出 其 它 类 .如 果 一 个 密 封 类 被 指 定 为 其 他 类 的 基 类 , 则 会 发 生 编 译 时 错 误 .



密封类不能同时为抽象类.



sealed 修 饰 符 主 要 用 于 防 止 非 有 意 的 派 生 , 但 是 它 还 能 促 使 某 些 运 行 时 优 化 .具 体 说 来 , 由 于 密 封 类 永 远 不 会 有 任 何 派 生 类 , 所 以 对 密 封 类 的 实 例 的 虚 拟 函 数



成员的调用可以转换为非虚拟调用来处理.







41. 概述三层结构体系

1.表 示 层 (USL): 主 要 表 示 WEB 方 式 , 也 可 以 表 示 成 WINFORM 方 式 。 如 果 逻 辑 层 相 当 强 大 和 完 善 , 无 论 表 现 层 如 何 定 义 和 更 改 , 逻 辑 层 都 能 完 善 地 提 供 服 务 。



2.业 务 逻 辑 层 (BLL): 主 要 是 针 对 具 体 的 问 题 的 操 作 , 也 可 以 理 解 成 对 数 据 层 的 操 作 , 对 数 据 业 务 逻 辑 处 理 。 如 果 说 数 据 层 是 积 木 , 那 逻 辑 层 就 是 对 这 些 积 木



的搭建。



3.数 据 访 问 层 (DAL):主 要 是 对 原 始 数 据 (数 据 库 或 者 文 本 文 件 等 存 放 数 据 的 形 式 )的 操 作 层 ,而 不 是 指 原 始 数 据 ,也 就 是 说 ,是 对 数 据 的 操 作 ,而 不 是 数 据 库 ,



具体为业务逻辑层或表示层提供数据服务。



“三层结构”开发模式的一个非常明显的缺点就是其执行速度不够快。当然这个“执行速度”是相对于非分层的应用程序来说的 。







42. asp.net 如何实现 MVC 模式,举例说明

MVC 是 个 将 一 个 应 用 的 实 现 分 成 三 个 组 件 角 色 的 框 架 技 术 : 模 型 , 视 图 和 控 制 器 。



在 基 于 MVC 的 应 用 里 , Model(模 型 )是 负 责 保 持 状 态 的 应 用 组 件 。 这 个 状 态 通 常 都 持 久 于 数 据 库 之 中 ( 譬 如 , 我 们 也 许 会 有 一 个 Product( 产 品 ) 类 用 来 代 表



SQL 中 的 Products 数 据 表 中 的 订 单 数 据 )。



在 基 于 MVC 的 应 用 里 ,View(视 图 )是 负 责 显 示 用 户 界 面 的 组 件 。这 个 UI 通 常 是 使 用 模 型 数 据 来 创 建 的( 譬 如 ,我 们 也 许 会 生 成 一 个 Product"编 辑 "视 图 ,根 据



当 前 Product 对 象 的 状 态 , 显 示 文 本 框 , 下 拉 框 和 复 选 框 等 )。



在 基 于 MVC 的 应 用 里 ,Controller(控 制 器 )是 处 理 用 户 交 互 ,操 作 模 型 和 最 终 选 择 用 哪 个 视 图 来 显 示 UI 的 组 件 。在 MVC 应 用 中 ,视 图 只 是 用 来 显 示 信 息 而 已 ,



是控制器来处理和回应用户的输入和交互的。



使 用 MVC 方 法 的 一 个 好 处 是 ,它 有 助 于 促 进 应 用 中 模 型 ,视 图 ,控 制 器 间 的 关 注 的 清 晰 分 离 。保 持 关 注 的 清 晰 分 离 使 得 对 应 用 的 测 试 极 其 容 易 ,因 为 不 同 应 用



组件间的契约的定义和表达是更明确的。



MVC 模 式 也 有 利 于 促 进 红 /绿 式 测 试 驱 动 的 开 发 ( TDD), 通 过 它 , 你 可 以 在 你 实 际 编 写 应 用 代 码 本 身 之 前 首 先 实 现 自 动 化 的 单 元 测 试 , 这 些 单 元 测 试 定 义 和 核



实了新代码的需求。



ASP.NET MVC 框 架 的 一 些 简 要 细 节



在 几 个 星 期 后 ,相 关 代 码 可 以 下 载 之 后 ,我 将 写 一 些 关 于 这 个 新 的 ASP.NET MVC 框 架 的 深 入 性 的 教 程 贴 子( 与 此 同 时 ,想 进 一 步 了 解 它 的 最 佳 方 式 是 观 看 我 的



Alt.net 讲 座 的 录 像 ) :



这 里 是 关 于 ASP.NET MVC 框 架 的 一 些 简 要 细 节 :

56



它 将 促 进 清 晰 的 关 注 分 离 , 可 测 试 性 , 和 TDD。 MVC 框 架 中 的 所 以 核 心 契 约 都 是 基 于 接 口 的 , 可 以 轻 易 地 通 过 mock 来 模 拟 ( 包 括 基 于 接 口 的



IHttpRequest/IHttpResponse 这 些 基 本 的 东 西 )。你 可 以 不 用 在 ASP.NET 进 程 中 运 行 控 制 器( 这 使 得 单 元 测 试 很 快 ),就 单 元 测 试 你 的 应 用 。你 可 以 使 用 你 想



使 用 的 任 何 单 元 测 试 框 架 来 做 单 元 测 试 , 包 括 NUnit, MBUnit, MS Test 等 等 。



这 个 框 架 具 有 高 度 的 可 扩 展 性 和 可 插 拔 性 。MVC 框 架 中 所 有 的 东 西 都 是 这 样 设 计 的 ,它 们 可 以 被 轻 易 地 替 换 掉 或 者 定 制( 譬 如 ,你 可 以 插 入 你 自 己 的 视 图 引 擎 ,



( , 。

路 径 转 向 策 略 routing policy) 参 数 序 列 化 等 等 ) 它 还 支 持 使 用 现 有 的 依 赖 注 入 dependency injection) 控 制 反 转 IOC) 器 模 型 Windsor, Spring.Net,

( 和 ( 容 (



NHibernate 等 等 ) 。



它 包 括 一 个 非 常 强 大 的 URL 映 射 组 件 , 允 许 你 使 用 非 常 干 净 的 URL 来 建 造 应 用 。 URL 不 需 要 拥 有 文 件 扩 展 , 是 设 计 来 轻 松 支 持 SEO 和 REST 友 好 的 命 名 模 式 的 。



在 我 或

譬 如 , 我 上 面 的 项 目 中 , 可 以 轻 松 地 把 /products/edit/4 映 射 到 ProductsController 类 的 Edit 方 法 上 , 者 把 /Blogs/scottgu/10-10-2007/SomeTopic/



映 射 到 BlogEngineController 类 的 DisplayPost 方 法 上 。



MVC 框 架 支 持 将 现 有 的 ASP.NET .ASPX, .ASCX,和 .Master 标 识 文 件 当 作 视 图 模 板 ( view template)之 用 (这 意 味 着 你 可 以 轻 松 地 使 用 很 多 现 有 的 ASP.NET 特



性 , 象 嵌 套 的 母 版 页 , 块 , 声 明 式 服 务 控 件 , 模 板 , 数 据 绑 定 , 本 地 化 等 等 ) 。 但 是 , 它 不 使 用 现 有 的 将 交 互 返 回 服 务 器 的 postback 模 型 , 取 而 代 之



的 是 , 你 将 把 用 户 的 所 有 交 互 转 给 控 制 器 类 来 调 度 , 这 有 助 于 关 注 的 清 晰 分 离 和 提 高 可 测 试 性 ( 这 也 意 味 着 , 在 基 于 MVC 的 视 图 内 没 有 viewstate 或 page 的



生命周期之说)。



ASP.NET MVC 框 架 将 完 全 支 持 象 forms/windows 认 证 ,URL 授 权 ,成 员 /角 色 ,输 出 和 数 据 缓 存 ,session/profile 状 态 管 理 ,健 康 监 测 ,配 置 系 统 ,以 及 provider



架 构 等 等 现 有 的 ASP.NET 特 性 。







43. 私有成员会被继承么?

会 , 但 是 不 能 被 访 问 .所 以 看 上 去 他 们 似 乎 是 不 能 被 继 承 的 , 但 实 际 上 确 实 被 继 承 了 . 派 生 类 可 以 访 问 基 类 的 公 共 成 员 、 受 保 护 成 员 、 内 部 成 员 和 受 保 护 内 部



成 员 。即 使 派 生 类 继 承 基 类 的 私 有 成 员 ,仍 不 能 访 问 这 些 成 员 。但 是 ,所 有 这 些 私 有 成 员 在 派 生 类 中 仍 然 存 在 ,且 执 行 与 基 类 自 身 中 相 同 的 工 作 。例 如 ,假 定



一个受保护基类方法访问私有字段。要使继承的基类方法正常工作,派生类中必须有该字段。







44. 请描述一下修饰符 protected internal.

被 protected internal 修 饰 的 属 性 /方 法 只 能 在 它 的 在 同 一 个 程 序 集 ( Assembly) 中 的 子 类 被 访 问 .







45. C#提供一个默认的无参数构造函数,当我实现了另外一个有一个参数的构造函数时候,还想

保留这个无参数的构造函数.这样我应该写几个构造函数?

两 个 , 一 旦 你 实 现 了 一 个 构 造 函 数 , C#就 不 会 再 提 供 默 认 的 构 造 函 数 了 , 所 以 需 要 手 动 实 现 那 个 无 参 数 构 造 函 数 .







46. 重载和覆写有什么区别?

override 表 示 重 写 , 用 于 继 承 类 对 基 类 中 虚 成 员 的 实 现



overload 表 示 重 载 , 用 于 同 一 个 类 中 同 名 方 法 不 同 参 数 ( 包 括 类 型 不 同 或 个 数 不 同 ) 的 实 现







47. 在方法定义中,virtual 有什么含意?

abstract 定 义 的 类 方 法 必 须 在 继 承 的 类 中 进 行 实 现 , 而 且 没 有 实 际 的 代 码 , 类 似 接 口



virtual 定 义 的 方 法 中 可 以 有 实 际 的 代 码 , 但 是 可 以 在 父 类 的 子 类 中 定 义 override 类 型 的 同 名 方 法







48. 能够将非静态的方法覆写成静态方法么?

不 能 , 覆 写 方 法 的 签 名 必 须 与 被 覆 写 方 法 的 签 名 保 持 一 致 , 除 了 将 virtual 改 为 override.







49. 可以覆写私有的虚方法么?

不可以,甚至子类中无法访问父类中的私有方法 .







50. 能够阻止某一个类被其他类继承么?

可 以 , 使 用 关 键 字 sealed.







51. 能够实现允许某个类被继承,但不允许其中的某个方法被覆写么?

可 以 , 标 记 这 个 类 为 public, 并 标 记 这 个 方 法 为 sealed.







52. 什么是抽象类(abstract class)?

一 种 不 可 以 被 实 例 化 的 类 .抽 象 类 中 一 般 含 有 抽 象 方 法 , 当 然 也 可 有 具 体 实 现 .继 承 类 只 有 实 现 过 所 有 抽 象 类 的 抽 象 方 法 后 才 能 被 实 例 化 .







53. 何时必须声明一个类为抽象类?

当这个类中包含抽象方法时,或是该类并没有完全实现父类的抽象方法时 .

57





54. 接口(interface)是什么?

只 含 有 共 有 抽 象 方 法 ( public abstract method) 的 类 .这 些 方 法 必 须 在 子 类 中 被 实 现 .







55. 为什么不能指定接口中方法的修饰符?

接 口 中 的 方 法 用 来 定 义 对 象 之 间 通 信 的 契 约 , 指 定 接 口 中 的 方 法 为 私 有 或 保 护 没 有 意 义 .他 们 默 认 为 公 有 方 法 .







56. 那么如果这些接口中有重复的方法名称呢?

这 种 情 况 中 你 可 以 决 定 如 何 实 现 .当 然 需 要 特 别 得 小 心 .但 是 在 编 译 环 节 是 没 有 问 题 的 .







57. 如何区别重载方法?

不同的参数类型,不同的参数个数,不同的参数顺序.







58. const 和 readonly 有什么区别?



C#的 readonly 关 键 字 只 能 在 字 段 上 面 使 用



public readonly TcpClient client;



不 能 在 类 , 方 法 , 属 性 上 面 使 用 readonly!!



顺 便 看 了 一 下 readonly 和 const 的 区 别 :



· readonly 和 const 都 是 用 来 标 识 常 量 的 。



· const 可 用 于 修 饰 class 的 field 或 者 一 个 局 部 变 量 ( local variable) ; 而 readonly 仅 仅 用 于 修 饰 class 的 field。



· const 常 量 的 值 必 定 在 编 译 时 就 已 明 确 并 且 恒 定 的 ;而 readonly 常 量 却 有 一 点 不 同 ,那 就 是 其 值 可 以 在 运 行 时 编 译 ,当 然 ,它 也 必 须 遵 守 作 为 常



量的约束,那就是值必须恒定不变。



· const 常 量 必 须 在 声 明 的 同 时 对 其 进 行 赋 值 ,并 且 确 保 该 值 在 编 译 时 可 确 定 并 恒 定 ;而 readonly 常 量 则 可 以 根 据 情 况 选 择 在 声 明 的 同 时 对 其 赋 予



一 个 编 译 时 确 定 并 恒 定 的 值 , 或 者 将 其 值 的 初 始 化 工 作 交 给 实 例 构 造 函 数 ( instant constructor) 完 成 。 如 : public readonly string m_Now =



DateTime.Now.ToString();, m_Now 会 随 着 运 行 时 实 际 情 况 变 化 而 变 化 。







· const 常 量 属 于 类 级 别 ( class level) 而 不 是 实 例 对 象 级 别 ( instant object level) , 并 且 它 不 能 跟 static 结 合 一 起 使 用 , 该 常 量 的 值 将 由



整 个 类 的 所 有 实 例 对 象 共 同 分 享 ( 详 细 论 述 参 见 后 面 的 Remark 区 域 ) 。



· readonly 常 量 既 可 以 是 类 级 别 也 可 以 是 实 例 对 象 级 别 的 ,这 取 决 于 它 的 声 明 以 及 初 始 化 工 作 怎 么 实 施 。readonly 可 以 与 static 结 合 使 用 ,用 于



指 定 该 常 量 属 于 类 级 别 , 并 且 把 初 始 化 工 作 交 由 静 态 构 造 函 数 ( static constructor) 完 成 ( 有 关 如 何 把 readonly 常 量 声 明 为 类 级 别 或 实 例 对 象 级 别 的 论 述



清 参 见 后 面 的 Remark 区 域 ) 。



· 能 被 const 修 饰 声 明 为 常 量 的 类 型 必 须 是 以 下 的 基 元 类 型 ( primitive type) : sbyte, byte, short, ushort, int, uint, long, ulong, char,



float, double, float, bool, decimal, string 。



· object, 数 组 ( Array) 和 结 构 ( struct) 不 能 被 声 明 为 const 常 量 。



· 一 般 情 况 下 ,引 用 类 型 是 不 能 被 声 明 为 const 常 量 的 ,不 过 有 一 个 例 外 :string。该 引 用 类 型 const 常 量 的 值 可 以 有 两 种 情 况 ,string 或 null。



其 实 , string 虽 然 是 引 用 类 型 , 但 是 .NET 却 对 它 特 别 处 理 , 这 种 处 理 叫 做 字 符 串 恒 定 性 ( immutable) , 使 得 string 的 值 具 有 只 读 特 性 。



Examples:



using System;



public class Order



{



public Order()



{



Guid guid = Guid.NewGuid();



ID = guid.ToString("D");



}







// 对 于 每 一 份 订 单 , 其 订 单 序 号 都 是 实 时 确 定 的 常 量 。



public readonly string ID;



public override string ToString()



{

58



return "Order ID: " + ID;



}



}



Explaintion:



· 如 果 结 合 数 据 库 使 用 , ID field 通 常 都 会 都 会 与 某 个 表 的 主 健 ( primary key) 关 联 起 来 , 如 Orders 表 的 OrderID。



· 数据库的主健通常采用以下三种方式:



o 自 动 递 增 值 。 你 可 以 通 过 把 DataColumn.AutoIncrement 设 定 为 true 值 来 激 活 自 动 递 增 特 性 。



o 唯一名称。这个是使用自己定义的算法来生成一个唯一序列号。



o GUID( 全 局 唯 一 标 识 符 ) 。 你 可 以 通 过 System.Guid 结 构 来 生 成 GUID, 如 上 例 。



using System;



class Customer



{



public Customer(string name, int kind)



{



m_Name = name;



m_Kind = kind;



}







public const int NORMAL = 0;



public const int VIP = 1;



public const int SUPER_VIP = 2;



private string m_Name;



public string Name



{



get { return m_Name; }



}



private readonly int m_Kind;



public int Kind



{



get { return m_Kind; }



}







public override string ToString()



{



if(m_Kind == SUPER_VIP)



return "Name: " + m_Name + "[SuperVip]";



else if(m_Kind == VIP)



return "Name: " + m_Name + "[Vip]";



else



return "Name: " + m_Name + "[Normal]";



}



}



Remarks:



·一 般 情 况 下 ,如 果 你 需 要 声 明 的 常 量 是 普 遍 公 认 的 并 作 为 单 个 使 用 ,例 如 圆 周 率 ,黄 金 分 割 比 例 等 。你 可 以 考 虑 使 用 const 常 量 ,如 :public const double



PI = 3.1415926;。 如 果 你 需 要 声 明 常 量 , 不 过 这 个 常 量 会 随 着 实 际 的 运 行 情 况 而 决 定 , 那 么 , readonly 常 量 将 会 是 一 个 不 错 的 选 择 , 例 如 上 面 第 一 个 例 子 的



订 单 号 Order.ID。



另 如 而 那 更 (

· 外 , 果 要 表 示 对 象 内 部 的 默 认 值 的 话 , 这 类 值 通 常 是 常 量 性 质 的 , 么 也 可 以 考 虑 const。 多 时 候 我 们 对 源 代 码 进 行 重 构 时 使 用 Replace Magic Number



with Symbolic Constant) , 要 去 除 魔 数 ( Magic Number) 的 影 响 都 会 借 助 于 const 的 这 种 特 性 。



·对 于 readonly 和 const 所 修 饰 的 变 量 究 竟 是 属 于 类 级 别 的 还 是 实 例 对 象 级 别 的 问 题 , 我 们 先 看 看 如 下 代 码 :

59



Using directives#region Using directives



using System;



using System.Collections.Generic;



using System.Text;



#endregion



namespace ConstantLab



{



class Program



{



static void Main(string[] args)



{



Constant c = new Constant(3);



Console.WriteLine("ConstInt = " + Constant.ConstInt.ToString());



Console.WriteLine("ReadonlyInt = " + c.ReadonlyInt.ToString());



Console.WriteLine("InstantReadonlyInt = " + c.Ins tantReadonlyInt.ToString());



Console.WriteLine("StaticReadonlyInt = " + Constant.StaticReadonlyInt.ToString());







Console.WriteLine("Press any key to continue");



Console.ReadLine();



}



}







class Constant



{



public Constant(int instantReadonlyInt)



{



InstantReadonlyInt = instantReadonlyInt;



}







public const int ConstInt = 0;







public readonly int ReadonlyInt = 1;







public readonly int InstantReadonlyIn t;







public static readonly int StaticReadonlyInt = 4;



}



}







· 发

使 用 Visual C#在 Main()里 面 使 用 IntelliSence 插 入 Constant 的 相 关 field 的 时 候 , 现 ReadonlyInt 和 InstantReadonlyInt 需 要 指 定 Constant



的 实 例 对 象 ; 而 ConstInt 和 StaticReadonlyInt 却 要 指 定 Constant class( 参 见 上 面 代 码 ) 。 可 见 , 用 const 或 者 static readonly 修 饰 的 常 量 是 属 于 类 级



别 的 ; 而 readonly 修 饰 的 , 无 论 是 直 接 通 过 赋 值 来 初 始 化 或 者 在 实 例 构 造 函 数 里 初 始 化 , 都 属 于 实 例 对 象 级 别 。



· 一 般 情 况 下 ,如 果 你 需 要 表 达 一 组 相 关 的 编 译 时 确 定 常 量 ,你 可 以 考 虑 使 用 枚 举 类 型( enum),而 不 是 把 多 个 const 常 量 直 接 嵌 入 到 class 中 作



为 field, 不 过 这 两 种 方 式 没 有 绝 对 的 孰 优 孰 劣 之 分 。







using System;



enum CustomerKind



{



SuperVip,

60



Vip,



Normal



}







class Customer



{



public Customer(string name, CustomerKind kind)



{



m_Name = name;



m_Kind = kind;



}







private string m_Name;



public string Name



{



get { return m_Name; }



}







private CustomerKind m_Kind;



public CustomerKind Kind



{



get { return m_Kind; }



}







public override string ToString()



{



return "Name: " + m_Name + "[" + m_Kind.ToString() + "]";



}



}



然 当 并 你 使

· 而 , 这 种 结 合 使 用 枚 举 和 条 件 判 断 的 代 码 阻 碍 了 你 进 行 更 灵 活 的 扩 展 , 有 可 能 导 致 日 后 的 维 护 成 本 增 加 , 可 以 代 之 以 多 态 , 用 Replace Conditional



with Polymorphism 来 对 代 码 进 行 重 构 。



Comments:



·readonly field 准 确 来 说 应 该 翻 译 成 为 “ 只 读 域 ” , 这 里 是 为 了 统 一 翻 译 用 语 才 将 它 和 const 两 者 所 修 饰 的 量 都 说 成 “ 常 量 ” , 希 望 没 有 引 起 误 会 。









59. System.String 和 System.StringBuilder 有什么区别?

String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的



空 间 。在 需 要 对 字 符 串 执 行 重 复 修 改 的 情 况 下 ,与 创 建 新 的 String 对 象 相 关 的 系 统 开 销 可 能 会 非 常 昂 贵 。如 果 要 修 改 字 符 串 而 不 创 建 新 的 对 象 ,则 可



以使用 System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder 类可以提升性能。



StringBuilder 类 并 没 有 String 类 的 功 能 强 大 , 只 提 供 基 本 的 替 换 和 添 加 和 删 除 字 符 串 中 的 文 本 , 但 它 的 工 作 效 率 非 常 高 , 当 定 义 StringBuilder 对 象 时 可



以 指 定 内 存 的 内 存 容 量 ,如 果 不 指 定 系 统 就 会 根 据 对 象 初 始 化 时 的 字 符 串 长 度 来 确 定 。它 有 两 个 主 要 参 数 Length 和 Capacity 分 别 表 示 字 符 串 的 实 际 长 度 和 字



符串占据的内存空间长度。对字符串的修改就是在这个内存中进行的,大大提高了添加和替换的的效率。



如定义:



StringBuilder sb=new StringBuilder("Hello,Welcome",100);// 初 始 化 对 象 并 设 置 初 始 容 量 为 100



sb.Append(" to www.csdn.net");



sb.Replace(old,new);//将 old 替 换 为 new,作 用 与 String.Replace()一 样 只 是 不 需 要 在 过 程 中 复 制 字 符 。



StringBuilder 的 成 员 :



StringBuilder sb=new StringBuilder(" www.csdn.net");//定 义 初 值 为 www.csdn.net 的 对 象 。



StringBuilder sb=new StringBuilder(20);初 始 化 容 量 为 20 的 空 对 象 。

61



另 外 StringBuilder 还 有 MaxCapacity 属 性 用 来 限 定 对 象 可 以 使 用 的 最 大 容 量 。 默 认 大 约 是 int.MaxValue( 20 亿 )



可 以 在 使 用 过 程 中 定 义 sb.MaxCapacity=value;



sb.Append(),给 当 前 字 符 串 追 加 字 符 串 。



sb.AppendFormat()— — 添 加 特 定 格 式 的 字 符 串



sb.Insert()— — 插 入 一 个 子 字 符 串



sb.Remove()— — 从 当 前 字 符 串 删 除 字 符



sb.Replace()— — 替 换 字 符 串 中 指 定 的 字 符



sb.ToString()— — 将 sb 转 化 为 String 对 象







60. string

字符串 驻留 池 (String Pooling): 公 共 语 言 运 行 库 通 过 维 护 一 个 哈 希 表 来 存 放 字 符 串 , 该 表 称 为 字 符 串 驻 留 池 。 它 包 含 程 序 中 以 编 程 方 式 声 明 或



创 建 的 每 个 唯 一 的 字 符 串 的 一 个 引 用 。 因 此 , 具 有 特 定 值 的 字 符 串 的 实 例 在 系 统 中 只 有 一 个 。 [应 该 是 应 用 了 单 例 模 式 的 应 用 。 单 例 可 以 参 考 : 老 生常谈 :



单件模式 ],这 里 非 常 谢 谢 飞林沙 的 提 示 ,应 该 是 享 元 模 式 , 同 样 可 以 参 考 如 下 文 章 : 老 生 常 谈: 享元模 式



字 符 串驻 留: 根 据 字 符 串 驻 留 池 的 定 义 可 以 看 出 字 符 串 驻 留 技 术 的 用 处 。 主 要 目 的 是 减 少 内 存 开 销 和 系 统 开 销 , 充 分 利 用 资 源 。 提 供 了 两 个 重 要 的 方



法:



1:String.Intern :检 索 系 统 对 指 定 String 的 引 用 。 如 果 str 的 值 已 经 留 用 , 则 返 回 系 统 的 引 用 ; 否 则 返 回 对 带 有 str 值 的 字 符 串 的 新 引 用 。



2:String.IsInterned 方 法 :检 索 对 指 定 String 的 引 用 。 如 果 str 位 于 公 共 语 言 运 行 库 “ 拘 留 池 ” 中 , 则 为 对 它 的 String 引 用 ; 否 则 为



nullNothingnullptrnull 引 用



字 符 串的 不可更 改特 性: 当 一 个 字 符 串 创 建 后 , 我 们 就 不 能 对 它 进 行 增 加 长 , 或 者 是 缩 减 , 也 不 能 修 改 其 中 的 任 何 字 符 。 但 有 朋 友 可 以 会 问 , 那 为



什 么 能 对 字 符 串 进 行 那 么 多 的 操 作 呢 ? 例 如 :"+"操 作 符 ,Substring 等 等 。其 实 这 些 操 作 都 会 产 生 一 个 全 新 的 字 符 串 ,而 不 是 在 原 来 字 符 串 在 修 改 。只 要 是 把



操作后的数据赋值给一个新的字符串变量,那么就是产生一个和原字符串完全不同的对象。先看下赋值的情况:把一个 变 量 直 接 赋值 给 一 个 新 的字 符 串 变量 ,



根 据 上 面 的 理 论 来 看 , 下 面 应 该 会 返 回 false,但 实 际 结 果 返 回 的 是 true,因 为 在 字 符 串 驻 留 的 存 在 。



string b = "aaa";



string c = b;



Console.WriteLine(Object.ReferenceEquals(b, c));



但 是 这 并 不 代 表 b 和 c 是 一 样 的 , 我 们 再 看 下 如 下 代 码 : 分 别 分 返 回 aaa,aaaa,false, 既 然 string 属 于 引 用 类 型 , 但 为 什 么 修 改 c 的 值 后 , b 的 值 没 有



跟 着 变 呢 , 这 只 因 为 把 字 符 串 赋 值 给 另 一 个 字 符 串 时 , 会 创 建 一 个 全 新 的 String 对 象 。







string b = "aaa";



string c = b;



c = b + "a";



Console.WriteLine(b);



Console.WriteLine(c);



Console.WriteLine(Object.ReferenceEquals(b, c));







字 符 串 中的"+"操作 符: 通 常 我 们 会 对 字 符 串 进 行 修 改 , 例 如 "+"号 , 表 示 连 接 多 个 字 符 串 。 但 加 号 会 产 生 非 常 多 的 字 符 串 , 这 样 会 造 成 非 常 多 的 内 存



开 销 和 系 统 开 销 , 这 里 可 以 利 用 StringBuilder 来 解 决 多 字 符 串 连 接 的 性 能 问 题 。 来 看 下 下 面 的 示 例 代 码 :



string b = "aaa";



b = b + "a";



StringBuilder strb = new StringBuilder();



strb.Append("aaa");



strb.Append("a");







IL 代 码 : 由 于 ConCat 操 作 后 会 生 成 全 新 的 对 象 , 而 StringBuild 是 在 原 对 象 上 修 改 变 量 , 并 不 会 生 成 新 对 象 , 这 样 就 会 产 生 性 能 优 势 。 上 面 的 代 码 只 是



示范,一般在大量字符串拼接时这种性能问题会倍增 。



安 全 字 符 串 :如 果 字 符 串 中 存 放 了 此 比 较 敏 感 的 信 息 ,例 如 用 户 的 密 码 ,信 用 卡 信 息 之 类 的 ,如 果 此 时 有 非 安 全 代 码 或 者 是 非 托 管 代 码 访 问 这 些 字 符 串 ,这 些



代码可以扫描进程的地址空间,访问包含敏感数据的字符串,以一种非授权的方式来使用这些数据。即使只使用这些字 符 串 时 间 很短 , 马 上 回 进行 垃 圾 回收 ,



也 是 需 要 一 定 时 间 的 , 而 且 string 本 身 无 法 修 改 的 特 性 , 使 这 些 非 安 全 的 或 者 是 非 托 管 代 码 在 访 问 时 会 留 string 的 副 本 在 内 存 中 。



FCL 解 决 了 这 个 问 题 , 它 就 是 System.Security.SecureString: 表 示 应 保 密 的 文 本 。 文 本 在 使 用 时 出 于 保 密 目 的 被 加 密 , 并 在 不 再 需 要 时 从 计 算 机 内 存 中

62



删 除 。 无 法 继 承 此 类 。 当 你 构 造 一 个 SecureString 对 象 时 , 系 统 会 在 内 存 中 分 配 一 个 非 托 管 区 域 , 里 面 包 含 一 个 字 符 数 组 , 里 面 的 内 容 均 是 经 常 加 密 的 。 之



所以是非托管空间,就是为了不让垃圾回收器来管理 。



System.Security.SecureString 实 现 了 IDisposable 接 口 , 这 样 可 以 保 证 在 使 用 完 后 立 即 对 字 符 串 进 行 销 毁 。







61. 如何把一个 array 复制到 arrayList 里

foreach( object o in array )arrayList.Add(o);







62. 概述反射和序列化

反 射 :程 序 集 包 含 模 块 , 而 模 块 包 含 类 型 , 类 型 又 包 含 成 员 .反 射 则 提 供 了 封 装 程 序 集 、 模 块 和 类 型 的 对 象 .您 可 以 使 用 反 射 动 态 地 创 建 类 型 的 实 例 , 将 类 型 绑



定 到 现 有 对 象 , 或 从 现 有 对 象 中 获 取 类 型 .然 后 , 可 以 调 用 类 型 的 方 法 或 访 问 其 字 段 和 属 性



序 列 化 :序 列 化 是 将 对 象 转 换 为 容 易 传 输 的 格 式 的 过 程 .例 如 , 可 以 序 列 化 一 个 对 象 , 然 后 使 用 HTTP 通 过 Internet 在 客 户 端 和 服 务 器 之 间 传 输 该 对 象 .在 另



一端,反序列化将从该流重新构造对象.







63. 用 sealed 修饰的类有什么特点

sealed 修 饰 符 用 于 防 止 从 所 修 饰 的 类 派 生 出 其 它 类 .如 果 一 个 密 封 类 被 指 定 为 其 他 类 的 基 类 , 则 会 发 生 编 译 时 错 误 .



密封类不能同时为抽象类.



sealed 修 饰 符 主 要 用 于 防 止 非 有 意 的 派 生 ,但 是 它 还 能 促 使 某 些 运 行 时 优 化 .具 体 说 来 ,由 于 密 封 类 永 远 不 会 有 任 何 派 生 类 ,所 以 对 密 封 类 的 实 例 的 虚 拟 函 数



成员的调用可以转换为非虚拟调用来处理.









64. 程序设计: 猫大叫一声,所有的老鼠都开始逃跑,主人被惊醒.(C#语言)

要 求 : 1.要 有 联 动 性 , 老 鼠 和 主 人 的 行 为 是 被 动 的 .



2.考 虑 可 扩 展 性 , 猫 的 叫 声 可 能 引 起 其 他 联 动 效 应 .



要 点 : 1. 联 动 效 果 , 运 行 代 码 只 要 执 行 Cat.Cryed()方 法 .2. 对 老 鼠 和 主 人 进 行 抽 象



评 分 标 准 : .构 造 出 Cat、 Mouse、 Master 三 个 类 , 并 能 使 程 序 运 行 (2 分 )



从 Mouse 和 Master 中 提 取 抽 象 ( 5 分 )



联 动 效 应 , 只 要 执 行 Cat.Cryed()就 可 以 使 老 鼠 逃 跑 , 主 人 惊 醒 .(3 分 )







public interface Observer



{



void Response(); //观 察 者 的 响 应 , 如 是 老 鼠 见 到 猫 的 反 映



}



public interface Subject



{



void AimAt(Observer obs); // 针 对 哪 些 观 察 者 , 这 里 指 猫 的 要 扑 捉 的 对 象 ---老 鼠



}



public class Mouse : Observer



{



private string name;



public Mouse(string name, Subject subj)



{



this.name = name;



subj.AimAt(this);



}







public void Response()



{



Console.WriteLine(name + " attempt to escape!");



}



}

63



public class Master : Observer



{



public Master(Subject subj)



{



subj.AimAt(this);



}







public void Response()



{



Console.WriteLine("Host waken!");



}



}







public class Cat : Subject



{



private ArrayList observers;



public Cat()



{



this.observers = new ArrayList();



}



public void AimAt(Observer obs)



{



this.observers.Add(obs);



}



public void Cry()



{



Console.WriteLine("Cat cryed!");



foreach (Observer obs in this.observers)



{



obs.Response();



}



}



}



class MainClass



{



static void Main(string[] args)



{



Cat cat = new Cat();



Mouse mouse1 = new Mouse("mouse1", cat);



Mouse mouse2 = new Mouse("mouse2", cat);



Master master = new Master(cat);



cat.Cry();



}



}







//---------------------------------------------------------------------------------------------



设 计 方 法 二 : 使 用 event -- delegate 设 计 ..



public delegate void SubEventHandler();



public abstract class Subject

64



{



public event SubEventHandler SubEvent;



protected void FireAway()



{



if (this.SubEvent != null)



this.SubEvent();



}



}



public class Cat : Subject



{



public void Cry()



{



Console.WriteLine("cat cryed.");



this.FireAway();



}



}



public abstract class Observer



{



public Observer(Subject sub)



{



sub.SubEvent += new SubEventHandler(Response);



}



public abstract void Response();



}



public class Mouse : Observer



{



private string name;



public Mouse(string name, Subject sub) : base(sub)



{



this.name = name;



}



public override void Response()



{



Console.WriteLine(name + " attempt to escape!");



}



}



public class Master : Observer



{



public Master(Subject sub) : base(sub){}



public override void Response()



{



Console.WriteLine("host waken");



}



}



class Class1



{



static void Main(string[] args)



{



Cat cat = new Cat();

65



Mouse mouse1 = new Mouse("mouse1", cat);



Mouse mouse2 = new Mouse("mouse2", cat);



Master master = new Master(cat);



cat.Cry();



}



}









65. C#中 property 与 attribute 的区别,他们各有什么用处,这种机制的好处在哪里?



66. C#中要使一个类支持 FOREACH 遍历,实现过程怎样?

在 C#中 ,凡 是 实 现 了 IEnumerator 接 口 的 数 据 类 型 都 可 以 用 foreach 语 句 进 行 迭 代 访 问 ,可 是 ,对 于 自 定 义 类 型 如 何 实 现 这 个 接 口 以 支 持 foreach 的 迭 代 呢 ?



要 实 现 这 个 功 能 , 先 来 看 看 IEnumerable 和 IEnumerator 接 口 的 定 义 :



public interface IEnumerable



{



//IEnumerable 只 有 一 个 方 法 , 返 回 可 循 环 访 问 集 合 的 枚 举 数 。



IEnumerator GetEnumerator() ;



}



public interface IEnumerator



{



// 方 法



//移 到 集 合 的 下 一 个 元 素 。 如 果 成 功 则 返 回 为 true; 如 果 超 过 集 合 结 尾 , 则 返 回 false。



bool MoveNext();



// 将 集 合 设 置 为 初 始 位 置 , 该 位 置 位 于 集 合 中 第 一 个 元 素 之 前



void Reset();







// 属 性 : 获 取 集 合 中 的 当 前 元 素



object Current { get; }



}



如 果 我 们 的 自 定 义 数 据 类 型 派 生 于 这 两 个 接 口 , 并 实 现 接 口 中 的 方 法 , 即 可 用 foreach 进 行 迭 代 。



下面是一个实现的简单例子:



using System;



using System.Collections ;



using System.Collections.Generic;



using System.Text;







namespace IEnumberableTest



{



class Program



{



static void Main(string[] args)



{



myEnum enum1 = new myEnum(20);







foreach ( point p in enum1)



{



Console.WriteLine("("+p.x .ToString ()+","+p.y.ToString ()+","+p.z.ToString ()+")");



}







Console.Read();

66



}



}







//一 个 结 构 体 , 用 于 类 myEnum



struct point



{



public int x;



public int y;



public int z;



}



//我 们 的 一 个 派 生 于 IEnumerable 和 IEnumerator 接 口 的 自 定 义 类



class myEnum :IEnumerable,IEnumerator



{



//定 义 索 引



private int index;







//定 义 一 个 point 结 构 的 数 组



private point[] points;







//类 的 构 造 函 数 , 用 于 初 始 化 point 结 构 数 组



public myEnum(int numofpoint)



{



this.index = -1;



points = new point[numofpoint];







for (int i = 0; i = points.Length ? false : true;



}







}



}







67. 大概描述一下 ASP.NET 服务器控件的生命周期

ASP.NET 服 务 器 控 件 生 命 周 期 所 要 经 历 的 11 个 阶 段 。



(1)初 始 化 :在 此 阶 段 中 , 主 要 完 成 两 项 工 作 : 一 、 初 始 化 在 传 入 Web 请 求 生 命 周 期 内 所 需 的 设 置 ;二 、 跟 踪 视 图 状 态 。 首 先 , 页 面 框 架 通 过 默 认 方 式 引 发 Init



事 件 , 并 调 用 OnInit()方 法 , 控 件 开 发 人 员 可 以 重 写 该 方 法 为 控 件 提 供 初 始 化 逻 辑 。 此 后 , 页 面 框 架 将 调 用 TrackViewState 方 法 来 跟 踪 视 图 状 态 。 需 要 注 意



的 是 : 多 数 情 况 下 , Control 基 类 提 供 的 TrackViewState 方 法 实 现 已 经 足 够 了 。 只 有 在 控 件 定 义 了 复 杂 属 性 时 , 开 发 人 员 才 可 能 需 要 重 写 TrackViewState 方



法。



(2)加 载 视 图 状 态 :此 阶 段 的 主 要 任 务 是 检 查 ASP.NET 服 务 器 控 件 是 否 存 在 以 及 是 否 需 要 将 其 状 态 恢 复 到 它 在 处 理 之 前 的 请 求 结 束 的 状 态 。因 此 该 过 程 发 生 在 页



面 回 传 过 程 中 ,而 不 是 初 始 化 请 求 过 程 。在 此 阶 段 ,页 面 框 架 将 自 动 恢 复 ViewState 字 典 。如 果 服 务 器 控 件 不 维 持 其 状 态 ,或 者 它 有 能 力 通 过 默 认 方 式 保 存 其



所 有 状 态 而 使 用 ViewState 字 典 ,那 么 开 发 人 员 则 不 必 实 现 任 何 逻 辑 。针 对 那 些 无 法 在 ViewState 字 典 中 存 储 的 数 据 类 型 或 者 需 要 自 定 义 状 态 管 理 的 情 况 ,开



发 人 员 可 以 通 过 重 写 LoadViewState 方 法 来 自 定 义 状 态 的 恢 复 和 管 理 。



(3)处 理 回 发 数 据 :若 要 使 控 件 能 够 检 查 客 户 端 发 回 的 窗 体 数 据 , 那 么 必 须 实 现 System.Web.UI.IPostBackDataHandler 接 口 的 LoadPostData()方 法 。 因 此 只



有处理回发数据的控件参与此阶段。



控 状 此 开

(4)加 载 :至 此 阶 段 开 始 , 件 树 中 的 ASP.NET 服 务 器 控 件 已 创 建 并 初 始 化 、 态 已 还 原 并 且 窗 体 控 件 反 映 了 客 户 端 的 数 据 。 时 , 发 人 员 可 以 通 过 重 写 OnLoad()



方法来实现每个请求共同的逻辑。



(5)发 送 回 发 更 改 通 知 :在 此 阶 段 , ASP.NET 服 务 器 控 件 通 过 引 发 事 件 作 为 一 种 信 号 , 表 明 由 于 回 发 而 发 生 的 控 件 状 态 变 化 (因 此 该 阶 段 仅 用 于 回 发 过 程 )。 为 了



建 立 这 种 信 号 , 开 发 人 员 必 须 再 次 使 用 System.Web.UI.IPostBackDataHandler 接 口 , 并 实 现 另 一 方 法 - RaisePostBackChangedEvent()。 其 判 断 过 程 为 : 如 果



则 页

控 件 状 态 因 回 发 而 更 改 , LoadPostData()返 回 true;否 则 返 回 false。 面 框 架 跟 踪 所 有 返 回 true 的 控 件 并 在 这 些 控 件 上 调 用 RaisePostDataChangedEvent()。



(6)处 理 回 发 事 件 :该 阶 段 处 理 引 起 回 发 的 客 户 端 事 件 。 为 了 便 于 将 客 户 端 事 件 映 射 到 服 务 器 端 事 件 上 进 行 处 理 , 开 发 人 员 在 此 阶 段 可 以 通 过 实 现



System.Web.UI.IPostBackEventHandler 接 口 的 RaisePostBackEvent()方 法 来 实 现 该 逻 辑 。由 此 途 径 ,服 务 器 控 件 将 成 功 捕 获 回 发 的 客 户 端 事 件 进 行 服 务 器 端



的相应处理。



(7)预 呈 现 :该 阶 段 完 成 在 生 成 控 件 之 前 所 需 要 的 任 何 工 作 。 通 常 情 况 下 是 通 过 重 写 OnPreRender()方 法 完 成 该 工 作 。 需 要 注 意 的 是 : 在 该 阶 段 , 可 以 保 存 在 预



呈现阶段对控件状态所做的更改,而在呈现阶段进 行的更改则会丢失。



(8)保 存 状 态 :如 果 ASP.NET 服 务 器 控 件 不 维 持 状 态 ,或 者 它 有 能 力 通 过 默 认 方 式 保 存 其 所 有 状 态 而 使 用 ViewState 字 典 ,那 么 开 发 人 员 不 必 在 该 阶 段 实 现 任 何



逻 辑 。因 为 这 个 保 存 状 态 的 过 程 是 自 动 的 。如 果 ASP.NET 服 务 器 控 件 需 要 自 定 义 状 态 保 存 ,或 者 控 件 无 法 在 ViewState 字 典 中 存 储 特 殊 的 数 据 类 型 ,则 需 要 通



过 重 写 SaveViewState()方 法 来 实 现 状 态 保 存 。



(9)呈 现 :表 示 向 HTTP 输 出 流 中 写 入 标 记 文 本 的 过 程 。 开 发 人 员 通 过 重 写 Render()方 法 使 其 在 输 出 流 上 自 定 义 标 记 文 本 。



(10)处 置 :在 此 阶 段 中 , 通 过 重 写 Dispose ()方 法 完 成 释 放 对 昂 贵 资 源 的 引 用 , 如 数 据 库 链 接 等 。



(11)卸 载 :完 成 的 工 作 与 "处 置 "阶 段 相 同 , 但 是 , 开 发 人 员 通 常 在 Dispose()方 法 中 执 行 清 除 , 而 不 处 理 Unload 事 件 。







68. 类划分的依据.类粒度问题



69. 数据库存储过程和函数的区别

存储过程:



存 储 过 程 可 以 使 得 对 数 据 库 的 管 理 、 以 及 显 示 关 于 数 据 库 及 其 用 户 信 息 的 工 作 容 易 得 多 。 存 储 过 程 是 SQL 语 句 和 可 选 控 制 流 语 句 的 预 编 译 集 合 , 以 一 个 名 称



存储并作为一个单元处理。存储过程存储在数据库内,可由应用程序通过一个调用执行 ,而且允许用户声明变量、有条件执行以及其它强大的编程功能。

68



存储过程可包含程序流、逻辑以及对数据库的查询。它们可以接受参数、输出参数、返回单个或多个结果集以及返回值。



可 以 出 于 任 何 使 用 SQL 语 句 的 目 的 来 使 用 存 储 过 程 , 它 具 有 以 下 优 点 :



1、 可 以 在 单 个 存 储 过 程 中 执 行 一 系 列 SQL 语 句 。



2、 可 以 从 自 己 的 存 储 过 程 内 引 用 其 它 存 储 过 程 , 这 可 以 简 化 一 系 列 复 杂 语 句 。



3、 存 储 过 程 在 创 建 时 即 在 服 务 器 上 进 行 编 译 , 所 以 执 行 起 来 比 单 个 SQL 语 句 快 。









用户定义函数:



Microsoft SQL Server 2000 允 许 创 建 用 户 定 义 函 数 。 与 任 何 函 数 一 样 , 用 户 定 义 函 数 是 可 返 回 值 的 例 程 。 根 据 所 返 回 值 的 类 型 , 每 个 用 户 定 义 函 数 可 分 成 以



下三个类别:



1、 返 回 可 更 新 数 据 表 的 函 数



如 果 用 户 定 义 函 数 包 含 单 个 Select 语 句 且 该 语 句 可 更 新 , 则 该 函 数 返 回 的 表 格 格 式 结 果 也 可 以 更 新 。



2、 返 回 不 可 更 新 数 据 表 的 函 数



如 果 用 户 定 义 函 数 包 含 不 止 一 个 Select 语 句 , 或 包 含 一 个 不 可 更 新 的 Select 语 句 , 则 该 函 数 返 回 的 表 格 格 式 结 果 也 不 可 更 新 。



3、 返 回 标 量 值 的 函 数



用户定义函数可以返回标量值。









存储过程



功能强大,限制少



不能直接引用返回值



用 select 语 句 返 回 记 录 集







自定义函数



诸多限制,有许多语句不能使用,许多功能不能实现



可以直接引用返回值



用表变量返回记录集







70. 数据库事务是什么?

事 务 是 作 为 单 个 逻 辑 工 作 单 元 执 行 的 一 系 列 操 作 。一 个 逻 辑 工 作 单 元 必 须 有 四 个 属 性 ,称 为 原 子 性 、一 致 性 、隔 离 性 和 持 久 性 (ACID) 属 性 ,只 有 这 样 才 能 成



为一个事务。



原子性



事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。



一致性



事 务 在 完 成 时 ,必 须 使 所 有 的 数 据 都 保 持 一 致 状 态 。在 相 关 数 据 库 中 ,所 有 规 则 都 必 须 应 用 于 事 务 的 修 改 ,以 保 持 所 有 数 据 的 完 整 性 。事 务 结 束 时 ,所 有 的 内



部数据结构(如 B 树索引或双向链表)都必须是正确的。



隔离



由 并 发 事 务 所 做 的 修 改 必 须 与 任 何 其 他 并 发 事 务 所 做 的 修 改 隔 离 。事 务 识 别 数 据 时 数 据 所 处 的 状 态 ,要 么 是 另 一 并 发 事 务 修 改 它 之 前 的 状 态 ,要 么 是 第 二 个 事



务 修 改 它 之 后 的 状 态 ,事 务 不 会 识 别 中 间 状 态 的 数 据 。这 称 为 可 串 行 性 ,因 为 它 能 够 重 新 装 载 起 始 数 据 ,并 且 重 播 一 系 列 事 务 ,以 使 数 据 结 束 时 的 状 态 与 原 始



事务执行的状态相同。



持久性



事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。







71. Sql Server 数据库事务介绍 Sql 语句,SqlTransaction 和 TransactionScope 的使用方法

Sql 语 句 事 务



Sql Server2005/2008 提 供 了 begin tran, commit tran 和 rollback tran 三 个 语 句 来 显 示 的 使 用 事 务 。 begin tran 表 示 事 务 开 始 , commit tran 表 示 事 务 提



交 , rollback tran 表 示 事 务 回 滚 。 具 体 代 码 如 下 :







begin try



begin tran

69



insert into dbo.TransTestTable values (66,'66');



update dbo.TransTestTable set [Name] = '77' where [Id] = 66;



--RAISERROR ('Error raised in TRY block.',16,1);



commit tran



end try



begin catch



rollback tran



end catch







代 码 中 的 begin try 和 begin catch 是 捕 获 异 常 时 使 用 的 , 只 在 sql server2005/2008 中 支 持 , sql server 2000 上 不 支 持 这 个 语 句 。 在 begin try 和



end try 之 间 的 代 码 运 行 时 如 果 发 生 异 常 , 则 程 序 会 跳 转 到 begin catch 和 end catch 中 执 行 相 关 的 rollback tran 回 滚 操 作 。 在 begin tran 和 commit tran



之间就是一个事务, 否

insert 和 update 必 须 同 时 成 功 , 则 就 同 时 失 败 。 只

RAISERROR 语 句 的 意 思 是 抛 出 一 个 异 常 , 在 sql server2005/2008 中 支 持 ,sql server



2000 上 不 支 持 这 个 语 句 。



执 行 上 面 的 代 码 ,我 们 会 发 现 ,插 入 和 更 新 同 时 都 成 功 了 。把 RAISERROR 的 注 释 去 掉 后 ,再 执 行 ,我 们 会 发 现 ,插 入 和 更 新 都 回 滚 了 。因 为 RAISERROR



抛 出 异 常 后 , 没 有 执 行 到 commit tran, 而 是 直 接 执 行 begin catch 里 面 的 rollback tran 回 滚 语 句 了



SqlTransaction 使 用 :



SqlTransaction 是 System.Data.SqlClient 命 名 空 间 下 的 一 个 事 务 类 , 主 要 方 法 有 Commit()和 Rollback()两 个 函 数 , 更 多 方 法 和 属 性 请 参 考 MSDN。 具 体 代



码如下:







static void Main(string[] args)



{







SqlConnection sqlConn = new SqlConnection(



ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString);



SqlTransaction sqlTrans = null;



try



{



sqlConn.Open();



sqlTrans = sqlConn.BeginTransaction();// 事 务 开 始



SqlCommand sqlComm = new SqlCommand("", sqlConn, sqlTrans);



sqlComm.CommandTimeout = 120;



sqlComm.CommandType = System.Data.CommandType.Text;



string insertSql = "insert into dbo.TransTestTable values (66,'66');";



string updateSql = "update dbo.TransTestTable set [N ame] = '77' where [Id] = 66;";



sqlComm.CommandText = insertSql;



sqlComm.ExecuteNonQuery();// 执 行 insert



sqlComm.CommandText = updateSql;



sqlComm.ExecuteNonQuery();// 执 行 update



//throw new Exception("test exception.the transaction must rollback");







sqlTrans.Commit();//事 务 提 交



}



catch (Exception ex)



{



sqlTrans.Rollback();//事 务 回 滚



Console.WriteLine(ex.Message);



}



finally



{

70



if (sqlConn.State != System.Data.ConnectionState.Closed)



sqlConn.Close();



}







Console.ReadLine();



}







上 面 的 代 码 显 示 了 SqlTransaction 类 的 基 本 使 用 方 法 。 首 先 使 用 SqlConnection 建 立 连 接 后 , sqlConn.BeginTransaction() 表 示 事 务 的 开 始 , 在 执 行 一 些 基



本 操 作 后 ( 代 码 是 执 行 一 个 insert 和 一 个 update) 后 , 执 行 sqlTrans.Commit();表 示 事 务 提 交 , 这 时 候 , 刚 才 insert 和 update 的 数 据 在 数 据 库 才 能 被 使



用 。 如 果 把 throw new Exception("test exception.the transaction must rollback"); 这 句 的 注 释 去 掉 , 我 们 会 发 现 , 程 序 没 有 执 行 提 交 , 而 是 直 接 执 行



了 catch 中 的 Rollback(), 进 行 了 回 滚 。 那 么 刚 才 的 insert 和 update 一 起 被 回 滚 。



TransactionScope 用 法 :



TransactionScope 继 承 IDisposable 接 口 , 所 以 一 般 在 using 中 使 用 。 具 体 代 码 如 下 :



static void Main(string[] args)



{



using (TransactionScope scope = new TransactionScope())



{



SqlConnection sqlConn = new SqlConnection(



ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString);



sqlConn.Open();







string insertSql = "insert into [TransTestTable] values(11,'11')";



string updateSql = "update [TransTestTable] set [Name] = '111' where [Id] = 11";







SqlCommand sqlComm = new SqlCommand(insertSql, sqlConn);



sqlComm.CommandType = System.Data.CommandType.Text;



sqlComm.ExecuteNonQuery();







sqlComm = new SqlCommand(updateSql, sqlConn);



sqlComm.CommandType = System.Data.CommandType.Text;



sqlComm.ExecuteNonQuery();







sqlConn.Close();







scope.Complete();



}







Console.ReadLine();



}







在 using 中 定 义 了 一 个 TransactionScope,相 当 于 定 义 了 一 个 事 务 范 围 即 这 个 事 务 作 用 域 为 using 内 。程 序 执 行 了 两 个 动 作 ,一 个 insert,一 个 update,最



后 执 行 了 scope.Complete();相 当 于 提 交 事 务 。 如 果 把 scope.Complete();注 释 掉 , 我 们 会 发 现 insert 和 update 都 被 回 滚 了 , 因 为 在 using 作 用 域 内 , 如 果



没 有 提 交 命 令 , 那 么 scope 在 销 毁 时 , 会 自 动 回 滚 所 有 的 操 作 。









72. 数据库游标的作用?如何知道游标已经到了最后?



73. 数据库触发器分为事前触发和事后触发,这两种触发有和区别.语句级触发和行级触发有何区

别.

触发器种类

71



触 发 器 的 种 类 可 划 分 为 4 种 : 1.数 据 操 纵 语 言 (DML)触 发 器 、 2.替 代 (INSTEAD OF)触 发 器 、 3.数 据 定 义 语 言 (DDL)触 发 器 、 4.数 据 库 事 件 触 发 器 。



数 据 操 纵 语 言 (DML)触 发 器 :简 称 DML 触 发 器 ,是 定 义 在 表 上 的 触 发 器 ,创 建 在 表 上 。由 DML 事 件 引 发 的 触 发 器 ,编 写 DML 触 发 器 时 的 两 点 要 素 是 : 1.确 定 触



发 的 表 ,即 在 其 上 定 义 触 发 器 的 表 。2.确 定 触 发 的 事 件 ,DML 触 发 器 的 触 发 事 件 有 INSERT、UPDATE 和 DELETE 三 种 ;替 代 触 发 器 ,简 称 INSTEADOF 触 发 器 ,创 建



在 视 图 上 ,用 来 替 换 对 视 图 进 行 的 删 除 、插 入 和 修 改 操 作 ; 数 据 定 义 语 言 (DDL)触 发 器 ,简 称 DDL 触 发 器 ,定 义 在 模 式 上 ,触 发 事 件 是 数 据 对 象 的 创 建 和 修 改 ;



数据库事件触发器,定义在整个数据库或模式上,触发事件是数据库事件 .



ORACLE 产 生 数 据 库 触 发 器 的 语 法 为 :



CREATE [OR REPLACE] TRIGGER 触 发 器 名



{BEFORE|AFTER|INSTEAD OF} 触 发 事 件 1 [OR 触 发 事 件 2...]



ON 表 名



WHEN 触 发 条 件



[FOR EACH ROW]



DECLARE



声明部分



BEGIN



主体部分



END;



其中:



触 发 器 名 :触 发 器 对 象 的 名 称 。由 于 触 发 器 是 数 据 库 自 动 执 行 的 ,因 此 该 名 称 只 是 一 个 名 称 ,没 有 实 质 的 用 途 。一 个 触 发 器 可 由 多 个 不 同 的 数 据 操 纵 语 言 操 作



触 发 。 在 触 发 器 中 , 可 用 INSERTING、 DELETING、 UPDATING 谓 词 来 区 别 不 同 的 数 据 操 纵 语 言 操 作 。 这 些 谓 词 可 以 在 IF 分 支 条 件 语 句 中 作 为 判 断 条 件 来 使 用 。



触 发 时 间 : 指 明 触 发 器 何 时 执 行 , 该 值 可 取 , 触 发 的 时 间 有 BEFORE 和 AFTER 两 种 , 分 别 表 示 触 发 动 作 发 生 在 DML 语 句 执 行 之 前 和 语 句 执 行 之 后 。 确 定 触 发 级



别 , 有 语 句 级 触 发 器 和 行 级 触 发 器 两 种 。 语 句 级 触 发 器 表 示 SQL 语 句 只 触 发 一 次 触 发 器 , 行 级 触 发 器 表 示 SQL 语 句 影 响 的 每 一 行 都 要 触 发 一 次 。



Before:表 示 在 数 据 库 动 作 之 前 触 发 器 执 行 ;在 SQL 语 句 的 执 行 过 程 中 ,如 果 存 在 行 级 BEFORE 触 发 器 ,则 SQL 语 句 在 对 每 一 行 操 作 之 前 ,都 要 先 执 行 一 次 行 级



BEFORE 触 发 器 , 然 后 才 对 行 进 行 操 作 。 如 果 存 在 行 级 AFTER 触 发 器 , 则 SQL 语 句 在 对 每 一 行 操 作 之 后 , 都 要 再 执 行 一 次 行 级 AFTER 触 发 器 。



after: 表 示 在 数 据 库 动 作 之 后 出 发 器 执 行 。 如 果 存 在 语 句 级 AFTER 触 发 器 , 则 在 SQL 语 句 执 行 完 毕 后 , 要 最 后 执 行 一 次 语 句 级 AFTER 触 发 器 。



触 发 事 件 : 指 明 哪 些 数 据 库 动 作 会 触 发 此 触 发 器 ,指 INSERT、 DELETE 或 UPDATE 事 件 , 事 件 可 以 并 行 出 现 , 中 间 用 OR 连 接 ;



insert: 数 据 库 插 入 会 触 发 此 触 发 器 ;



update: 数 据 库 修 改 会 触 发 此 触 发 器 ;



delete: 数 据 库 删 除 会 触 发 此 触 发 器 。



表 名:数据库触发器所在的表。



for each row: 表 示 触 发 器 为 行 级 触 发 器 , 省 略 则 为 语 句 级 触 发 器 ,对 表 的 每 一 行 触 发 器 执 行 一 次 。



触 发 器 的 创 建 者 或 具 有 DROP ANY TIRGGER 系 统 权 限 的 人 才 能 删 除 触 发 器 。 删 除 触 发 器 的 语 法 如 下 :



DROP TIRGGER 触 发 器 名



可以通过命令设置触发器的可用状态,使其暂时关闭或重新打开,即当触发器暂时不用时,可以将其置成无效状态,在使用时重新打开。该命令语法如下:



ALTER TRIGGER 触 发 器 名 {DISABLE|ENABLE}



其 中 , DISABLE 表 示 使 触 发 器 失 效 , ENABLE 表 示 使 触 发 器 生 效 。



同 存 储 过 程 类 似 , 触 发 器 可 以 用 SHOW ERRORS 检 查 编 译 错 误 。



如 果 有 多 个 触 发 器 被 定 义 成 为 相 同 时 间 、相 同 事 件 触 发 ,且 最 后 定 义 的 触 发 器 是 有 效 的 ,则 最 后 定 义 的 触 发 器 被 触 发 ,其 他 触 发 器 不 执 行 。触 发 器 体 内 禁 止 使



用 COMMIT、ROLLBACK、SAVEPOINT 语 句 ,也 禁 止 直 接 或 间 接 地 调 用 含 有 上 述 语 句 的 存 储 过 程 。定 义 一 个 触 发 器 时 要 考 虑 上 述 多 种 情 况 ,并 根 据 具 体 的 需 要 来 决



定触发器的种类。



触发器的作用



触发器的主要作用就是其能够实现由主键和外键所不能保证的复杂的参照完整性和数据的一致性。除此之外,触发器还有其它许多不同的功能:



(1) 强 化 约 束 (Enforce restriction)



触 发 器 能 够 实 现 比 CHECK 语 句 更 为 复 杂 的 约 束 。



(2) 跟 踪 变 化 Auditing changes



触发器可以侦测数据库内的操作,从而不允许数据库中未经许可的指定更新和变化。



(3) 级 联 运 行 (Cascaded operation)。



触 发 器 可 以 侦 测 数 据 库 内 的 操 作 , 并 自 动 地 级 联 影 响 整 个 数 据 库 的 各 项 内 容 。 例 如 , 某 个 表 上 的 触 发 器 中 包 含 有 对 另 外 一 个 表 的 数 据 操 作 (如 删 除 , 更 新 , 插



入 )而 该 操 作 又 导 致 该 表 上 触 发 器 被 触 发 。



(4) 存 储 过 程 的 调 用 (Stored procedure invocation) 。

72



为 了 响 应 数 据 库 更 新 触 , 发 器 可 以 调 用 一 个 或 多 个 存 储 过 程 , 甚 至 可 以 通 过 外 部 过 程 的 调 用 而 在 DBMS( 数 据 库 管 理 系 统 )本 身 之 外 进 行 操 作 。



由 此 可 见 ,触 发 器 可 以 解 决 高 级 形 式 的 业 务 规 则 或 复 杂 行 为 限 制 以 及 实 现 定 制 记 录 等 一 些 方 面 的 问 题 。例 如 ,触 发 器 能 够 找 出 某 一 表 在 数 据 修 改 前 后 状 态 发 生



的 差 异 , 并 根 据 这 种 差 异 执 行 一 定 的 处 理 。 此 外 一 个 表 的 同 一 类 型 (INSERT、 UPDATE、 DELETE)的 多 个 触 发 器 能 够 对 同 一 种 数 据 操 作 采 取 多 种 不 同 的 处 理 。



总体而言,触发器性能通常比较低。



系 因 而

当运行触发器时, 统处理的大部分时间花费在参照其它表的这 一处理上, 为这些表既不在内存中也不在数据库设备上, 删除表和插入表总是位于内存中。



可见触发器所参照的其它表的位置决定了操作要花费的时间长短







数据库触发器有以下两种:



( 1) 语 句 级 触 发 器 :在 CREATE TRIGGER 语 句 中 不 包 含 FOR EACH ROW 子 句 ;



( 2) 行 级 触 发 器 : 有 FOR EACH ROW 子 句 。



区 别 :语 句 级 触 发 器 对 于 触 发 事 件 只 能 触 发 一 次 ,而 且 不 能 访 问 受 触 发 器 影 响 的 每 一 行 的 列 值 。行 级 触 发 器 可 对 受 触 发 器 影 响 的 每 一 行 触 发 。并 且 能 够 访 问 原



列 值 和 通 过 SQL 语 句 处 理 新 列 值 。









74. 用 C#实现以下功能

a 产 生 一 个 int 数 组 , 长 度 为 100, 并 向 其 中 随 机 插 入 1-100, 并 且 不 能 重 复 .



b 对上面生成的数组排序,需要支持升序、降序两种顺序







75. 请说出强名的含义

签名机制



1. 用 SN.exe 生 成 一 个 key 文 件 , 这 个 key 文 件 包 括 一 个 public key 和 一 个 private key.



2. 用 这 个 key 文 件 签 名 assembly 时 , 编 译 器 将 用 private key 签 名 程 序 集 , 并 将 public key 嵌 入 manifest 中



3. 编 译 器 哈 希 manifest 中 所 有 的 assembly 内 容 , 并 将 此 哈 希 值 各 自 assembly 的 FileDef Talbe 中 .



4. 当 如 上 3 步 处 理 后 , 编 译 器 将 哈 希 PE 文 件 的 整 个 内 容 (除 authenticode signature, 强 名 称 数 据 , PE 头 ), 然 后 将 此 哈 希 值 用 private key 签 名 . 得 到 RSA



数字签名.



5. 将 此 数 字 签 名 嵌 入 到 PE 文 件 的 CLR 头



防修改机制



1. 当 签 名 后 的 assembly 安 装 到 GAC, 系 统 会 哈 希 PE 文 件 (同 签 名 ), 得 到 哈 希 值



2. 系 统 读 取 PE 文 件 中 CLR 头 中 的 RSA 签 名 , 并 用 public key(包 含 在 manifest 中 )解 密



3. 比 较 第 1 步 得 到 的 哈 希 值 和 第 2 步 得 到 解 密 值 是 否 一 致 , 从 而 判 断 其 是 否 被 修 改 .







一 个 程 序 集 包 括 四 个 区 分 彼 此 的 属 性 : 文 件 名 ( 不 包 括 扩 展 ) ,版 本 号 ,文 化 标 识 ,公 钥 标 识



现在让我们来看看一个比较常见的强名称程序集:



Name= MSCorLib



Version=1.0.3300.0



Culture=neutral



PublicKeyToken=b77a5c561934e089



如 果 我 们 自 己 编 写 一 个 弱 名 称 程 序 集 , 那 么 通 常 PublicKeyToken 这 一 项 就 没 有 。



下面我尝试着说一说强名称的机制。



首 先 , 你 通 过 SN.exe 获 得 一 个 公 钥 和 一 个 密 钥 。



然 后 , 使 用 公 钥 对 程 序 集 的 可 执 行 文 件 ( 不 包 括 DOS 头 、 PE 头 等 ) 进 行 哈 希 算 法 , 得 到 一 个 文 件 散 列 值 。



最后,使用密钥对文件散列值进行加密,得到一个密文。



这样,最后的强名称程序集里面要三样东西: 公钥标识(公钥的散列值的最后八个字节), 公钥 , 密文



使 用 公 钥 和 程 序 集 的 可 执 行 文 件( 不 包 括 DOS 头 、PE 头 等 )进 行 哈 希 算 法 可 以 得 到 一 个 文 件 散 列 值 ,使 用 公 钥 和 密 文 也 可 以 得 到 一 个 文 件 散 列 值 ,如 果 这 两 个



散 列 值 完 全 一 致 , OK, 验 证 通 过 。



最后,说说公钥标识(公钥的散列值的最后八个字节)的作用:



区分程序集,上面提到过它是程序集区分彼此的四大属性之一



验证公钥



总的来说,强名称机制最起码起到两个作用:



区分程序集

73



避免程序集被恶意更改







76. CLR 特性,PE 文件

MSIL(Microsoft Intermediate Language)微 软 的 中 间 语 言 。 和 JAVA 的 虚 拟 机 类 似 , 是 与 CPU 无 关 的 指 令 集 。 当 编 译 为 托 管 代 码 时 , 编 译 器 将 源 代 码 翻 译



为 MSIL,



如 上 图 所 示 。 MSIL 包 括 用 于 加 载 、 存 储 和 初 始 化 对 象 以 及 对 对 象 调 用 方 法 的 指 令 , 还 包 括 用 于 算 术 和 逻 辑 运 算 、 控 制 流 、 直 接 内 存 访 问 、 异 常 处 理 和 其 他 操



作 的 指 令 。 在 可 以 执 行 代 码 前 , 必 须 将 MSIL 转 换 为 CPU 特 定 的 代 码 , 这 通 常 是 通 过 实 时 (JIT) 编 译 器 完 成 的 。 由 于 公 共 语 言 运 行 库 为 它 支 持 的 每 种 计 算



机 结 构 都 提 供 了 一 种 或 多 种 JIT 编 译 器 , 因 此 可 以 在 任 何 受 支 持 的 结 构 上 对 同 一 组 MSIL 进 行 JIT 编 译 和 执 行 。 这 样 总 结 上 面 的 就 是 : 中 间 语 言 是 一 组 独



立 于 CPU 的 指 令 集 , 它 可 以 被 即 时 编 译 器 Jitter 翻 译 成 目 标 平 台 的 本 地 代 码 。



PE



Windows PE 和 一 个 .NET PE 的 主 要 区 别 在 于 Windows PE 是 由 操 作 系 统 执 行 的 , 而 .NET PE 却 被 转 变 成 为 .NET Framework 的 CLR. 识 别 一 个 PE 是 .NET 还 是



Windows 取 决 于 他 的 通 用 的 目 标 文 件 格 式 (COFF) 是 否 使 用 Windows 的 操 作 系 统 . 目 标 文 件 格 式 (COFF) 指 定 了 任 何 文 件 都 分 成 两 个 部 分 :文 件 数 据 本 身 以 及



描述文件内包含的数据内容的头文件串。



MSIL 汇 编 程 序 从 MSIL 汇 编 语 言 生 成 可 移 植 可 执 行 的 (PE) 文 件 。可 以 运 行 结 果 可 执 行 文 件( 该 文 件 包 含 MSIL 和 所 需 的 元 数 据 )以 确 定 MSIL 是 否 按 预 期



执 行 。 这 就 是 我 为 什 么 会 谈 到 PE。









那 么 PE 文 件 是 怎 么 执 行 的 呢 ? 下 面 是 一 个 典 型 的 .NET 应 用 程 序 的 执 行 过 程 :



用 户 执 行 编 译 器 输 出 的 应 用 程 序 (PE 文 件 ), 操 作 系 统 载 入 PE 文 件 , 以 及 其 他 的 DLL(.NET 动 态 连 接 库 )。



操 作 系 统 装 载 器 根 据 PE 文 件 中 的 可 执 行 文 件 头 跳 转 到 程 序 的 入 口 点 。 显 然 , 操 作 系 统 并 不 能 执 行 中 间 语 言 , 该 入 口 点 也 被 设 计 为 跳 转 到 mscoree.dll( .NET



平 台 的 核 心 支 持 DLL) 的 _ CorExeMain()函 数 入 口 。



CorExeMain()函 数 开 始 执 行 PE 文 件 中 的 中 间 语 言 代 码 。这 里 的 执 行 的 意 思 是 CRL( 通 用 语 言 运 行 时 )按 照 调 用 的 对 象 方 法 为 单 位 ,用 JIT(即 时 编 译 器 )将 中 间



语言编译成本地机二进制代码,执行并根据需要存于机器缓存。



程 序 的 执 行 过 程 中 , GC(垃 圾 收 集 器 )负 责 内 存 的 分 配 , 释 放 等 管 理 功 能 。



程序执行完毕,操作系统卸载应用程序。







CLR 的 特 性 :



1. 跨 语 言 应 用



当 编 程 人 员 在 用 自 己 喜 欢 的 编 程 语 言 写 源 代 码 的 时 候 , 这 个 源 代 码 在 被 转 化 成 媒 介 语 言 ( IL) 之 前 , 先 被 编 译 成 了 一 个 独 立 的 可 执 行 单 元 ( PE) 。 这 样 无 论



你 是 一 个 VB。 NET 程 序 员 , 或 一 个 C#程 序 员 , 甚 至 是 使 用 托 管 的 C++的 程 序 员 。 只 要 被 编 译 成 IL 就 是 同 等 的 。



首 先 ,编 译 输 出 的 exe 是 一 个 由 中 间 语 言( IL),元 数 据 (Metadata)和 一 个 额 外 的 被 编 译 器 添 加 的 目 标 平 台 的 标 准 可 执 行 文 件 头( 比 如 Win32 平 台 就 是 加 了 一



个 标 准 Win32 可 执 行 文 件 头 ) 组 成 的 PE( portable executable, 可 移 植 执 行 体 ) 文 件 , 而 不 是 传 统 的 二 进 制 可 执 行 文 件 --虽 然 他 们 有 着 相 同 的 扩 展 名 。 中 间



它 中

语 言 是 一 组 独 立 于 CPU 的 指 令 集 , 可 以 被 即 时 编 译 器 Jitter 翻 译 成 目 标 平 台 的 本 地 代 码 。 间 语 言 代 码 使 得 所 有 Microsoft.NET 平 台 的 高 级 语 言 C#,VB.NET,



VC.NET 等 得 以 平 台 独 立 , 以 及 语 言 之 间 实 现 互 操 作 。 元 数 据 是 一 个 内 嵌 于 PE 文 件 的 表 的 集 合 。







2. 安 全 性



.NET 提 供 了 一 组 安 全 方 案 。 负 责 进 行 代 码 的 访 问 安 全 性 检 查 。 允 许 我 们 对 保 护 资 源 和 操 作 的 访 问 。 代 码 需 要 经 过 身 份 确 认 和 出 处 鉴 别 后 才 能 得 到 不 同 程 度 的



信 任 。安 全 策 略 是 一 组 可 配 置 的 规 则 ,公 共 语 言 运 行 库 在 决 定 允 许 代 码 执 行 的 操 作 时 遵 循 此 规 则 。安 全 策 略 由 管 理 员 设 置 ,并 由 运 行 库 强 制 。运 行 库 确 保 代 码



只能访问安全策略允许的资源和调用安全策略允许的代码。



每 当 发 生 加 载 程 序 集 的 尝 试 时 ,运 行 库 就 使 用 安 全 策 略 确 定 授 予 程 序 集 的 权 限 。在 检 查 了 描 述 程 序 集 标 识 的 信 息( 称 为 证 据 )后 ,运 行 库 使 用 安 全 策 略 决 定 代



码的信任程度和由此授予程序集的权限。证据包括但不仅限于代码的出版商、它的站点以及它的区域。安全策略还确定授予应用程序域的权限。



3. 版 本 化 和 程 序



由 于 使 用 了 元 数 据 , 所 以 你 可 以 使 用 XCOPY 简 单 的 复 制







77. 通过超链接怎样传递中文参数?

Server.UrlEncode();



Server.UrlDecode();

74





78. 请编程遍历页面上所有 TextBox 控件并给它赋值为 string.Empty?



79. 请编程实现一个冒泡排序算法?





80. 一个长度为 10000 的字符串,通过随机从 a-z 中抽取 10000 个字符组成.请用 c#语言编写主

要程序来实现.



81. 对于这样的一个枚举类型:

enum Color:byte{

Red,

Green,

Blue,

Orange

}

string[] ss=Enum.GetNames(typeof(Color));

byte[] bb=Enum.GetValues(typeof(Color));

试写一段程序显示出枚举类型中定义的所有符号名称以及它们对应的数值.



82. 您了解设计模式么?请列出您所知道的设计模式的名称.



83. 什么是 Application Pool?

应 用 程 序 池 是 将 一 个 或 多 个 应 用 程 序 链 接 到 一 个 或 多 个 工 作 进 程 集 合 的 配 置 .因 为 应 用 程 序 池 中 的 应 用 程 序 与 其 他 应 用 程 序 被 工 作 进 程 边 界 分 隔 .所 以 某 个 应



用 程 序 池 中 的 应 用 程 序 不 会 受 到 其 他 应 用 程 序 池 中 应 用 程 序 所 产 生 的 问 题 的 影 响 .例 如 不 同 的 权 限 设 置 。







84. SQL Server 的两种索引是何形式?索引的作用?索引的优缺点?

聚 集 索 引 : 表 中 存 储 的 数 据 按 照 索 引 的 顺 序 存 储 , 检 索 效 率 比 普 通 索 引 高 , 但 对 数 据 新 增 /修 改 /删 除 的 影 响 比 较 大 。



非 聚 集 索 引 : 不 影 响 表 中 的 数 据 存 储 顺 序 , 检 索 效 率 比 聚 集 索 引 低 , 对 数 据 新 增 /修 改 /删 除 的 影 响 很 小 。



一张表只有一个聚唨簇索引,可有多个非聚簇索引







85. 微软推出了一系列的 Application Block,请举出您所知道的 Application Block 并说明其作

用?

缓存应用程序块。开发人员可以使用这个应用程序块向应用程序中加入本地缓存。



密码应用程序块。 开发人员可以使用这个应用程序块向应用程序中加入哈希和对称加密。



数据访问应用程序块。 开发人员可以使用这个应用程序块向应用程序中加入标准数据库功能 。



异常处理应用程序块。 开发人员和决策者可以使用这个应用程序块创建一个处理发生穿过企业应用架构层异常的固定策略。



日志应用程序块。 开发人员可以使用这个应用程序块在应用程序中包含标准的日志功能。



安全应用程序块。 开发人员可以使用这个应用程序块向应用程序中加入授权和安全缓存功能。



验证应用程序块。 开发人员可以用这个应用程序块为业务对象创建验证规则,这些规则跨越应用程序不同层。



策 略 注 入 应 用 程 序 块 。开 发 人 员 可 以 使 用 这 个 应 用 程 序 块 实 现 拦 截 策 略 ,这 用 于 使 普 通 特 征 的 实 现 更 简 单 而 有 效 ,例 如 跨 越 一 个 应 用 的 日 志 、缓 存 、异 常



处理和验证。



Enterprise Library 还 包 括 一 个 核 心 功 能 集 , 包 括 配 置 、 度 量 、 对 象 构 建 服 务 。 这 些 功 能 被 所 有 应 用 程 序 块 所 使 用 。







86. 请列举一些您用到过的设计模式以及在什么情况下使用该模式?



87. Please describe the different between XML,XSD,XSL through their definition.

xml, xsl, xslt, xsd, uddi, wsdl, soap 辨 析



xml 所 用 的 标 签 可 以 为 用 户 自 定 义 标 签 , 主 要 存 储 数 据 信 息 和 内 容



以 下 都 应 用 在 xml 中 :



xsl 是 可 扩 展 样 式 语 言 , 是 对 xml 的 内 容 进 行 逻 辑 控 制 , 其 版 本 标 准 一 般 是 在 1999 年 之 前 定 义 的







xslt 为 可 扩 展 样 式 表 语 言 转 换 ,是 xsl 的 一 部 分 , xsl 的 功 能 主 要 有 两 个 ,一 个 是 xml 标 签 转 换 ,主 要 体 现 在 从 一 种 xml 形 式 转 换 到 另 一 种 xml 形 式 ;还 有 一

75



个 就 是 对 xml 进 行 格 式 化 , 即 用 样 式 进 行 润 色 , 渲 染 ; xslt 的 作 用 主 要 就 是 前 一 种 , 故 现 在 提 到 xsl 时 就 狭 义 上 指 xslt







xsd 是 xml 的 一 种 模 式 , 主 要 用 来 规 范 注 册 该 xsd 引 用 的 xml 文 档 信 息 , 比 如 数 据 类 型 的 字 节 大 小 等 ;







命名空间(引用):







对命名空间进行命名





当您在编程语言 例如, 中 有

C++) 定 义 命 名 空 间 时 , 一 些 对 可 用 在 该 名 称 中 的 字 符 的 限 制 。XML 命 名 空 间 标 识 符 还 必 须 符 合 特 定 的 语 法 — 统 一 资 源 标 识 符 (URI)



引 用 的 语 法 。 这 表 示 XML 命 名 空 间 标 识 符 必 须 遵 守 由 RFC 2396 定 义 的 URI 的 常 用 语 法 。







URI 被 定 义 为 用 来 标 识 抽 象 或 物 理 资 源 的 紧 凑 字 符 串 。 在 大 多 数 情 况 下 , URI 引 用 用 来 标 识 物 理 资 源 ( 网 页 、 要 下 载 的 文 件 等 ) , 但 是 , 对 于 XML 命 名 空 间



来 说 , URI 引 用 用 于 标 识 抽 象 资 源 ( 特 别 是 命 名 空 间 ) 。







按 照 URI 规 范 ,有 两 种 常 规 类 型 的 URI:统 一 资 源 定 位 器 (URL) 和 统 一 资 源 名 称 (URN)。这 两 种 类 型 的 URI 都 可 以 用 作 命 名 空 间 标 识 符 。下 面 是 一 个 可 用



作 命 名 空 间 标 识 符 的 两 个 URL 的 示 例 :







http://www.develop.com/student http://www.ed.g ov/elementary/students 下 面 是 几 个 也 可 用 作 命 名 空 间 标 识 符 的 URN 的 示 例 :



urn:www-develop-com:student urn:www.ed.gov:elementary.students urn:uuid:E7F73B13 -05FE-44ec-81CE-F898C4A6CDB4 命 名 空 间 标 识 符 最 重 要 的 属 性 是



它 的 唯 一 性 。 作 者 可 以 通 过 向 Internet 命 名 机 构 注 册 域 名 来 保 证 URL 的 唯 一 性 , 然 后 要 负 责 确 保 域 名 后 面 使 用 的 所 有 字 符 串 都 保 持 唯 一 。







URN 以 同 样 的 方 式 工 作 。 下 面 是 基 本 的 URN 语 法 :







urn:: 为 了 保 证 URN 的 一 致 性 , 作 者 必 须 再 次 向 Internet 命 名 机 构 注 册 他 们 的 命 名 空 间 标 识 符 。 然



后,作者负责按照某个方案来生成特定于命名空间的唯一字符串。









定 义 XML 命 名 空 间 的 组 织 应 当 为 新 命 名 空 间 名 称 的 创 建 制 定 一 个 一 致 的 方 案 。 如 , 这

W3C 经 常 定 义 新 的 XML 命 名 空 间 。 些 组 织 使 用 一 个 相 当 直 观 的 试 探 法 ,



该 试 探 法 使 用 当 前 年 份 以 及 工 作 组 的 名 称 。 图 2 阐 释 了 由 W3C 使 用 的 模 式 。



根 据 定 义 , URI 是 唯 一 的 , 因 此 完 全 不 必 在 XML 命 名 空 间 标 识 符 的 上 面 放 置 其 他 命 名 空 间 。 只 要 命 名 空 间 作 者 保 证 命 名 空 间 标 识 符 的 唯 一 性 , 总 是 可 以 只 用



单 个 命 名 空 间 限 定 符 来 唯 一 地 标 识 XML 中 的 内 容 。 这 大 大 简 化 了 这 一 在 XML 中 处 理 命 名 空 间 的 工 作 。







XML 处 理 器 将 命 名 空 间 标 识 符 视 为 不 透 明 的 字 符 串 ,而 从 不 将 它 们 视 为 可 解 析 的 资 源 。重 申 一 遍 :命 名 空 间 标 识 符 仅 仅 是 字 符 串 !当 两 个 命 名 空 间 标 识 符 中 的



各个字符都完全相同时,它们就被视为相同。







最 后 , 它 确 实 与 选 择 使 用 哪 个 类 型 的 URI 引 用 无 关 。 许 多 开 发 人 员 因 URL 更 易 于 读 取 和 记 忆 而 喜 欢 使 用 它 们 , 而 其 他 开 发 人 员 因 URN 的 灵 活 性 而 喜 欢 使 用



它们。无论选择哪种类型,您都要确保知道如何保证唯一性。







定义命名空间



“ Namespaces in XML Recommendation” 没 有 提 供 用 来 定 义 XML 命 名 空 间 中 有 何 内 容 的 语 法 。 在 许 多 情 况 下 , 这 种 类 型 的 语 法 定 义 甚 至 不 是 必 需 的 。 目 前 ,



大 多 数 XML 命 名 空 间 都 是 在 正 式 规 范 文 档 中 定 义 的 ,这 些 文 档 描 述 元 素 的 名 称 ,以 及 属 性 及 其 语 义 。这 恰 好 说 明 了 如 何 正 式 定 义 所 有 的 W3C 命 名 空 间( 请 参



阅 http://www.w3.org/TR/xslt 上 的 XSLT 1.0 规 范 , 以 查 看 相 关 示 例 ) 。







在 定 义 了 某 个 命 名 空 间 之 后 , 软 件 开 发 人 员 按 照 规 范 中 所 概 述 的 那 样 实 现 该 命 名 空 间 。 例 如 , MSXML 3.0、 Xalan 和 Saxon 都 是 对 XSLT 1.0 规 范 的 实 现 。 对



这 些 实 现 进 行 硬 编 码 ,以 便 查 找 那 些 属 于 XSLT 1.0 命 名 空 间 (http://www.w3.org/1999/XSL/Transform) 的 元 素 。要 使 用 这 些 实 现 ,您 需 要 向 它 们 提 供 一 个



正 确 使 用 XSLT 1.0 命 名 空 间 中 名 称 的 XML 文 档 ( 下 一 节 将 介 绍 这 方 面 的 详 细 信 息 ) 。 如 果 要 更 改 XSLT 1.0 命 名 空 间 中 的 任 何 内 容 , 支 持 软 件 将 必 须 进 行



更新。







XML 架 构 工 作 组 (http://www.w3.org/XML/Schema) 已 经 合 并 了 一 个 新 规 范( XML 架 构 ),该 规 范 为 在 命 名 空 间 中 定 义 元 素 、属 性 和 类 型 定 义 了 一 个 基 于 XML



的 语 法 。 XML 架 构 最 终 使 得 提 供 命 名 空 间 的 语 法 定 义 成 为 可 能 , 如 下 所 示 。

76











本 例 定 义 的 http://www.develop.com/student 命 名 空 间 包 含 五 个 命 名 元 素 : student、 id、



name、 language 和 rating。 此 架 构 不 仅 提 供 命 名 空 间 , 而 且 还 提 供 其 他 元 数 据 , 例 如 , student 子 元 素 的 顺 序 以 及 它 们 的 类 型 。







在 有 了 语 法 命 名 空 间 定 义 ( 例 如 , 那 些 由 XML 架 构 提 供 的 定 义 ) 时 , 就 可 以 构 建 更 高 级 的 软 件 , 以 便 在 运 行 时 利 用 名 称 和 类 型 信 息 。 XML 架 构 仍 然 未 定 义 所



定 义 元 素 和 属 性 的 语 义 , 因 此 仍 将 需 要 一 个 随 附 的 规 范 。 将 来 , 大 多 数 XML 命 名 空 间 都 将 通 过 规 范 和 架 构 定 义 同 时 定 义 。







使用命名空间



我 将 命 名 空 间 的 使 用 过 程 定 义 为 : 使 用 XML 文 档 中 给 定 命 名 空 间 中 的 一 个 或 多 个 元 素 或 属 性 。 这 要 求 您 了 解 如 下 由 “ Namespaces in XML Recommendation ”



概述的语法:用命名空间标识符限定元素名称和属性名称。







元 素 和 属 性 的 名 称 实 际 上 都 由 两 部 分 组 成 : 一 个 命 名 空 间 名 称 和 一 个 本 地 名 称 。 这 样 的 两 部 分 名 称 就 是 所 谓 的 限 定 名 或 QName。







在 XML 文 档 中 ,我 们 使 用 命 名 空 间 前 缀 来 限 定 元 素 和 属 性 的 本 地 名 称 。前 缀 实 际 上 只 是 命 名 空 间 标 识 符 (URI) 的 缩 写 ,URI 通 常 相 当 长 。前 缀 首 先 通 过 命 名



空间声明映射到命名空间标识符。命名空间声明的语法是:







xmlns:='' 命 名 空 间 声 明 看 起 来 就 像( 元 素 的 )属 性 ,但 是 从 文 档 的 逻 辑 结 构 来 看 ,它 们 不 被 正 式 视 为 属 性( 即 ,在 使 用 DOM



时,它们将不出现在元素的属性集中)。







命 名 空 间 前 缀 被 视 为 在 声 明 元 素 以 及 它 的 任 何 子 代 元 素 的 作 用 域 内 。 声 明 的 前 缀 可 用 在 任 何 元 素 或 属 性 名 称 的 前 面 ( 用 冒 号 分 隔 , 例 如 , s:student) 。 这 个



包 括 前 缀 的 完 整 名 称 是 限 定 名 (QName) 的 词 法 形 式 :







QName = : 前 缀 通 过 映 射 到 当 前 位 于 作 用 域 中 的 前 缀 的 命 名 空 间 标 识 符 与 元 素 或 属 性 关 联 。







让 我 们 假 设 某 个 开 发 人 员 希 望 使 用 XSLT 1.0 命 名 空 间 。 他 将 需 要 提 供 一 个 命 名 空 间 声 明 , 以 便 将 任 意 前 缀 映 射 到 正 式 的 XSLT 1.0 命 名 空 间 标 识 符



然 只 如

(http://www.w3.org/1999/XSL/Transform) 。 后 , 需 在 该 开 发 人 员 希 望 从 XSLT 1.0 命 名 空 间 中 使 用 的 每 个 元 素 或 属 性 前 面 加 上 相 应 的 前 缀 , 下 例 所 示 :











上 例 显 示 了 在 命 名 空 间 中 引 用 元 素 的 语 法 。前 缀 为 “ x” 的 每 个 元 素 都 来 自 http://www.w3.org/1999/XSL/Transform 命 名



而 (

空间, 没有前缀的任何元素 例如,hello_world) 不 来 自 命 名 空 间 。 理 器 现 在 可 以 区 分 XSLT 1.0 编 程 构 造 和 将 要 输 出 的 文 本 元 素 例 如 ,

都 处 ( hello_world)。



如 果 XSLT 1.0 命 名 空 间 中 有 一 个 字 符 拼 错 了 , 则 XSLT 1.0 处 理 器 将 无 法 将 该 文 档 识 别 为 它 能 够 理 解 的 词 汇 。







在 本 质 上 ,每 个 元 素 现 在 都 有 一 个 由 两 部 分 组 成 的 名 称 :一 个 命 名 空 间 标 识 符 和 一 个 本 地 名 称 。这 两 个 名 称 组 合 在 一 起 通 常 称 作 命 名 空 间 名 称( 注 意 :这 不 同



于 QName, QName 由 前 缀 和 本 地 名 称 组 合 而 成 ) 。







下 面 的 XML 文 档 是 另 一 个 示 例 , 它 显 示 了 如 何 从 本 专 栏 前 面 显 示 的 XML 架 构 定 义 使 用 元 素 :







3235329 Jeff Smith C#



9.5 请 注 意 , 无 论 命 名 空 间 是 如 何 定 义 的 , 引 用 它 们 的 语 法 都 是 相 同 的 。







当文档使用多个命名空间中的元素或属性时,通常针对给定的元素进行多个命名空间声明,如下例所示:







3235329 Jeff Smith C#



9.5 在 这 里 ,student 和 rating 来 自 同 一 个 命 名 空 间 ,而 id 和 language 分 别 来 自 不 同 的 命 名 空 间 ,但 是 name 不



属于命名空间。

77



命名空间前缀还可以通过在嵌套作用域中重新声明来进行重写,如下所示:







3235329 Jeff Smith



C# 35 在 本 例 中 , 除 了 name 元 素 , 所 有 的 内 容 都 来 自 同 一 个 命 名 空 间 —



urn:names-r-us 命 名 空 间 。 尽 管 可 以 重 新 声 明 命 名 空 间 前 缀 , 但 是 却 无 法 取 消 对 命 名 空 间 前 缀 的 声 明 。 例 如 , 下 面 的 声 明 是 非 法 的 :







3235329 ? ·?·?· 对 于 大 多 数 软 件 开 发 人 员



来 说 , 面 向 前 缀 的 语 法 ( 用 来 在 XML 命 名 空 间 中 引 用 内 容 ) 是 相 当 直 观 的 。 如 果 “ Namespaces in XML Recommendation ” 只 包 含 这 些 内 容 , 则 命 名 空 间 所



引起的混淆会小得多。







默认命名空间







可以使用另一种类型的命名空间声明来将命名空间标识符与元素名称相关联。这就是所谓的默认命名空间声明,它使用下面的语法:







xmlns='' 请 注 意 , 这 里 没 有 前 缀 。 在 针 对 某 个 元 素 使 用 默 认 命 名 空 间 声 明 时 , 其 作 用 域 中 的 所 有 非 限 定 元 素 名 称 都 自 动 与 指 定 的 命



名空间标识符相关联。然而,默认命名空间声明决不会影响属性。如果要将属性与命名空间标识符相关联,唯一的方法就是使用前缀。







请考虑下例:







Jeff Smith C# 35 在 这 里 ,“ student” 来 自 http://www.develop.com/student 命 名 空 间 ,而 “ name” 和



“ rating” 来 自 默 认 命 名 空 间 urn:foo。 id 属 性 不 属 于 命 名 空 间 , 这 是 由 于 属 性 不 会 自 动 与 默 认 的 命 名 空 间 标 识 符 相 关 联 。







本 例 还 阐 释 了 您 可 以 取 消 对 默 认 命 名 空 间 的 声 明( 只 需 将 默 认 的 命 名 空 间 标 识 符 重 新 设 置 为 空 字 符 串 ),如 language 元 素 所 示( 请 记 住 ,对 于 前 缀 声 明 不 能



这 样 做 ) 。 因 此 , language 元 素 也 不 属 于 命 名 空 间 。







默认命名空间的语法旨在提高方便程度,但是它们会导致更多的混淆,而不是带来更大的价值。这种混淆通常源自如下 事 实 : 元 素和 属 性 以 不 同的 方 式 处理 ,



而 且 嵌 套 元 素 被 指 定 为 默 认 的 命 名 空 间 标 识 符 也 不 是 立 即 可 见 的 。然 而 ,除 了 属 性 起 作 用 时 ,在 前 缀 和 默 认 命 名 空 间 之 间 进 行 选 择 最 终 将 主 要 是 样 式 方 面 的 问



题。







webservice:







uddi(Universal Description, Discovery, and Integration) , wsdl(Web Services Description Language), soap( Simple Object Access Protocol ) 都



是 webservice 中 用 到 的 技 术 , uddi 指 一 种 基 于 soap 通 信 的 注 册 机 构 , 里 面 存 储 着 每 个 web 服 务 的 wsdl 信 息 , 当 调 用 web 服 务 时 就 通 过 查 询 uddi 获 取 web



服 务 的 wsdl 信 息 ; wsdl 存 储 着 web 服 务 的 一 系 列 参 数 信 息 , 比 如 所 用 语 言 , 编 码 方 式 等 ; soap 主 要 用 来 通 信 用 。









88. 请用代码简单描述一下 Singleton、抽象工厂、Bridge、Composite(任选三个)的设计模

式的概念



89. 描述数据适配器中的多表查询?



90. SQL SERVER 中的表存到多少条记录时,速度明显变慢?



91. 总结 C# 集合类 Array Arraylist List Hashtable Dictionary Stack Queue



我 们 用 的 比 较 多 的 非 泛 型 集 合 类 主 要 有 ArrayList 类 和 HashTable 类 。我 们 经 常 用 HashTable 来 存 储 将 要 写 入 到 数 据 库 或 者 返 回 的 信 息 ,在 这 之 间 要 不 断



的 进 行 类 型 的 转 化 ,增 加 了 系 统 装 箱 和 拆 箱 的 负 担 ,如 果 我 们 操 纵 的 数 据 类 型 相 对 确 定 的 化 用 Dictionary 集 合 类 来 存 储 数 据 就 方 便 多 了 ,例



如 我 们 需 要 在 电 子 商 务 网 站 中 存 储 用 户 的 购 物 车 信 息 (商 品 名 , 对 应 的 商 品 个 数 )时 , 完 全 可 以 用 Dictionary 来 存 储 购 物 车 信 息 , 而 不 需 要 任

78



何的类型转化。







1.数 组 是 固 定 大 小 的 , 不 能 伸 缩 。 虽 然 System.Array.Resize 这 个 泛 型 方 法 可 以 重 置 数 组 大 小 ,



但是该方法是重新创建新设置大小的数组,用的是旧数组的元素初始化。随后以前的数组就废弃!而集合却是可变长的



2.数 组 要 声 明 元 素 的 类 型 , 集 合 类 的 元 素 类 型 却 是 object.



3.数 组 可 读 可 写 不 能 声 明 只 读 数 组 。 集 合 类 可 以 提 供 ReadOnly 方 法 以 只 读 方 式 使 用 集 合 。



4.数 组 要 有 整 数 下 标 才 能 访 问 特 定 的 元 素 , 然 而 很 多 时 候 这 样 的 下 标 并 不 是 很 有 用 。 集 合 也 是 数 据 列 表 却 不 使 用 下 标 访 问 。



很多时候集合有定制的下标类型,对于队列和栈根本就不支持下标访问!







1. 数组



int[] intArray1;



//初 始 化 已 声 明 的 一 维 数 组



intArray1 = new int[3];



intArray1 = new int[3]{1,2,3};



intArray1 = new int[]{1,2,3};







2. ArrayList 类 对 象 被 设 计 成 为 一 个 动 态 数 组 类 型 , 其 容 量 会 随 着 需 要 而 适 当 的 扩 充



方法



Add()向 数 组 中 添 加 一 个 元 素 ,



Remove()删 除 数 组 中 的 一 个 元 素



RemoveAt(int i)删 除 数 组 中 索 引 值 为 i 的 元 素



Reverse()反 转 数 组 的 元 素



Sort()以 从 小 到 大 的 顺 序 排 列 数 组 的 元 素



Clone()复 制 一 个 数 组







3. List



可 通 过 索 引 访 问 的 对 象 的 强 类 型 列 表 。 提 供 用 于 对 列 表 进 行 搜 索 、 排 序 和 操 作 的 方 法 ,在 决 定 使 用 List 还 是 使 用 ArrayList 类 ( 两 者 具 有 类 似 的 功 能 ) 时 ,



如 则 但 如

记 住 List 类 在 大 多 数 情 况 下 执 行 得 更 好 并 且 是 类 型 安 全 的 。 果 对 List 类 的 类 型 T 使 用 引 用 类 型 , 两 个 类 的 行 为 是 完 全 相 同 的 。 是 , 果 对 类 型 T 使



用值类型,则需要考虑实现和装箱问题。



如 果 对 类 型 T 使 用 值 类 型 , 则 编 译 器 将 特 别 针 对 该 值 类 型 生 成 List 类 的 实 现 。 这 意 味 着 不 必 对 List 对 象 的 列 表 元 素 进 行 装 箱 就 可 以 使 用 该 元 素 , 并 且 在



创 建 大 约 500 个 列 表 元 素 之 后 , 不 对 列 表 元 素 装 箱 所 节 省 的 内 存 将 大 于 生 成 该 类 实 现 所 使 用 的 内 存 。



4. Dictionary



表 示 键 和 值 的 集 合 。 Dictionary 遍 历 输 出 的 顺 序 , 就 是 加 入 的 顺 序 , 这 点 与 Hashtable 不 同



5. SortedList 类



与 哈 希 表 类 似 , 区 别 在 于 SortedList 中 的 Key 数 组 排 好 序 的







6.Hashtable 类



哈 希 表 , 名 -值 对 。 类 似 于 字 典 (比 数 组 更 强 大 )。 哈 希 表 是 经 过 优 化 的 , 访 问 下 标 的 对 象 先 散 列 过 。 如 果 以 任 意 类 型 键 值 访 问 其 中 元 素 会 快 于 其 他 集 合 。



GetHashCode()方 法 返 回 一 个 int 型 数 据 , 使 用 这 个 键 的 值 生 成 该 int 型 数 据 。 哈 希 表 获 取 这 个 值 最 后 返 回 一 个 索 引 , 表 示 带 有 给 定 散 列 的 数 据 项 在 字 典 中 存



储的位置。



Hashtable 和 Dictionary 类 型



1: 单 线 程 程 序 中 推 荐 使 用 Dictionary, 有 泛 型 优 势 , 且 读 取 速 度 较 快 , 容 量 利 用 更 充 分 .



2:多 线 程 程 序 中 推 荐 使 用 Hashtable, 默 认 的 Hashtable 允 许 单 线 程 写 入 , 多 线 程 读 取 , 对 Hashtable 进 一 步 调 用 Synchronized() 方 法 可 以 获 得 完 全



线 程 安 全 的 类 型 . 而 Dictionary 非 线 程 安 全 , 必 须 人 为 使 用 lock 语 句 进 行 保 护 , 效 率 大 减 .



3:Dictionary 有 按 插 入 顺 序 排 列 数 据 的 特 性 (注 : 但 当 调 用 Remove() 删 除 过 节 点 后 顺 序 被 打 乱 ), 因 此 在 需 要 体 现 顺 序 的 情 境 中 使 用 Dictionary 能 获



得一定方便.







HashTable 中 的 key/value 均 为 object 类 型 , 由 包 含 集 合 元 素 的 存 储 桶 组 成 。 存 储 桶 是 HashTable 中 各 元 素 的 虚 拟 子 组 , 与 大 多 数 集 合 中 进 行 的 搜 索 和 检 索



相 比 , 存 储 桶 可 令 搜 索 和 检 索 更 为 便 捷 。 每 一 存 储 桶 都 与 一 个 哈 希 代 码 关 联 , 该 哈 希 代 码 是 使 用 哈 希 函 数 生 成 的 并 基 于 该 元 素 的 键 。 HashTable 的 优 点 就 在 于

79



其索引的方式,速度非常快。如果以任意类型键值访问其中元素会快于其他集合,特别是当数据量特别大的时候 ,效率差别尤其大。



HashTable 的 应 用 场 合 有 : 做 对 象 缓 存 , 树 递 归 算 法 的 替 代 , 和 各 种 需 提 升 效 率 的 场 合 。







Dictionary 和 HashTable 内 部 实 现 差 不 多 , 但 前 者 无 需 装 箱 拆 箱 操 作 , 效 率 略 高 一 点 。







HashTable 是 经 过 优 化 的 , 访 问 下 标 的 对 象 先 散 列 过 , 所 以 内 部 是 无 序 散 列 的 , 保 证 了 高 效 率 , 也 就 是 说 , 其 输 出 不 是 按 照 开 始 加 入 的 顺 序 , 而 Dictionary



遍 历 输 出 的 顺 序 , 就 是 加 入 的 顺 序 , 这 点 与 Hashtable 不 同 。 如 果 一 定 要 排 序 HashTable 输 出 , 只 能 自 己 实 现 :



//Hashtable sorting



System.Collections.ArrayList akeys = new System.Collections.ArrayList(ht.Keys); //from Hashtable



akeys.Sort(); //Sort by leading letter



foreach (string skey in akeys)



{



Console.Write(skey + ":");



Console.WriteLine(ht[skey]);



}







HashTable 与 线 程 安 全 :



为 了 保 证 在 多 线 程 的 情 况 下 的 线 程 同 步 访 问 安 全 , 微 软 提 供 了 自 动 线 程 同 步 的 HashTable:



如 果 HashTable 要 允 许 并 发 读 但 只 能 一 个 线 程 写 , 要 这 么 创 建 HashTable 实 例 :



//Thread safe HashTable



System.Collections.Hashtable htSyn = System.Collections.Hashtable.Synchronized( new System.Collections.Hashtable());



这 样 , 如 果 有 多 个 线 程 并 发 的 企 图 写 HashTable 里 面 的 item, 则 同 一 时 刻 只 能 有 一 个 线 程 写 , 其 余 阻 塞 ; 对 读 的 线 程 则 不 受 影 响 。







另 外 一 种 方 法 就 是 使 用 lock 语 句 , 但 要 lock 的 不 是 HashTable, 而 是 其 SyncRoot; 虽 然 不 推 荐 这 种 方 法 , 但 效 果 一 样 的 , 因 为 源 代 码 就 是 这 样 实 现 的 :







7. Stack 类



栈 , 后 进 先 出 。 push 方 法 入 栈 , pop 方 法 出 栈 。



System.Collections.Stack stack=new System.Collections.Stack();



stack.Push(1);



stack.Push(2);







8.Queue 类



队 列 , 先 进 先 出 。 enqueue 方 法 入 队 列 , dequeue 方 法 出 队 列 。



System.Collections.Queue queue=new System.Collections.Queue();







92. TREE 控件



93. 请解释 web.config 文件中的重要节点

Web.config 文 件 是 一 个 XML 文 本 文 件 , 它 用 来 储 存 ASP.NET Web 应 用 程 序 的 配 置 信 息 ( 如 最 常 用 的 设 置 ASP.NET Web 应 用 程 序 的 身 份 验 证 方 式 ) , 它 可 以



出 现 在 应 用 程 序 的 每 一 个 目 录 中 。 当 你 通 过 .NET 新 建 一 个 Web 应 用 程 序 后 , 默 认 情 况 下 会 在 根 目 录 自 动 创 建 一 个 默 认 的 Web.config 文 件 , 包 括 默 认 的 配 置 设



置 , 所 有 的 子 目 录 都 继 承 它 的 配 置 设 置 。 如 果 你 想 修 改 子 目 录 的 配 置 设 置 , 你 可 以 在 该 子 目 录 下 新 建 一 个 Web.config 文 件 。 它 可 以 提 供 除 从 父 目 录 继 承 的 配



置信息以外的配置信息,也可以重写或修改父目录中定义的设置。



(一 ).Web.Config 是 以 XML 文 件 规 范 存 储 ,配 置 文 件 分 为 以 下 格 式



1.配 置 节 处 理 程 序 声 明



特 点 : 位 于 配 置 文 件 的 顶 部 , 包 含 在 标 志 中 。



2.特 定 应 用 程 序 配 置



特 点 : 位 于 中 。 可 以 定 义 应 用 程 序 的 全 局 常 量 设 置 等 信 息 .



3.配 置 节 设 置



特 点 : 位 于 节 中 , 控 制 Asp.net 运 行 时 的 行 为 .



4.配 置 节 组

80



特 点 : 用 标 记 , 可 以 自 定 义 分 组 , 可 以 放 到 内 部 或 其 它 标 记 的 内 部 .



(二 ).配 置 节 的 每 一 节



1.节



根元素,其它节都是在它的内部.



2.节



此节用于定义应用程序设置项。对一些不确定设置,还可以让用户根据自己实际情况自己设置



用法:



I.















定义了一个连接字符串常量,并且在实际应用时可以修改连接字符串,不用修改程式代码 .



II.











定义了一个错误重定向页面.



3.节



格式:







I.default language: 定 义 后 台 代 码 语 言 ,可 以 选 择 C#和 VB.net 两 种 语 言 .



IIdebug : 为 true 时 , 启 动 aspx 调 试 ; 为 false 不 启 动 aspx 调 试 , 因 而 可 以 提 高 应 用 程 序 运 行



时 的 性 能 。 一 般 程 序 员 在 开 发 时 设 置 为 true,交 给 客 户 时 设 置 为 false.



4.节



格式:











/>



I.mode : 具 有 On,Off,RemoteOnly 3 种 状 态 。 On 表 示 始 终 显 示 自 定 义 的 信 息 ; Off 表 示 始 终 显 示 详 细 的 asp.net 错 误 信 息 ; RemoteOnly 表 示 只 对 不 在 本 地 Web



服务器上运行的用户显示自定义信息 .



II.defaultRedirect: 用 于 出 现 错 误 时 重 定 向 的 URL 地 址 . 是 可 选 的



III.statusCode: 指 明 错 误 状 态 码 , 表 明 一 种 特 定 的 出 错 状 态 .



IV. redirect:错 误 重 定 向 的 URL.



5.节



格式:







I.requestEncoding: 它 用 来 检 查 每 一 个 发 来 请 求 的 编 码 .



II.responseEncoding: 用 于 检 查 发 回 的 响 应 内 容 编 码 .



III.fileEncoding: 用 于 检 查 aspx,asax 等 文 件 解 析 的 默 认 编 码 .



6.节

81



格式:







I.mode: 分 为 off,Inproc,StateServer,SqlServer 几 种 状 态



这 里 有 详 细 介 绍 此 属 性 : http://blog.csdn.net/chengking/archive/2005/10/27/518079.aspx



II. stateConnectionString :指 定 Asp.net 应 用 程 序 存 储 远 程 会 话 状 态 的 服 务 器 名 , 默 认 为 本 机



III.sqlConnectionString: 当 用 会 话 状 态 数 据 库 时 , 在 这 里 设 置 连 接 字 符 串



IV. Cookieless: 设 置 为 true 时 , 表 示 不 使 用 cookie 会 话 状 态 来 标 识 客 户 ; 否 则 , 相 反 .



V. TimeOut: 用 来 定 义 会 话 状 态 存 储 的 时 间 , 超 过 期 限 , 将 自 动 终 止 会 话 .



7.节



格式:



























I.Windows: 使 用 IIS 验 证 方 式



II.Forms: 使 用 基 于 窗 体 的 验 证 方 式



III.Passport: 采 用 Passport cookie 验 证 模 式



IV.None: 不 采 用 任 何 验 证 方 式



里 面 内 嵌 Forms 节 点 的 属 性 涵 义 :



I.Name: 指 定 完 成 身 份 验 证 的 Http cookie 的 名 称 .



II.LoginUrl: 如 果 未 通 过 验 证 或 超 时 后 重 定 向 的 页 面 URL, 一 般 为 登 录 页 面 , 让 用 户 重 新 登 录



III.Protection: 指 定 cookie 数 据 的 保 护 方 式 .



可 设 置 为 : All None Encryption Validation 四 种 保 护 方 式



a. All 表 示 加 密 数 据 , 并 进 行 有 效 性 验 证 两 种 方 式



b. None 表 示 不 保 护 Cookie.



c. Encryption 表 示 对 Cookie 内 容 进 行 加 密



d. validation 表 示 对 Cookie 内 容 进 行 有 效 性 验 证



IV. TimeOut: 指 定 Cookie 的 失 效 时 间 . 超 时 后 要 重 新 登 录 .







在 运 行 时 对 Web.config 文 件 的 修 改 不 需 要 重 启 服 务 就 可 以 生 效 ( 注 : 节 例 外 ) 。 当 然 Web.config 文 件 是 可 以 扩 展 的 。 你 可 以 自 定 义 新 配 置



参数并编写配置节处理程序以对它们进行处理。







web.config 配 置 文 件 ( 默 认 的 配 置 设 置 ) 以 下 所 有 的 代 码 都 应 该 位 于























之 间 , 出 于 学 习 的 目 的 下 面 的 示 例 都 省 略 了 这 段 XML 标 记 。

82







1、 节



作 用 : 配 置 ASP.NET 身 份 验 证 支 持 ( 为 Windows、 Forms、 PassPort、None 四 种 ) 。 该 元 素 只 能 在 计 算 机 、 站 点 或 应 用 程 序 级 别 声 明 。 元



素 必 需 与 节 配 合 使 用 。



示例:



以 下 示 例 为 基 于 窗 体 ( Forms) 的 身 份 验 证 配 置 站 点 , 当 没 有 登 陆 的 用 户 访 问 需 要 身 份 验 证 的 网 页 , 网 页 自 动 跳 转 到 登 陆 网 页 。















其 中 元 素 loginUrl 表 示 登 陆 网 页 的 名 称 , name 表 示 Cookie 名 称 。



2、 节



控 ( 。 (

作 用 : 制 对 URL 资 源 的 客 户 端 访 问 如 允 许 匿 名 用 户 访 问 ) 此 元 素 可 以 在 任 何 级 别 计 算 机 、 点 、 用 程 序 、 目 录 或 页 ) 声 明 。 需 与

站 应 子 上 必



节配合使用。



示例:以下示例禁止匿名用户的访问















注 :你 可 以 使 用 user.identity.name 来 获 取 已 经 过 验 证 的 当 前 的 用 户 名 ;可 以 使 用 web.Security.FormsAuthentication.RedirectFromLoginPage 方 法 将 已 验



证 的 用 户 重 定 向 到 用 户 刚 才 请 求 的 页 面 .具 体 的



3、 节



作 用 : 配 置 ASP.NET 使 用 的 所 有 编 译 设 置 。 默 认 的 debug 属 性 为 “ True” .在 程 序 编 译 完 成 交 付 使 用 之 后 应 将 其 设 为 False( Web.config 文 件 中 有 详 细 说 明 ,



此处省略示例)



4、



作 用 : 为 ASP.NET 应 用 程 序 提 供 有 关 自 定 义 错 误 信 息 的 信 息 。 它 不 适 用 于 XML Web services 中 发 生 的 错 误 。



示例:当发生错误时,将网页跳转到自定义的错误页面。











其 中 元 素 defaultRedirect 表 示 自 定 义 的 错 误 网 页 的 名 称 。 mode 元 素 表 示 : 对 不 在 本 地 Web 服 务 器 上 运 行 的 用 户 显 示 自 定 义 (友 好 的 )信 息 。



5、 节



作 用 : 配 置 ASP.NET HTTP 运 行 库 设 置 。 该 节 可 以 在 计 算 机 、 站 点 、 应 用 程 序 和 子 目 录 级 别 声 明 。 示 例 : 控 制 用 户 上 传 文 件 最 大 为 4M, 最 长 时 间 为 60 秒 , 最



多 请 求 数 为 100







6、



作 用 : 标 识 特 定 于 页 的 配 置 设 置 ( 如 是 否 启 用 会 话 状 态 、 视 图 状 态 , 是 否 检 测 用 户 的 输 入 等 ) 。 可 以 在 计 算 机 、 站 点 、 应 用 程 序 和 子 目 录 级 别 声 明 。



示 例 : 不 检 测 用 户 在 浏 览 器 输 入 的 内 容 中 是 否 存 在 潜 在 的 危 险 数 据 ( 注 : 该 项 默 认 是 检 测 , 如 果 你 使 用 了 不 检 测 , 一 要 对 用 户 的 输 入 进 行 编 码 或 验 证 ), 在 从



客 户 端 回 发 页 时 将 检 查 加 密 的 视 图 状 态 , 以 验 证 视 图 状 态 是 否 已 在 客 户 端 被 篡 改 。 (注 : 该 项 默 认 是 不 验 证 )







7、



作用:为当前应用程序配置会话状态设置(如设置是否启用会话状态,会话状态保存位置)。









示例:











注:



mode="InProc"表 示 : 在 本 地 储 存 会 话 状 态 ( 你 也 可 以 选 择 储 存 在 远 程 服 务 器 或 SAL 服 务 器 中 或 不 启 用 会 话 状 态 )



cookieless="true"表 示 : 如 果 用 户 浏 览 器 不 支 持 Cookie 时 启 用 会 话 状 态 (默 认 为 False)



timeout="20"表 示 : 会 话 可 以 处 于 空 闲 状 态 的 分 钟 数

83



8、



作 用 : 配 置 ASP.NET 跟 踪 服 务 , 主 要 用 来 程 序 测 试 判 断 哪 里 出 错 。



示 例 : 以 下 为 Web.config 中 的 默 认 配 置 :







注:



enabled="false"表 示 不 启 用 跟 踪 ;



requestLimit="10"表 示 指 定 在 服 务 器 上 存 储 的 跟 踪 请 求 的 数 目



pageOutput="false"表 示 只 能 通 过 跟 踪 实 用 工 具 访 问 跟 踪 输 出 ;



traceMode="SortByTime"表 示 以 处 理 跟 踪 的 顺 序 来 显 示 跟 踪 信 息



localOnly="true" 表 示 跟 踪 查 看 器 (trace.axd) 只 用 于 宿 主 Web 服 务 器







自 定 义 Web.config 文 件 配 置



自 定 义 Web.config 文 件 配 置 节 过 程 分 为 两 步 。



1.在 在 配 置 文 件 顶 部 和 标 记 之 间 声 明 配 置 节 的 名 称 和 处 理 该 节 中 配 置 数 据 的 .NET Framework 类 的 名 称 。



2.是 在 区 域 之 后 为 声 明 的 节 做 实 际 的 配 置 设 置 。



示例:创建一个节存储数据库连接字符串



































......















访 问 Web.config 文 件 你 可 以 通 过 使 用 ConfigurationSettings.AppSettings 静 态 字 符 串 集 合 来 访 问 Web.config 文 件 示 例 : 获 取 上 面 例 子 中 建 立 的 连 接 字



符串。例如:



protected static string Isdebug = ConfigurationSett ings.AppSettings["debug"]







二 、 web.config 中 的 session 配 置 详 解



打 开 某 个 应 用 程 序 的 配 置 文 件 Web.config 后 , 我 们 会 发 现 以 下 这 段 :







这 一 段 就 是 配 置 应 用 程 序 是 如 何 存 储 Session 信 息 的 了 。我 们 以 下 的 各 种 操 作 主 要 是 针 对 这 一 段 配 置 展 开 。让 我 们 先 看 看 这 一 段 配 置 中 所 包 含 的 内 容 的 意



思 。 sessionState 节 点 的 语 法 是 这 样 的 :







必须有的属性是 属性 选项 描述



mode 设 置 将 Session 信 息 存 储 到 哪 里



Ø Off 设 置 为 不 使 用 Session 功 能 ,



Ø InProc 设 置 为 将 Session 存 储 在 进 程 内 , 就 是 ASP 中 的 存 储 方 式 , 这 是 默 认 值 ,



Ø StateServer 设 置 为 将 Session 存 储 在 独 立 的 状 态 服 务 中 ,



Ø SQLServer 设 置 将 Session 存 储 在 SQL Server 中 。







可选的属性是: 属性 选项 描述



Ø cookieless 设 置 客 户 端 的 Session 信 息 存 储 到 哪 里 ,



Ø ture 使 用 Cookieless 模 式 ,



Ø false 使 用 Cookie 模 式 , 这 是 默 认 值 ,



Ø timeout 设 置 经 过 多 少 分 钟 后 服 务 器 自 动 放 弃 Session 信 息 , 默 认 为 20 分 钟 。





stateConnectionString 设 置 将 Session 信 息 存 储 在 状 态 服 务 中 时 使 用 的 服 务 器 名 称 和 端 口 号 , 如 : 。

"tcpip=127.0.0.1:42424” 当 mode 的 值 是 StateServer



是,这个属性是必需的。



sqlConnectionString 设 置 与 SQL Server 连 接 时 的 连 接 字 符 串 。 例 如 "data source= localhost;Integrated Security=SSPI;Initial Catalog=northwind" 。



当 mode 的 值 是 SQLServer 时 , 这 个 属 性 是 必 需 的 。



stateNetworkTimeout 设 置 当 使 用 StateServer 模 式 存 储 Session 状 态 时 , 经 过 多 少 秒 空 闲 后 , 断 开 Web 服 务 器 与 存 储 状 态 信 息 的 服 务 器 的 TCP/IP 连 接 的 。



默 认 值 是 10 秒 钟 。







ASP.NET 中 客 户 端 Session 状 态 的 存 储



大 分 客

在 我 们 上 面 的 Session 模 型 简 介 中 , 家 可 以 发 现 Session 状 态 应 该 存 储 在 两 个 地 方 , 别 是 客 户 端 和 服 务 器 端 。 户 端 只 负 责 保 存 相 应 网 站 的 SessionID,



而 其 他 的 Session 信 息 则 保 存 在 服 务 器 端 。 在 ASP 中 , 客 户 端 的 SessionID 实 际 是 以 Cookie 的 形 式 存 储 的 。 如 果 用 户 在 浏 览 器 的 设 置 中 选 择 了 禁 用 Cookie,



那 末 他 也 就 无 法 享 受 Session 的 便 利 之 处 了 , 甚 至 造 成 不 能 访 问 某 些 网 站 。 为 了 解 决 以 上 问 题 , 在 ASP.NET 中 客 户 端 的 Session 信 息 存 储 方 式 分 为 : Cookie



和 Cookieless 两 种 。



ASP.NET 中 , 默 认 状 态 下 , 在 客 户 端 还 是 使 用 Cookie 存 储 Session 信 息 的 。 如 果 我 们 想 在 客 户 端 使 用 Cookieless 的 方 式 存 储 Session 信 息 的 方 法 如 下 :



找 到 当 前 Web 应 用 程 序 的 根 目 录 , 打 开 Web.Config 文 件 , 找 到 如 下 段 落 :







这 段 话 中 的 cookieless="false"改 为 : cookieless="true", 这 样 , 客 户 端 的 Session 信 息 就 不 再 使 用 Cookie 存 储 了 , 而 是 将 其 通 过 URL 存 储 。 关 闭 当



前 的 IE, 打 开 一 个 新 IE, 重 新 访 问 刚 才 的 Web 应 用 程 序 , 就 会 看 到 类 似 下 面 的 样 子 :



其 中 ,http://localhost/MyTestApplication/(ulqsek45heu3ic2a5zgdl245) /default.aspx 中 黑 体 标 出 的 就 是 客 户 端 的 Session ID。注 意 ,这 段 信 息 是 由 IIS



自动加上的,不会影响以前正常的连接。



ASP.NET 中 服 务 器 端 Session 状 态 的 存 储 准 备 工 作 :



为 了 您 能 更 好 的 体 验 到 实 验 现 象 , 您 可 以 建 立 一 个 叫 做 SessionState.aspx 的 页 面 , 然 后 把 以 下 这 些 代 码 添 加 到 中 。







Sub Session_Add(sender As Object, e As EventArgs)



Session("MySession") = text1.Value



span1.InnerHtml = "Session data updated! Your session contains: " & Session("MySession"). ToString() & " "



End Sub



Sub CheckSession(sender As Object, eAs EventArgs)



If (Session("MySession")Is Nothing) Then



span1.InnerHtml = "NOTHING, SESSION DATA LOST!"

85



Else



span1.InnerHtml = "Your session contains: " & Session("MySession").ToString() & ""



End If



End Sub



































这 个 SessionState.aspx 的 页 面 可 以 用 来 测 试 在 当 前 的 服 务 器 上 是 否 丢 失 了 Session 信 息 。









将 服 务 器 Session 信 息 存 储 在 进 程 中



让 我 们 来 回 到 Web.config 文 件 的 刚 才 那 段 段 落 中 :







当 mode 的 值 是 InProc 时 , 说 明 服 务 器 正 在 使 用 这 种 模 式 。



这 种 方 式 和 以 前 ASP 中 的 模 式 一 样 ,就 是 服 务 器 将 Session 信 息 存 储 在 IIS 进 程 中 。当 IIS 关 闭 、重 起 后 ,这 些 信 息 都 会 丢 失 。但 是 这 种 模 式 也 有 自 己 最



大 好 处 ,就 是 性 能 最 高 。应 为 所 有 的 Session 信 息 都 存 储 在 了 IIS 的 进 程 中 ,所 以 IIS 能 够 很 快 的 访 问 到 这 些 信 息 ,这 种 模 式 的 性 能 比 进 程 外 存 储 Session 信



息 或 是 在 SQL Server 中 存 储 Session 信 息 都 要 快 上 很 多 。 这 种 模 式 也 是 ASP.NET 的 默 认 方 式 。



好 了 , 现 在 让 我 们 做 个 试 验 。打 开 刚 才 的 SessionState.aspx 页 面 , 随 便 输 入 一 些 字 符 , 使 其 存 储 在 Session 中 。 然 后 , 让 我 们 让 IIS 重 起 。 注 意 , 并 不



是 使 当 前 的 站 点 停 止 再 开 始 ,而 是 在 IIS 中 本 机 的 机 器 名 的 节 点 上 点 击 鼠 标 右 键 ,选 择 重 新 启 动 IIS。(想 当 初 使 用 NT4 时 ,重 新 启 动 IIS 必 须 要 重 新 启 动 计 算



机 才 行 , 微 软 真 是 @#$%^&)返 回 到 SessionState.aspx 页 面 中 , 检 查 刚 才 的 Session 信 息 , 发 现 信 息 已 经 丢 失 了 。









将 服 务 器 Session 信 息 存 储 在 进 程 外



首 先 ,让 我 们 来 打 开 管 理 工 具 ->服 务 ,找 到 名 为 :ASP.NET State Service 的 服 务 ,启 动 它 。实 际 上 ,这 个 服 务 就 是 启 动 一 个 要 保 存 Session 信 息 的 进 程 。



启 动 这 个 服 务 后 , 你 可 以 从 Windows 任 务 管 理 器 ->进 程 中 看 到 一 个 名 为 aspnet_state.exe 的 进 程 , 这 个 就 是 我 们 保 存 Session 信 息 的 进 程 。



然 后 ,回 到 Web.config 文 件 中 上 述 的 段 落 中 ,将 mode 的 值 改 为 StateServer。保 存 文 件 后 的 重 新 打 开 一 个 IE,打 开 SessionState.aspx 页 面 ,保 存 一 些



信 息 到 Session 中 。 这 时 , 让 我 们 重 起 IIS, 再 回 到 SessionState.aspx 页 面 中 查 看 刚 才 的 Session 信 息 , 发 现 没 有 丢 失 。



实 际 上 , 这 种 将 Session 信 息 存 储 在 进 程 外 的 方 式 不 光 指 可 以 将 信 息 存 储 在 本 机 的 进 程 外 , 还 可 以 将 Session 信 息 存 储 在 其 他 的 服 务 器 的 进 程 中 。 这 时 ,



不 光 需 要 将 mode 的 值 改 为 StateServer, 还 需 要 在 stateConnectionString 中 配 置 相 应 的 参 数 。 例 如 你 的 计 算 你 是 192.168.0.1, 你 想 把 Session 存 储 在 IP



为 192.168.0.2 的 计 算 机 的 进 程 中 ,就 需 要 设 置 成 这 样 : stateConnectionString="tcpip=192.168.0.2:42424" 。当 然 ,不 要 忘 记 在 192.168.0.2 的 计 算 机 中



装 上 .NET Framework, 并 且 启 动 ASP.NET State Services 服 务 。

86



将 服 务 器 Session 信 息 存 储 在 SQL Server 中



首 先 , 还 是 让 我 们 来 做 一 些 准 备 工 作 。 启 动 SQL Server 和 SQL Server 代 理 服 务 。 在 SQL Server 中 执 行 一 个 叫 做 InstallSqlState.sql 的 脚 本 文 件 。 这



个 脚 本 文 件 将 在 SQL Server 中 创 建 一 个 用 来 专 门 存 储 Session 信 息 的 数 据 库 , 及 一 个 维 护 Session 信 息 数 据 库 的 SQL Server 代 理 作 业 。 我 们 可 以 在 以 下 路 径



中找到那个文件:



[system drive]\winnt\Microsoft.NET\Framework\[version]\



然 后 打 开 查 询 分 析 器 ,连 接 到 SQL Server 服 务 器 ,打 开 刚 才 的 那 个 文 件 并 且 执 行 。稍 等 片 刻 ,数 据 库 及 作 业 就 建 立 好 了 。这 时 ,你 可 以 打 开 企 业 管 理 器 ,



看 到 新 增 了 一 个 叫 ASPState 的 数 据 库 。 但 是 这 个 数 据 库 中 只 是 些 存 储 过 程 , 没 有 用 户 表 。 实 际 上 Session 信 息 是 存 储 在 了 tempdb 数 据 库 的



ASPStateTempSessions 表 中 的 ,另 外 一 个 ASPStateTempApplications 表 存 储 了 ASP 中 Application 对 象 信 息 。这 两 个 表 也 是 刚 才 的 那 个 脚 本 建 立 的 。另 外 查



看 管 理 ->SQL Server 代 理 ->作 业 , 发 现 也 多 了 一 个 叫 做 ASPState_Job_DeleteExpiredSessions 的 作 业 , 这 个 作 业 实 际 上 就 是 每 分 钟 去 ASPStateTempSessions



表 中 删 除 过 期 的 Session 信 息 的 。



接 着 , 我 们 返 回 到 Web.config 文 件 , 修 改 mode 的 值 改 为 SQLServer。 注 意 , 还 要 同 时 修 改 sqlConnectionString 的 值 , 格 式 为 :



sqlConnectionString="data source=localhos t; Integrated Security=SSPI;"



其 中 data source 是 指 SQL Server 服 务 器 的 IP 地 址 , 如 果 SQL Server 与 IIS 是 一 台 机 子 , 写 127.0.0.1 就 行 了 。 Integrated Security=SSPI 的 意 思 是



使 用 Windows 集 成 身 份 验 证 ,这 样 ,访 问 数 据 库 将 以 ASP.NET 的 身 份 进 行 ,通 过 如 此 配 置 ,能 够 获 得 比 使 用 userid=sa;password=口 令 的 SQL Server 验 证 方 式



更 好 的 安 全 性 。 当 然 , 如 果 SQL Server 运 行 于 另 一 台 计 算 机 上 , 你 可 能 会 需 要 通 过 Active Directory 域 的 方 式 来 维 护 两 边 验 证 的 一 致 性 。



让 向 这 即 刚

同 样 , 我 们 做 个 试 验 。 SessionState.aspx 中 添 加 Session 信 息 , 时 发 现 Session 信 息 已 经 存 在 SQL Server 中 了 , 使 你 重 起 计 算 机 , 才 的 Session



信 息 也 不 会 丢 失 。 现 在 , 你 已 经 完 全 看 见 了 Session 信 息 到 底 是 什 么 样 子 的 了 , 而 且 又 是 存 储 在 SQL Server 中 的 , 能 干 什 么 就 看 你 的 发 挥 了 。







总结



通 过 这 篇 文 章 , 你 可 以 看 到 在 Session 的 管 理 和 维 护 上 , ASP.NET 比 ASP 有 了 很 大 的 进 步 , 我 们 可 以 更 加 随 意 的 挑 选 适 合 的 方 法 了 。 对 于 企 业 级 的 应 用 来



说,这无疑对于服务器的同步、服务器的稳定性、可靠性都是有利的。相信在强大的微软支持下,新一代的电子商务平台将会搭建的更好!



同 时 , 大 家 也 会 发 现 , 在 这 个 整 个 技 术 中 包 括 了 操 作 系 统 、 Web 服 务 及 数 据 库 多 种 技 术 的 整 合 。 我 相 信 , 也 许 Windows 没 有 Unix 稳 定 , IIS



没 有 Apache 稳 定 ,SQL Server 也 没 有 Oracle 强 大 ,但 是 ,谁 可 以 将 他 们 如 此 完 美 的 联 动 到 一 起 呢 ? 所 以 说 ,虽 然 微 软 每 一 方 面 都 不 是 太 强 ,但 是 如 果 把 微 软



的东西都整合到一起,谁敢说他不强大呢?微软就是微软!







三 、 Asp.net 关 于 form 认 证 的 一 般 设 置



asp.net 关 于 form 认 证 的 一 般 设 置 :



1: 在 web.config 中 , 加 入 form 认 证 ;



























2: 如 果 有 注 册 页 面 时 还 应 该 允 许 匿 名 用 户 调 用 注 册 页 面 进 行 注 册 ;



以 下 代 码 应 该 在 之 间 ,而 不 应 该 包 含 到 ..之 间 ;



----------------表 示 允 许 匿 名 用 户 对 userReg.aspx 页 面 进 行 访 问 .































3 在登录成功后要创建身份验证票, 表明已经通过认证的合法用户;



if(登 陆 成 功 )







System.Web.Security.FormsAuthentication.SetAuthCookie( 用 户 名 称 , false);



四 、 访 问 Web.config 文 件

87



你 可 以 通 过 使 用 ConfigurationSettings.AppSettings 静 态 字 符 串 集 合 来 访 问 Web.config 文 件 示 例 : 获 取 上 面 例 子 中 建 立 的 连 接 字 符 串 。 例 如 :



protected static string Isdebug = ConfigurationSettings.AppSettings["scon"]







94. 什么是 viewstate,能否禁用?是否所用控件都可以禁用?





95. 请解释 ASP.NET 中 button linkbutton imagebutton 及 hyperlink 这四个控件之间的功别



96. 请解释一下.NET 多层应用程序中层与层之间以那几种方式进行数据传递.并解释你自己的项

目中采用那种方式进行.



97. 如果需要在 datagride 控件中的某一列中添加下拉列表框并绑定数据怎么解决?



98. 请解释 asp.net 中的数据绑定与传统数据绑定有什么区别?



99. Repeater Web 服务器控件概述

Repeater Web 服 务 器 控 件 是 一 个 容 器 控 件 , 它 使 您 可 以 从 页 的 任 何 可 用 数 据 中 创 建 出 自 定 义 列 表 。 Repeater 控 件 不 具 备 内 置 的 呈 现 功 能 , 这 表 示 用 户 必 须



通 过 创 建 模 板 为 Repeater 控 件 提 供 布 局 。 当 该 页 运 行 时 , Repeater 控 件 依 次 通 过 数 据 源 中 的 记 录 , 并 为 每 个 记 录 呈 现 一 个 项 。



由 于 Repeater 控 件 没 有 默 认 的 外 观 , 因 此 可 以 使 用 该 控 件 创 建 许 多 种 列 表 , 其 中 包 括 :



表布局



逗 号 分 隔 的 列 表 ( 例 如 , a、 b、 c、 d 等 )



XML 格 式 的 列 表



配 合 模 板 使 用 Repeater 控 件



若 要 使 用 Repeater 控 件 ,请 创 建 定 义 控 件 内 容 布 局 的 模 板 。模 板 可 以 包 含 标 记 和 控 件 的 任 意 组 合 。如 果 未 定 义 模 板 ,或 者 如 果 模 板 都 不 包 含 元 素 ,则 当 应 用



程序运行时,该控件不显示在页上。



下 表 描 述 了 Repeater 控 件 支 持 的 模 板 。





模板属性 说明





ItemTemplate 包 含 要 为 数 据 源 中 每 个 数 据 项 都 要 呈 现 一 次 的 HTML 元 素 和 控 件 。





AlternatingItemTemplate 包 含 要 为 数 据 源 中 每 个 数 据 项 都 要 呈 现 一 次 的 HTML 元 素 和 控 件 。 通 常 , 可 以 使 用 此 模 板 为 交 替 项 创 建 不 同 的 外 观 , 例 如 指 定 一 种 与 在





ItemTemplate 中 指 定 的 颜 色 不 同 的 背 景 色 。







HeaderTemplate 和 包含在列表的开始和结束处分别呈现的文本和控件。



FooterTemplate







SeparatorTemplate 包 含 在 每 项 之 间 呈 现 的 元 素 。 典 型 的 示 例 可 能 是 一 条 直 线 ( 使 用 hr 元 素 ) 。









100. 比较 GridView 和 DataGrid Web 服务器控件

GridView 控 件 是 DataGrid 控 件 的 后 继 控 件 。 与 DataGrid 控 件 相 似 , GridView 控 件 旨 在 在 HTML 表 中 显 示 数 据 。 当 绑 定 到 数 据 源 时 , DataGrid 和



GridView 控 件 分 别 将 DataSource 中 的 一 行 显 示 为 输 出 表 中 的 一 行 。



DataGrid 和 GridView 控 件 都 是 从 WebControl 类 派 生 的 。虽 然 GridView 控 件 与 DataGrid 控 件 具 有 类 似 的 对 象 模 型 ,但 与 DataGrid 控 件 相 比 ,前 者



还具有许多新功能和优势,包括:



更丰富的设计时功能。



改进的数据源绑定功能。



排序、分页、更新和删除的自动处理。



其他列类型和设计时列操作。



具 有 PagerTemplate 属 性 的 自 定 义 页 导 航 用 户 界 面 (UI)。



GridView 控 件 和 DataGrid 控 件 之 间 的 差 异 包 括 :



不同的自定义分页支持。



不同的事件模型。

88



改进的设计时功能



使 用 DataGrid 控 件 时 , 数 据 的 排 序 、 分 页 和 就 地 编 辑 需 要 附 加 的 编 码 。 GridView 控 件 则 使 您 无 需 编 写 任 何 代 码 即 可 添 加 排 序 、 分 页 和 编 辑 功 能 。 实 际 上 ,



可以通过在控件上设置属性来自动完成这些任务(以及诸如到数据源的数据绑定等其他常见任务)。



在 设 计 器 ( 如 Microsoft Visual Studio) 中 工 作 时 , 可 以 利 用 内 置 在 GridView 控 件 中 的 设 计 器 功 能 。 GridView 控 件 提 供 了 对 智 能 标 记 面 板 的 支 持 , 这 种



面板为执行常见任务(如设置属性和启动模板编辑)提供了方便的界面。



改进的数据源绑定功能



通 常 ,将 DataSet 控 件 、 DbDataReader 控 件 或 集 合( 如 Array、 ArrayList 或 System.Collections 命 名 空 间 中 的 其 他 一 些 类 )分 配 给 DataGrid 控



件 或 GridView 控 件 的 DataSource 属 性 。 DataGrid 控 件 和 GridView 控 件 可 以 绑 定 任 何 实 现 IEnumerable 或 IListSource 接 口 的 对 象 。



DataGrid 控 件 可 以 以 声 明 方 式 绑 定 DataSourceControl 控 件 , 但 这 只 适 用 于 数 据 选 择 。 必 须 手 动 编 码 才 能 实 现 排 序 、 分 页 、 更 新 和 删 除 。 GridView 控 件



支 持 DataSourceID 属 性 , 该 属 性 接 受 任 何 实 现 了 IDataSource 接 口 因 此 可 以 利 用 数 据 源 控 件 的 排 序 、 分 页 、 更 新 和 删 除 功 能 的 对 象 , 例 如 ,



SqlDataSource 控 件 。



其他列类型



GridView 控 件 支 持 以 下 列 类 型 : BoundField、 HyperLinkField、 ButtonField、 CommandField、 ImageField 和 CheckBoxField。



内置和自定义分页支持



DataGrid 控 件 需 要 其 他 编 码 用 于 分 页 。GridView 控 件 通 过 设 置 PagerSettings 属 性 自 动 支 持 分 页 。PagerSettings 属 性 支 持 四 种 模 式 : Numeric( 默 认



值 ) 、 NextPrevious、 NumericFirstLast 和 NextPreviousFirstLast。 Numeric 模 式 显 示 带 编 号 的 页 链 接 而 不 是 “ 下 一 个 /上 一 个 ” 链 接 , 并 且



NumericFirstLast 选 项 添 加 第 一 个 和 最 后 一 个 页 链 接 。 GridView 控 件 的 PagerStyle 属 性 可 以 用 于 设 置 页 导 航 的 样 式 和 位 置 。



另 外 , 可 以 使 用 PagerTemplate 自 定 义 GridView 控 件 的 页 导 航 按 钮 。



GridView 中 的 自 定 义 分 页 支 持 由 绑 定 数 据 源 控 件 提 供 , 而 AllowCustomPaging 机 制 则 是 由 DataGrid 控 件 提 供 的 。



扩展的事件模型



DataGrid 和 GridView 控 件 具 有 不 同 的 事 件 模 型 。



DataGrid 控 件 引 发 操 作 的 单 个 事 件 , 而 GridView 控 件 能 够 引 发 操 作 前 和 操 作 后 的 事 件 。 GridView 控 件 支 持 在 对 字 段 排 序 时 发 生 的 Sorting 事 件 。 注 意 ,



此 排 序 事 件 发 生 在 GridView 控 件 自 动 处 理 排 序 操 作 之 前 ,这 样 您 将 有 机 会 检 查 或 更 改 SortExpression 属 性 ,或 通 过 在 传 递 的 事 件 参 数 上 将 Cancel 属 性



设 置 为 true 来 取 消 此 操 作 。



GridView 控 件 支 持 在 GridView 控 件 完 成 排 序 操 作 之 后 发 生 的 Sorted 事 件 , 这 样 您 将 有 机 会 更 改 排 序 操 作 的 结 果 或 设 置 其 格 式 。 相 反 , DataGrid 控 件 支



持 在 对 列 进 行 排 序 时 发 生 的 SortCommand 事 件 。 同 样 , GridView 控 件 支 持 在 GridView 控 件 自 动 处 理 更 新 操 作 之 前 和 之 后 发 生 的 RowUpdating 和



RowUpdated 事 件 。 相 比 之 下 , DataGrid 控 件 支 持 在 对 网 格 中 的 项 单 击 “ 更 新 ” 按 钮 时 发 生 的 UpdateCommand 事 件 。









101. ASP.NET 保存用户状态

保持方法 谁需要数据 保持多长时间 数据量大小



Application 所 有 用 户 整 个 应 用 程 序 生 命 期 任 意 大 小



Cookie 一 个 用 户 可 以 很 短 , 如 果 用 户 不 删 除 也 可 以 很 长 小 的 、 简 单 数 据



Form Post 一 个 用 户 到 下 一 次 请 求 ( 可 以 跨 越 多 个 请 求 重 复 使 用 ) 任 意 大 小



QueryString 一 个 或 一 组 用 户 到下一次请求(可以跨越多个请求重复使用) 小的、简单数据



Sessions 一 个 用 户 用 户 活 动 时 一 直 保 持 + 一 段 时 间 ( 一 般 20 分 钟 )



Cache 所有用户或某些用户 根据需要 可大可小、可简单可复杂



Context 一 个 用 户 一个请求 可以保持大对象,但是一般不这样使用



ViewState 一 个 用 户 一 个 Web 窗 体 最 小



Config file 所 有 用 户 知道配置文件被更新 可 以 保 持 大 量 数 据 , 通 常 组 织 小 的 字 符 串 和 XML 结 构







Application



让 我 们 通 过 回 答 上 面 的 状 态 问 题 判 定 条 件 来 说 明 该 对 象 。谁 需 要 数 据 ? 所 有 的 用 户 需 要 访 问 它 。需 要 保 持 数 据 多 长 时 间 ? 永 久 保 持 ,或 在 应 用 程 序 生 存 期



中 保 持 。 数 据 多 大 ? 可 以 是 任 何 大 小 --在 任 何 给 定 的 时 刻 只 有 数 据 的 一 个 副 本 存 在 。



在 传 统 ASP 中 ,Application 对 象 提 供 了 一 个 保 存 频 繁 使 用 但 很 少 改 变 的 数 据 片 的 位 置 ,例 如 菜 单 内 容 和 参 考 数 据 。尽 管 在 ASP.NET 中 Application 依 然



作 为 数 据 容 器 存 在 , 但 是 有 其 它 一 些 更 适 合 以 前 保 存 在 传 统 ASP 应 用 程 序 的 Application 集 合 中 的 数 据 的 对 象 。



在 传 统 的 ASP 中 , 如 果 被 保 存 的 数 据 在 应 用 程 序 的 生 存 期 中 根 本 不 会 改 变 ( 或 很 少 改 变 , 例 如 只 读 数 据 和 大 多 数 情 况 下 是 读 操 作 的 数 据 ) , Application



对 象 是 理 想 的 选 择 。 连 接 字 符 串 就 是 保 存 在 Application 变 量 中 的 一 个 最 普 通 的 数 据 片 , 但 是 在 ASP.NET 中 类 似 的 配 置 数 据 最 好 保 存 在 Web.config 文 件 中 。

89



如 果 使 用 Application 对 象 一 个 需 要 考 虑 的 问 题 是 任 何 写 操 作 要 么 在 Application_OnStart 事 件 ( global.asax) 中 , 要 么 在 Application.Lock 部 分 中 完 成 。



尽 管 使 用 Application.Lock 来 确 保 写 操 作 正 确 地 执 行 是 必 要 的 , 但 是 它 串 行 化 了 对 Application 对 象 的 请 求 , 而 这 对 于 应 用 程 序 来 说 是 个 严 重 的 性 能 瓶 颈 。



图 2 演 示 了 怎 样 使 用 Application 对 象 , 它 包 括 一 个 Web 窗 体 和 它 的 代 码 文 件 。



Application.Lock();



Application[txtName.Text] = txtValue.Text;



Application.UnLock();







Cookies



当 特 定 的 用 户 需 要 特 定 的 数 据 片 ,并 且 需 要 把 数 据 在 某 个 可 变 的 时 段 中 保 持 的 时 候 ,cookie 就 非 常 方 便 。它 的 生 命 周 期 可 能 与 浏 览 器 窗 体 的 一 样 短 ,也 可



以 长 达 数 月 、 数 年 。 cookie 可 以 小 到 只 有 几 个 字 节 的 数 据 , 因 为 它 们 在 每 个 浏 览 器 请 求 中 传 递 , 它 们 的 内 容 需 要 尽 可 能 的 小 。



Cookie 提 供 了 一 条 灵 活 的 、强 大 的 维 护 用 户 请 求 间 数 据 的 途 径 ,这 就 是 为 什 么 Internet 上 大 多 数 动 态 站 点 使 用 它 们 的 原 因 。因 为 cookie 可 以 存 储 的 数 据



量 很 受 限 制 , 最 好 只 在 cookie 中 保 存 键 字 段 , 其 它 的 数 据 保 存 在 数 据 库 或 其 它 的 服 务 器 端 数 据 容 器 中 。 但 是 由 于 不 是 所 有 的 浏 览 器 都 支 持 cookie, 并 且 它 可



以 被 用 户 禁 止 或 删 除 , 因 此 它 们 也 不 能 用 于 保 存 关 键 数 据 。 你 应 该 很 好 地 处 理 用 户 的 cookie 被 删 除 的 情 况 。 最 后 , cookie 作 为 简 单 的 明 文 文 本 保 存 在 用 户 的



计算机中,因此在它里面不能保存敏感的、未加密的数据。



图 通 这

有 种 特 殊 的 cookie 可 以 保 存 单 个 值 或 名 称 /值 对 的 集 合 。 4 显 示 了 单 个 和 多 个 值 cookie 的 示 例 , 过 ASP.NET 的 内 建 追 踪 特 性 输 出 。 些 值 可 以 在 ASP.NET



页 面 中 使 用 Request.Cookies 和 Response.Cookies 集 合 来 维 护



//使 用 HttpCookie 类 是 指 cookie 的 值 和 /或 子 值



HttpCookie cookie;



if(Request.Cookies[txtName.Text] == null)



cookie = new HttpCookie(txtName.Text, txtValue.Text);



cookie = Request.Cookies[txtName.Text];







Response.Cookies[txtName.Text].Value = null;



Response.Cookies[txtName.Text].Expires =



System.DateTime.Now.AddMonths(-1); //上 个 月







Form Post / 隐 藏 的 窗 体 字 段



特 定 的 用 户 需 要 窗 体 的 数 据 , 并 且 它 需 要 在 单 个 请 求 到 应 用 程 序 终 止 的 任 何 阶 段 都 保 持 。 这 些 数 据 事 实 上 可 以 是 任 意 大 小 的 , 它 随 着 每 个 form post 在 网



络上向前和向后发送。



在 传 统 的 ASP 中 ,这 是 在 应 用 程 序 中 暴 露 状 态 的 通 常 的 途 径 ,特 别 是 在 多 页 面 窗 体 应 用 程 序 中 。但 是 在 ASP.NET 中 这 种 技 术 不 太 适 合 了 ,因 为 只 要 你 使 用



postback 模 型 ( 也 就 是 页 面 发 回 给 自 己 ) , Web 控 件 和 ViewState 自 动 处 理 了 这 些 操 作 。 ViewState 是 ASP.NET 对 这 种 技 术 的 实 现 , 我 将 在 本 文 的 后 部 分 讨 论



它 。 访 问 通 过 POST 发 送 的 窗 体 值 是 使 用 HttpRequest 对 象 的 窗 体 集 合 完 成 的 。 在 图 6 中 , 一 个 ASP.NET 页 面 设 置 了 某 个 用 户 的 ID, 在 这 以 后 它 保 持 在 一 个 隐



藏 的 窗 体 字 段 中 。 后 面 的 向 任 何 页 面 的 请 求 保 留 这 个 值 , 直 到 页 面 使 用 Submit 按 钮 链 接 到 其 它 的 用 户 。



username = Request.Form["username"].ToString();



lblUsername.Text = username;



在 ASP.NET 中 一 个 页 面 上 只 能 存 在 一 个 服 务 器 端 窗 体 ,并 且 该 窗 体 必 须 提 交 返 回 到 自 身( 仍 然 可 以 使 用 客 户 端 窗 体 ,没 有 限 制 )。隐 藏 窗 体 字 段 再 也 没 有 用 于



在 .NET 框 架 组 件 上 建 立 的 应 用 程 序 间 传 递 数 据 的 主 要 原 因 之 一 是 .NET 框 架 组 件 控 件 都 可 以 使 用 ViewState 自 动 维 护 自 己 的 状 态 。ViewState 简 单 地 把 使 用 隐 藏



窗体字段设置和检索值所包含的工作封装进一个使用简单的集合对象中。







QueryString



QueryString 对 象 中 保 存 的 数 据 由 单 独 的 用 户 使 用 。 它 的 生 命 周 期 可 能 只 有 一 个 请 求 那 么 短 , 也 可 能 有 用 户 使 用 应 用 程 序 的 时 间 那 么 长 ( 如 果 构 造 正 确 的



话 ) 。 这 类 数 据 一 般 小 于 1KB。 QueryString 中 的 数 据 在 URL 中 传 递 , 对 于 用 户 来 说 是 可 见 的 , 因 此 你 能 猜 到 , 使 用 这 种 技 术 时 , 敏 感 的 数 据 或 可 用 于 控 制 应



用程序的数据需要加密。



也 就 是 说 , QueryString 是 在 ASP.NET Web 窗 体 间 发 送 信 息 的 一 条 很 好 的 途 径 。 例 如 , 如 果 有 一 个 含 有 产 品 列 表 的 数 据 表 格 ( DataGrid) , 并 且 在 表 格 上



有 一 个 链 接 导 向 产 品 的 细 节 页 面 , 使 用 QueryString 就 是 理 想 的 , 可 以 把 产 品 的 ID 包 含 在 链 接 到 产 品 细 节 页 面 的 QueryString 中 ( 例 如



productdetails.aspx?id=4)。使 用 QueryStrings 的 另 一 个 好 处 是 页 面 的 状 态 包 含 在 URL 中 。这 意 味 着 用 户 可 以 把 某 个 通 过 QueryStrings 建 立 的 窗 体 放 入 他



的 收 藏 夹 中 。 当 它 们 作 为 收 藏 返 回 到 页 面 时 , 将 与 作 收 藏 的 时 候 一 样 。 很 明 显 这 只 在 页 面 不 依 赖 QueryString 外 的 所 有 状 态 和 不 作 任 何 改 变 的 时 候 有 作 用 。



敏 感 数 据 , 以 及 任 何 不 希 望 用 户 操 作 的 变 量 应 该 避 免 出 现 在 此 处 ( 除 非 加 密 使 用 户 不 能 阅 读 ) 。 并 且 URL 中 不 合 法 的 字 符 必 须 使 用 Server.UrlEncode 编



码 , 如 图 7 所 示 。 当 处 理 单 个 ASP.NET 页 面 时 , 对 维 护 状 态 来 说 ViewState 是 比 QueryString 好 的 选 择 。 对 于 长 期 的 数 据 存 储 , Cookie、 Sessions 或 Cache

90



都 比 QueryStrings 更 加 适 于 作 为 数 据 容 器 。



Request.QueryString[key].ToString();







Sessions



它 (

Sessions 数 据 对 于 特 定 的 用 户 是 特 定 的 。 的 生 存 期 是 用 户 持 续 请 求 的 时 间 加 上 后 来 一 段 时 间 一 般 是 20 分 钟 )。Sessions 可 以 保 持 或 大 或 小 的 数 据 量 ,



但是如果应用程序用于成百上千的用户,那么总共的存储应该保持最小。



不 幸 的 是 在 传 统 的 ASP 中 Sessions 对 象 的 名 声 很 不 好 , 因 为 它 把 应 用 程 序 约 束 到 特 定 的 计 算 机 上 , 阻 碍 了 用 户 分 组 和 Web 范 围 的 可 伸 缩 性 。 在 ASP.NET



中 几 乎 没 有 这 些 问 题 , 因 为 改 变 Sessions 保 存 的 位 置 很 简 单 。 在 默 认 情 况 下 ( 性 能 最 好 的 情 况 ) , Sessions 数 据 仍 然 保 存 在 本 地 Web 服 务 器 的 内 存 中 , 但 是



ASP.NET 支 持 使 用 外 部 状 态 服 务 器 或 数 据 库 管 理 Sessions 数 据 。



使 用 Sessions 对 象 很 简 单 ,并 且 它 的 语 法 与 传 统 ASP 相 同 。但 是 Sessions 对 象 是 保 存 用 户 数 据 的 方 法 中 效 率 很 低 的 一 种 ,因 为 即 使 用 户 停 止 使 用 应 用 程



序 后 它 仍 然 保 持 在 内 存 中 一 段 时 间 。这 对 于 非 常 繁 忙 的 站 点 的 可 伸 缩 性 有 严 重 的 影 响 。其 它 的 选 择 允 许 对 释 放 内 存 的 更 多 的 控 制 ,例 如 Cache 对 象 也 许 更 适 合



大 量 的 大 数 据 值 。并 且 在 默 认 情 况 下 ASP.NET Sessionss 依 赖 于 cookie,因 此 如 果 用 户 禁 止 或 不 支 持 cookie,Sessionss 就 不 能 工 作 ,但 是 可 以 配 置 Sessionss



支 持 cookie 无 关 。对 于 小 的 数 据 量 ,Sessionss 对 象 是 保 存 只 需 要 在 用 户 当 前 对 话 中 保 持 的 特 定 数 据 的 极 好 位 置 。下 面 的 例 子 演 示 了 怎 样 设 置 和 从 Sessionss



对象中检索值:



// 设 置 Sessions 值



Sessions[txtName.Text] = txtValue.Text;



该 Web 窗 体 与 Application 对 象 中 使 用 的 几 乎 相 同 , 当 允 许 页 面 追 踪 时 Sessions 集 合 的 内 容 也 是 可 见 的 。



你 需 要 记 住 的 是 即 使 没 有 使 用 , Sessionss 也 会 有 应 用 程 序 开 销 。 把 Sessionss 状 态 设 置 为 只 读 的 也 可 以 优 化 只 需 要 读 而 不 需 要 写 数 据 的 页 面 。 可 以 使 用 下 面



两 种 途 径 之 一 来 配 置 Sessionss:











ASP.NET Sessionss 可 以 在 Web.config 或 Machine.config 中 的 Sessionsstate 元 素 中 配 置 。 下 面 是 在 Web.config 中 的 设 置 的 例 子 :











Cache



Cache 对 象 用 于 单 个 用 户 、 一 组 用 户 或 所 有 的 用 户 。 这 种 数 据 为 多 个 请 求 保 持 。 它 可 以 保 持 很 长 时 间 , 但 是 不 能 超 过 应 用 程 序 重 新 启 动 的 时 间 , 并 且 数 据



的终止基于时间或者其它的依赖关系。它可以高效率地保持大量或少量地数据。



Cache 是 ASP.NET 中 最 "酷 "的 对 象 之 一 。 它 提 供 了 难 以 置 信 的 灵 活 性 、 通 用 性 和 性 能 , 因 此 在 ASP.NET 应 用 程 序 中 它 通 常 是 比 Application 或 Sessions



更 好 的 保 持 数 据 的 对 象 。本 文 没 有 详 细 介 绍 Cache 对 象 的 使 用 方 法 ,但 是 仍 然 可 以 说 它 是 一 个 万 能 对 象 。与 其 它 的 集 合 对 象 相 似 ,它 是 一 个 简 单 的 名 称 - 值 集



合 , 但 是 通 过 使 用 指 定 特 定 用 户 的 键 值 可 以 缓 存 特 定 用 户 的 值 。 同 样 你 可 以 缓 存 不 同 的 相 关 数 据 的 多 个 数 据 集 , 例 如 几 个 有 键 ( 如 fordcars 、 chevycars、



gmcars) 的 汽 车 集 合 。 Cache 中 的 数 据 可 以 给 定 一 个 绝 对 的 、 可 变 的 或 基 于 文 件 的 终 止 时 间 。 它 们 也 实 现 了 一 个 回 调 功 能 , 在 被 缓 存 的 值 从 缓 存 中 提 取 时 被 调



用,这个功能很有用,因为接着你能检查它是否为最新的数据变量,如果不是(或数据源不可用),就重新缓存被终止的值。



添 加 和 访 问 缓 存 中 值 的 语 法 与 先 前 谈 到 的 相 似 。但 是 Cache 给 访 问 集 合 内 容 的 标 准 索 引 器 方 法 作 了 补 充 ,它 支 持 多 种 方 法 ,允 许 对 被 缓 存 数 据 的 更 多 的 控



制。



// 给 缓 存 添 加 项



Cache["myKey"] = myValue;



// 从 缓 存 中 读 取 项



Response.Write(Cache["myKey"]);



Cache 对 象 的 最 强 大 的 特 性 之 一 是 当 缓 存 中 的 某 个 项 终 止 时 执 行 回 调 的 能 力 。 它 使 用 了 委 托 或 函 数 指 针 , 这 在 本 文 中 没 有 讨 论 。 幸 运 的 是 一 旦 你 有 了 某 些



这 些 技 术 怎 样 工 作 的 示 例 ,就 能 通 过 简 单 的 剪 切 和 粘 贴 在 应 用 程 序 中 使 用 它 们 ,不 需 要 知 道 委 托 是 怎 样 工 作 的 复 杂 过 程 。有 很 多 使 用 这 种 功 能 的 原 因 ,最 通 常



的是在数据终止时用当前数据重新填充缓存,或者如果重新填充缓存的数据源不可用时恢复旧的缓存数据 。







Context



Context 对 象 保 持 单 个 用 户 、单 个 请 求 的 数 据 ,并 且 数 据 只 在 该 请 求 期 间 保 持 。Context 容 器 可 以 保 持 大 量 的 数 据 ,但 是 典 型 的 情 况 下 是 保 存 小 的 数 据 片,



因 为 它 经 常 通 过 global.asax 中 的 某 个 处 理 方 法 为 每 个 请 求 实 现 。



Context 容 器 ( 从 Page 对 象 访 问 或 使 用 System.Web.HttpContext.Current) 被 提 供 用 于 保 持 需 要 在 不 同 的 HttpModules 和 HttpHandlers 之 间 传 递 的 值 。



它 也 可 以 用 于 保 持 某 个 完 整 请 求 的 相 应 信 息 。例 如 ,IbuySpy 入 口 在 global.asax 中 的 Application_BeginRequest 事 件 过 程 中 给 容 器 填 满 了 许 多 配 置 信 息 。注



意 这 只 在 当 前 请 求 中 可 用 , 如 果 你 希 望 在 下 一 个 请 求 中 也 能 使 用 , 请 考 虑 使 用 ViewState。



从 Context 集 合 中 设 置 和 获 取 数 据 使 用 的 语 法 与 前 面 讨 论 的 其 它 集 合 对 象 ( 如 Application、 Sessions 和 Cache) 的 相 似 。 下 面 是 两 个 简 单 的 例 子 :

91



// 给 Context 添 加 项



Context.Items["myKey"] = myValue;



// 从 Context 中 读 取 项



Response.Write(Context["myKey"]);







ViewState



ViewState 为 单 个 用 户 保 持 状 态 信 息 ,保 持 期 为 ASPX 页 面 工 作 时 间 。ViewState 容 器 可 以 保 持 大 量 的 数 据 ,但 是 必 须 小 心 管 理 ViewState 的 大 小 ,因 为 它



增 加 了 每 个 请 求 和 回 应 的 下 载 ( download) 大 小 。



ViewState 是 ASP.NET 中 的 一 个 新 容 器 , 也 许 你 已 经 使 用 它 了 , 但 是 你 可 能 还 是 不 了 解 它 。 这 是 因 为 所 有 的 内 建 Web 控 件 都 使 用 ViewState 在 页 面 回 发



( postback)间 保 持 自 己 的 值 。但 是 你 必 须 小 心 ,因 为 它 影 响 应 用 程 序 的 性 能 。影 响 的 大 小 依 赖 于 回 发 之 间 使 用 ViewState 的 多 少 --对 大 多 数 Web 窗 体 来 说 数



量非常小。



确 定 某 个 页 面 上 每 个 控 件 使 用 的 ViewState 的 数 量 最 简 单 的 方 法 是 打 开 页 面 追 踪 并 检 查 每 个 控 件 负 载 了 多 少 个 ViewState。 如 果 某 个 特 定 控 件 不 需 要 在 回



请 你

发 之 间 保 持 数 据 , 通 过 把 EnableViewState 设 置 为 false 关 闭 该 对 象 的 ViewState。 也 可 以 通 过 在 浏 览 器 中 查 看 的 HTML 源 并 检 查 隐 藏 窗 体 字 段 __VIEWSTATE



来 确 定 某 个 给 定 的 ASP.NET 页 面 ViewState 的 总 共 大 小 。 注 意 这 些 内 容 都 是 使 用 Base64 编 码 的 , 用 于 放 置 偶 然 的 查 看 和 维 护 。 ViewState 也 可 以 通 过 给 @Page



指 令 添 加 EnableViewState="false"在 整 个 页 面 中 禁 止 。







典 型 的 Web 窗 体 不 需 要 直 接 维 护 ViewState。 但 是 如 果 你 建 立 自 定 义 Web 控 件 , 就 需 要 了 解 它 是 怎 样 工 作 的 , 并 为 你 的 控 件 实 现 它 , 这 样 该 控 件 的 工 作 方



式 才 能 与 随 ASP.NET 发 布 的 Web 控 件 同 样 地 工 作 。 向 ViewState 读 取 或 写 入 值 都 可 以 通 过 上 面 讨 论 地 其 它 集 合 对 象 的 语 法 完 成 :



// 给 ViewState 添 加 项



ViewState["myKey"] = myValue;



//从 Context 读 取 项



Response.Write(ViewState["myKey"]);







Web.config 和 Machine.config 文 件



这 些 文 件 中 的 数 据 对 于 某 个 应 用 程 序 的 所 有 用 户 来 说 都 可 以 使 用 。 Web.config 文 件 中 存 储 的 数 据 可 用 于 应 用 程 序 的 整 个 生 命 周 期 。这 些 数 据 一 般 很 小 ,该



对象一般用于保持文件位置和数据库连接的字符串。大的数据片最好保存在其它位置。



作为其它多样集合对象的补充, 每

ASP.NET 引 入 了 一 组 XML 配 置 文 件 用 于 管 理 应 用 程 序 甚 至 于 整 个 服 务 器 的 很 多 设 置 。 个 ASP.NET 应 用 程 序 使 用 Web.config



文 件 来 设 置 它 的 许 多 属 性 , 每 个 服 务 器 在 系 统 文 件 夹 下 有 一 个 作 为 应 用 程 序 基 础 的 Machine.config 文 件 。 这 些 设 置 都 作 为 默 认 值 使 用 , 除 非 重 载 。 作 为 保 存



配置数据的补充,这些文件可以保存应用程序(或多个应用程序)需要的数据。



无 论 什 么 时 候 应 用 程 序 启 动 都 会 读 取 配 置 信 息 ,接 着 这 些 信 息 被 缓 冲 。由 于 被 缓 冲 了 ,应 用 程 序 可 以 快 速 读 取 它 们 ,因 此 不 需 要 考 虑 应 用 程 序 的 瓶 颈 ,因



为 它 经 常 执 行 某 个 文 本 文 件 的 一 些 整 型 信 息 。 此 外 , 某 个 应 用 程 序 的 Web.config 的 改 变 将 导 致 应 用 程 序 重 新 启 动 。 这 确 保 了 对 配 置 文 件 信 息 的 修 改 立 即 反 映



到应用程序中。



数 据 库 连 接 信 息 ,默 认 图 像 路 径 和 XML 数 据 文 件 路 径 是 通 常 保 存 在 Web.config 文 件 中 的 数 据 片 。在 Web.config 文 件 中 保 存 数 据 的 语 法 如 下 ,在 理 想 的 情



况 下 你 也 许 希 望 使 用 集 成 的 SQL 身 分 验 证 :







































为 了 访 问 ASP.NET 页 面 中 的 值 , 可 以 使 用 ConfigurationSettings 集 合 , 它 在 System.Configuration 名 字 空 间 中 。 下 面 的 简 单 例 子 演 示 了 怎 样 提 取 前 面



的连接字符串到一个本地变量中:



String strConnString =



ConfigurationSettings.AppSettings["connectionString"];





给 System.Configuration 名 字 空 间 添 加 一 个 引 用 减 少 了 引 用 这 些 值 的 代 码 数 量 。 为 对 Web.config 或 Machine.config 的 修 改 将 导 致 应 用 程 序 立 即 重 新 启 动 ,

92



典型情况下这些值只由服务器系统管理员手动修改。因此你可以认为这些文件是保存只读数据而不是应用程序中修改的数据的好位置。









102. ASP.NET 状态保存客户端保存方式比较

客户端保存的方式一般有如下 4 种:



1 ViewState:



利 用 场 合 为 : 在 对 同 一 页 的 多 个 请 求 间 自 动 保 留 值 , 多 用 于 客 户 端 的 一 些 事 件 。 , 典 型 利 用 场 合 为 : 页 面 信 息 重 置 , 登 陆 出 错 次 数 统 计 , Grid 列 排 序 等 。



优 点 : 不 利 用 服 务 器 端 资 源 , 实 现 简 单 , 相 对 高 的 性 : 因 为 经 过 哈 希 计 算 和 压 缩 , 并 且 针 对 Unicode 实 现 进 行 编 码 。



缺 点 : 因 为 ViewState 存 储 在 页 面 本 身 , 所 以 无 法 存 储 较 大 的 值 。 并 且 通 过 源 文 件 可 以 看 见 其 中 的 值 , 虽 然 经 过 哈 希 计 算 和 压 缩 , 但 仍 有 被 篡 改 的 风 险 。



可 存 储 的 类 型 : string,integer,bool,array,arr aylist,hashtable 以 及 其 他 可 以 序 列 化 的 类 型 。



2 HiddenField:



利用场合为:存储少量页面 中经常改动的信息,多和客户端脚本一块使用,典型利用场合为:客户端经历一系列验证之后向服务器端回发,服务器端从客户



端 HiddenField 中 获 取 值 , 进 行 处 理 。 例 如 : LeyserHomepage 中 , 要 删 除 一 项 产 品 , 需 要 在 客 户 端 弹 出 确 认 Form, 用 户 确 认 之 后 再 PostBac k 回 服 务 器 端 进



行 数 据 库 Delete 操 作 , 当 用 户 确 认 要 删 除 时 , 将 当 前 要 删 除 的 产 品 ID 存 放 到 一 个 HiddenField 中 , 然 后 执 行 Form(0).submit 回 发 到 服 务 器 端 , 服 务 器 端 再



从 HiddenField 获 取 产 品 ID 值 , 进 行 数 据 库 操 作 。



优点:不使用服务器资源 ,广泛支持,实现简单



缺 点 : 安 全 性 不 高 , 因 为 它 被 包 含 在 页 面 上 进 行 发 送 , 所 以 可 以 通 过 源 文 件 看 见 他 的 内 容 。 存 储 结 构 少 , 仅 仅 支 持 string,integer,bool,array,arraylis t



等简单的数据结构。并且在其上只存放简单的单值,若要存放多值,需要额外编码。存储量少,因为它被存储在页面本身,所以 无法存储较大的值。而且大



的 数 据 量 会 受 到 和 代 理 的 阻 止 。 注 意 : 使 用 了 HiddenField 之 后 , 需 要 回 发 到 服 务 器 进 行 处 理 , 应 该 使 用 Http Post 方 法 而 不 是 Http Get 方 法 (通 过 URL



请求访问)



3 Cookie:



利用场合为:存储少量页面中经常改动的信息,典型利用场合为:为登陆过的网站保存登陆用户名,为用户输入提供方便 ,还有在一些用户自定义项目上保



存用户的个性化设置。



优点:不使用服务器资源,实现简单,可配置到期时间。



缺 点 : 大 小 受 到 限 制 , 一 般 浏 览 器 支 持 的 最 大 的 Cookie 容 量 为 4096 字 节 。 客 户 端 用 户 可 能 会 配 置 为 拒 绝 Cookie。 安 全 性 : 保 存 在 客 户 端 的 信 息 可 能 会 被 恶



意用户修改或者获取,所以不应该保存敏感信息。持久性:保存期限受到客户端的配置影响。



Cookie 通 常 用 于 存 取 已 知 用 户 自 定 义 内 容 的 个 性 化 情 况 。 在 大 多 数 此 类 情 况 中 , Cookie 是 作 为 “ 标 识 ” 而 不 是 “ 身 份 验 证 ” , 所 以 在 Cookie 中 只 存 储



用 户 名 、 账 户 名 或 唯 一 用 户 ID( 例 如 GUID) 并 使 用 它 来 访 问 站 点 的 用 户 个 性 化 结 构 是 足 够 的 了 。



4 QueryString:



利用场合为: 将信息从一页传递给另一页的最简单的方法。



优点:不使用服务器资源,支持广泛,实现简单



缺 点 :安 全 性 ,因 为 直 接 在 URL 中 暴 露 给 用 户 ,所 以 有 被 篡 改 的 风 险 。容 量 有 限 ,一 般 的 浏 览 器 都 有 255 个 字 符 的 限 制 。只 有 在 通 过 其 URL 请 求 页 时 查 询 字



符串才是可行的选择。不能从已提交给服务器的页读取 查询字符串。







视 图 状 态 :需 要 为 将 回 发 到 自 身 的 页 存 储 少 量 信 息 。 ViewState 属 性 的 使 用 将 提 供 具 有 基 本 安 全 性 的 功 能 。



隐藏域:需要为将回发到自身或另一页的页存储少量信息,并且不需要较高的安全性。 (客户端事件)只能在提交到服务器的页上使用隐藏域。



Cookie: 需 要 在 客 户 端 存 储 少 量 信 息 并 且 不 需 要 较 高 的 安 全 性 。 ( 个 性 化 )



查询字符串: 可以将少量信息从一页传输到另一页,并且不需要较高的安全性。 (页面跳转 )只有在请求同一页,或通过链接请求另一页时,才能使用查



询字符串。







103. 请解释什么是上下文对象,在什么情况下要使用上下文对象

为 继 承 IHttpModule 和 IHttpHandler 接 口 的 类 提 供 了 对 当 前 HTTP 请 求 的 HttpContext 对 象 的 引 用 。该 对 象 提 供 对 请 求 的 内 部 Request、Response 和



Server 属 性 的 访 问 。



1.生 存 周 期 :从 客 户 端 用 户 点 击 并 产 生 了 一 个 向 服 务 器 发 送 请 求 开 始 ---服 务 器 处 理 完 请 求 并 生 成 返 回 到 客 户 端 为 止 .



注 :针 对 每 个 不 同 用 户 的 请 求 ,服 务 器 都 会 创 建 一 个 新 的 HttpContext 实 例 直 到 请 求 结 束 ,服 务 器 销 毁 这 个 实 例 .



2.为 什 么 会 有 HttpContext 类 呢 :在 ASP 年 代 ,大 家 都 是 通 过 在 .asp 页 面 的 代 码 中 使 用 Request,Respose,Server 等 等 这 些 Http 特 定 信 息 的 .但 在



ASP.NET 时 代 ,这 中 方 式 已 经 无 法 满 足 应 用 ,(比 如 我 们 要 在 IHttpModule 中 处 理 Request 时 ,我 们 使 用 什 么 方 法 来 获 取 呢 ?于 是 就 产 生 了 HttpContext 类 ,它 对



Request,Respose,Server 等 等 都 进 行 了 封 装 ,并 保 证 在 整 个 请 求 周 期 内 都 可 以 随 时 随 地 的 调 用 .)



3.特 殊 性 :当 然 HttpContext 不 仅 仅 只 有 这 点 功 能 .ASP.NET 中 它 还 提 供 了 很 多 特 殊 的 功 能 .例 如 Cache.还 有 HttpContext.Item,通 过 它 你 可 以 在



HttpContext 的 生 存 周 期 内 提 前 存 储 一 些 临 时 的 数 据 ,方 便 随 时 使 用 .具 体 的 大 家 还 是 参 考 上 面 的 MSDN 提 供 内 容 .

93





104. HttpContext.Cache 和 HttpRuntime.Cache

Asp.Net 中 可 以 方 便 的 使 用 缓 存 , 对 于 Cache, 一 般 有 两 种 方 式 调 用 : HttpContext.Cache 和 HttpRuntime.Cache。 那 么 这 两 种 Cache 有 什 么 区 别 呢 ?



先 来 看 看 Msdn 上 的 注 释 :



HttpRuntime.Cache: 获 取 当 前 应 用 程 序 的 Cache。



HttpContext.Cache: 为 当 前 HTTP 请 求 获 取 Cache 对 象 。



那 么 是 不 是 说 对 于 HttpRuntime.Cache 就 是 应 用 程 序 级 ,而 HttpContext.Cache 则 是 针 对 每 个 用 户 的 呢 ? NO,而 实 际 上 ,两 者 调 用 的 是 同 一 个 对 象 。他 们 的区



别仅仅在于调用方式不一样(就我所知)。



事 实 胜 过 雄 辩 , 写 个 例 子 来 证 实 一 下 ( 限 于 篇 幅 仅 贴 出 关 键 代 码 , 完 整 代 码 见 附 件 W ebDemo.rar) :



///



/// 通 过 HttpRuntime.Cache 的 方 式 来 保 存 Cache



///



private void btnHttpRuntimeCacheSave_Click(object sender, System.EventArgs e)

{



HttpRuntime.Cache.Insert(cacheKey, cacheValue, null, DateTime.Now.AddMinutes(3), TimeSpan.Zero);



}







///



/// 通 过 HttpRuntime.Cache 的 方 式 来 读 取 Cache



///



private void btnHttpRuntimeCacheLoad_Click(object sender, System.EventArgs e)

{



if (HttpRuntime.Cache[cacheKey] == null)



{



cacheContent = "No Cache";



}



else



{



cacheContent = (string)HttpRuntime.Cache[cacheKey];



}



lblCacheContent.Text = cacheContent;



}







///



/// 通 过 HttpContext.Cache 的 方 式 来 保 存 Cache



///



private void btnHttpContextCacheSave_Click(object sender, System.EventArgs e)

{



HttpContext.Current.Cache.Insert(cacheKey, cacheValue, null, DateTime.Now.AddMinutes(3), TimeSpan.Zero);



}







///



/// 通 过 HttpContext.Cache 的 方 式 来 读 取 Cache



///



private void btnHttpContextCacheLoad_Click(object sender, System.EventArgs e)

{



if (HttpContext.Current.Cache[cacheKey] == null)



{



cacheContent = "No Cache";



}

94



else



{



cacheContent = (string)HttpContext.Current.Cache[cacheKey];



}



lblCacheContent.Text = cacheContent;



}



通过这个例子可以很容易证明:



HttpContext.Cache 保 存 的 Cache, HttpContext.Cache 和 HttpRuntime.Cache 都 可 以 读 取 。



HttpRuntime.Cache 保 存 的 Cache, HttpContext.Cache 和 HttpRuntime.Cache 都 可 以 读 取 。



无 论 是 哪 个 用 户 通 过 什 么 方 式 对 Cache 的 改 变 , 其 他 用 户 无 论 用 什 么 方 式 读 取 的 Cache 内 容 也 会 随 之 变 。







105. 用存储过程和触发器如何解决并发?



106. SQL 触发器

CREATE TRIGGER trigger_name

ON { table | view }

[ WITH ENCRYPTION ]

{

{ { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] }

[ WITH APPEND ]

[ NOT FOR REPLICATION ]

AS

[ { IF UPDATE ( column )

[ { AND | OR } UPDATE ( column ) ]

[ ... n ]

| IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )

{ comparison_operator } column_bitmask [ ... n ]

} ]

sql_statement [ ... n ]

}

}

参数



trigger_name





是触发器的名称。触发器名称必须符合标识符规则,并且在数据库中必须唯一。可以选择是否指定触发器所有者名称。





Table | view





是在其上执行触发器的表或视图,有时称为触发器表或触发器视图。可以选择是否指定表或视图的所有者名称。





WITH ENCRYPTION





加密 syscomments 表中包含 CREATE TRIGGER 语句文本的条目。使用 WITH ENCRYPTION 可防止将触发器作为 SQL Server 复制的一部分发布。





AFTER





指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行后才激发。所有的引用级联操作和约束检查也必须成功完成后,才能执行此触发器。





如果仅指定 FOR 关键字,则 AFTER 是默认设置。





不能在视图上定义 AFTER 触发器。





INSTEAD OF





指定执行触发器而不是执行触发 SQL 语句,从而替代触发语句的操作。





在表或视图上,每个 INSERT、UPDATE 或 DELETE 语句最多可以定义一个 INSTEAD OF 触发器。然而,可以在每个具有 INSTEAD OF 触发器的视图上定义视图。

95



INSTEAD OF 触发器不能在 WITH CHECK OPTION 的可更新视图上定义。如果向指定了 WITH CHECK OPTION 选项的可更新视图添加 INSTEAD OF 触发器,SQL Server 将产生一个错误。



用户必须用 ALTER VIEW 删除该选项后才能定义 INSTEAD OF 触发器。





{ [DELETE] [,] [INSERT] [,] [UPDATE] }





是指定在表或视图上执行哪些数据修改语句时将激活触发器的关键字。必须至少指定一个选项。在触发器定义中允许使用以任意顺序组合的这些关键字。如果指定的选项多于一个,需用逗号分隔这些



选项。





对于 INSTEAD OF 触发器,不允许在具有 ON DELETE 级联操作引用关系的表上使用 DELETE 选项。同样,也不允许在具有 ON UPDATE 级联操作引用关系的表上使用 UPDATE 选项。





WITH APPEND





指定应该添加现有类型的其它触发器。只有当兼容级别是 65 或更低时,才需要使用该可选子句。如果兼容级别是 70 或更高,则不必使用 WITH APPEND 子句添加现有类型的其它触发器(这是兼



容级别设置为 70 或更高的 CREATE TRIGGER 的默认行为)。有关更多信息,请参见 sp_dbcmptlevel。





WITH APPEND 不能与 INSTEAD OF 触发器一起使用,或者,如果显式声明 AFTER 触发器,也不能使用该子句。只有当出于向后兼容而指定 FOR 时(没有 INSTEAD OF 或 AFTER),才能



使用 WITH APPEND。以后的版本将不支持 WITH APPEND 和 FOR(将被解释为 AFTER)。





NOT FOR REPLICATION





表示当复制进程更改触发器所涉及的表时,不应执行该触发器。





AS





是触发器要执行的操作。





sql_statement





是触发器的条件和操作。触发器条件指定其它准则,以确定 DELETE、INSERT 或 UPDATE 语句是否导致执行触发器操作。





当尝试 DELETE、INSERT 或 UPDATE 操作时,Transact-SQL 语句中指定的触发器操作将生效。





触发器可以包含任意数量和种类的 Transact-SQL 语句。触发器旨在根据数据修改语句检查或更改数据;它不应将数据返回给用户。触发器中的 Transact-SQL 语句常常包含控制流语言。CREATE



TRIGGER 语句中使用几个特殊的表:









 deleted 和 inserted 是逻辑(概念)表。这些表在结构上类似于定义触发器的表(也就是在其中尝试

用户操作的表) ;这些表用于保存用户操作可能更改的行的旧值或新值。例如,若要检索 deleted 表中的

所有值,请使用:

 SELECT *

 FROM deleted





 如果兼容级别等于 70,那么在 DELETE、INSERT 或 UPDATE 触发器中,SQL Server 将不允许引用

inserted 和 deleted 表中的 text、ntext 或 image 列。不能访问 inserted 和 deleted 表中

的 text、 ntext 和 image 值。若要在 INSERT 或 UPDATE 触发器中检索新值, 请将 inserted 表

与原始更新表联接。当兼容级别是 65 或更低时,对 inserted 或 deleted 表中允许空值的 text、

ntext 或 image 列,将返回空值;如果这些列不可为空,则返回零长度字符串。



当兼容级别是 80 或更高时,SQL Server 允许在表或视图上通过 INSTEAD OF 触发器更新 text、ntext 或 image 列。





n





是表示触发器中可以包含多条 Transact-SQL 语句的占位符。对于 IF UPDATE (column) 语句,可以通过重复 UPDATE (column) 子句包含多列。





IF UPDATE (column)





测试在指定的列上进行的 INSERT 或 UPDATE 操作,不能用于 DELETE 操作。可以指定多列。因为在 ON 子句中指定了表名,所以在 IF UPDATE 子句中的列名前不要包含表名。若要测试在多



个列上进行的 INSERT 或 UPDATE 操作,请在第一个操作后指定单独的 UPDATE(column) 子句。在 INSERT 操作中 IF UPDATE 将返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL)



值。





说明 IF UPDATE (column) 子句的功能等同于 IF、IF...ELSE 或 WHILE 语句,并且可以使用 BEGIN...END 语句块。有关更多信息,请参见控制流语言。

96









可以在触发器主体中的任意位置使用 UPDATE (column)。





column





是要测试 INSERT 或 UPDATE 操作的列名。该列可以是 SQL Server 支持的任何数据类型。但是,计算列不能用于该环境中。有关更多信息,请参见数据类型。





IF (COLUMNS_UPDATED())





测试是否插入或更新了提及的列,仅用于 INSERT 或 UPDATE 触发器中。COLUMNS_UPDATED 返回 varbinary 位模式,表示插入或更新了表中的哪些列。





COLUMNS_UPDATED 函数以从左到右的顺序返回位,最左边的为最不重要的位。最左边的位表示表中的第一列;向右的下一位表示第二列,依此类推。如果在表上创建的触发器包含 8 列以上,则



COLUMNS_UPDATED 返回多个字节,最左边的为最不重要的字节。在 INSERT 操作中 COLUMNS_UPDATED 将对所有列返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。





可以在触发器主体中的任意位置使用 COLUMNS_UPDATED。





bitwise_operator





是用于比较运算的位运算符。





updated_bitmask





是整型位掩码,表示实际更新或插入的列。例如,表 t1 包含列 C1、C2、C3、C4 和 C5。假定表 t1 上有 UPDATE 触发器,若要检查列 C2、C3 和 C4 是否都有更新,指定值 14;若要检



查是否只有列 C2 有更新,指定值 2。





comparison_operator





是比较运算符。使用等号 (=) 检查 updated_bitmask 中指定的所有列是否都实际进行了更新。使用大于号 (>) 检查 updated_bitmask 中指定的任一列或某些列是否已更新。





column_bitmask





是要检查的列的整型位掩码,用来检查是否已更新或插入了这些列。





注释



触发器常常用于强制业务规则和数据完整性。SQL Server 通过表创建语句(ALTER TABLE 和 CREATE TABLE)提供声明引用完整性 (DRI);但是 DRI 不提供数据库间的引用完整性。若要强制



引用完整性(有关表的主键和外键之间关系的规则),请使用主键和外键约束(ALTER TABLE 和 CREATE TABLE 的 PRIMARY KEY 和 FOREIGN KEY 关键字)。如果触发器表存在约束,则在



INSTEAD OF 触发器执行之后和 AFTER 触发器执行之前检查这些约束。如果违反了约束,则回滚 INSTEAD OF 触发器操作且不执行(激发)AFTER 触发器。





可用 sp_settriggerorder 指定表上第一个和最后一个执行的 AFTER 触发器。在表上只能为每个 INSERT、UPDATE 和 DELETE 操作指定一个第一个执行和一个最后一个执行的 AFTER 触



发器。如果同一表上还有其它 AFTER 触发器,则这些触发器将以随机顺序执行。





如果 ALTER TRIGGER 语句更改了第一个或最后一个触发器,则将除去已修改触发器上设置的第一个或最后一个特性,而且必须用 sp_settriggerorder 重置排序值。





只有当触发 SQL 语句(包括所有与更新或删除的对象关联的引用级联操作和约束检查)成功执行后,AFTER 触发器才会执行。AFTER 触发器检查触发语句的运行效果,以及所有由触发语句引起的



UPDATE 和 DELETE 引用级联操作的效果。





触发器限制



CREATE TRIGGER 必须是批处理中的第一条语句,并且只能应用到一个表中。





触发器只能在当前的数据库中创建,不过触发器可以引用当前数据库的外部对象。





如果指定触发器所有者名称以限定触发器,请以相同的方式限定表名。





在同一条 CREATE TRIGGER 语句中,可以为多种用户操作(如 INSERT 和 UPDATE)定义相同的触发器操作。





如果一个表的外键在 DELETE/UPDATE 操作上定义了级联,则不能在该表上定义 INSTEAD OF DELETE/UPDATE 触发器。





在触发器内可以指定任意的 SET 语句。所选择的 SET 选项在触发器执行期间有效,并在触发器执行完后恢复到以前的设置。





与使用存储过程一样,当触发器激发时,将向调用应用程序返回结果。若要避免由于触发器激发而向应用程序返回结果,请不要包含返回结果的 SELECT 语句,也不要包含在触发器中进行变量赋值



的语句。包含向用户返回结果的 SELECT 语句或进行变量赋值的语句的触发器需要特殊处理;这些返回的结果必须写入允许修改触发器表的每个应用程序中。如果必须在触发器中进行变量赋值,则



应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。

97



DELETE 触发器不能捕获 TRUNCATE TABLE 语句。尽管 TRUNCATE TABLE 语句实际上是没有 WHERE 子句的 DELETE(它删除所有行),但它是无日志记录的,因而不能执行触发器。因为



TRUNCATE TABLE 语句的权限默认授予表所有者且不可转让,所以只有表所有者才需要考虑无意中用 TRUNCATE TABLE 语句规避 DELETE 触发器的问题。





无论有日志记录还是无日志记录,WRITETEXT 语句都不激活触发器。





触发器中不允许以下 Transact-SQL 语句:







ALTER DATABASE CREATE DATABASE DISK INIT



DISK RESIZE DROP DATABASE LOAD DATABASE



LOAD LOG RECONFIGURE RESTORE DATABASE



RESTORE LOG





说明 由于 SQL Server 不支持系统表中的用户定义触发器,因此建议不要在系统表中创建用户定义触发器。









多个触发器



SQL Server 允许为每个数据修改事件(DELETE、INSERT 或 UPDATE)创建多个触发器。例如,如果对已有 UPDATE 触发器的表执行 CREATE TRIGGER FOR UPDATE,则将创建另一个更



新触发器。在早期版本中,在每个表上,每个数据修改事件(INSERT、UPDATE 或 DELETE)只允许有一个触发器。





说明 如果触发器名称不同,则 CREATE TRIGGER(兼容级别为 70)的默认行为是在现有的触发器中添加其它触发器。如果触发器名称相同,则 SQL Server 返回一条错误信息。但是,如果兼容



级别等于或小于 65,则使用 CREATE TRIGGER 语句创建的新触发器将替换同一类型的任何现有触发器,即使触发器名称不同。有关更多信息,请参见 sp_dbcmptlevel。









递归触发器



当在 sp_dboption 中启用 recursive triggers 设置时,SQL Server 还允许触发器的递归调用。





递归触发器允许发生两种类型的递归:









 间接递归



 直接递归



使用间接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T2。在这种情况下,触发器 T2 将激发并更新 T1。





使用直接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T1。由于表 T1 被更新,触发器 TR1 再次激发,依此类推。





下例既使用了间接触发器递归,又使用了直接触发器递归。假定在表 T1 中定义了两个更新触发器 TR1 和 TR2。触发器 TR1 递归地更新表 T1。UPDATE 语句使 TR1 和 TR2 各执行一次。



而 TR1 的执行将触发 TR1(递归)和 TR2 的执行。给定触发器的 inserted 和 deleted 表只包含与唤醒调用触发器的 UPDATE 语句相对应的行。





说明 只有启用 sp_dboption 的 recursive triggers 设置,才会发生上述行为。对于为给定事件定义的多个触发器,并没有确定的执行顺序。每个触发器都应是自包含的。









禁用 recursive triggers 设置只能禁止直接递归。若要也禁用间接递归,请使用 sp_configure 将 nested triggers 服务器选项设置为 0。





如果任一触发器执行了 ROLLBACK TRANSACTION 语句,则无论嵌套级是多少,都不会进一步执行其它触发器。





嵌套触发器



触发器最多可以嵌套 32 层。如果一个触发器更改了包含另一个触发器的表,则第二个触发器将激活,然后该触发器可以再调用第三个触发器,依此类推。如果链中任意一个触发器引发了无限循环,



则会超出嵌套级限制,从而导致取消触发器。若要禁用嵌套触发器,请用 sp_configure 将 nested triggers 选项设置为 0(关闭)。默认配置允许嵌套触发器。如果嵌套触发器是关闭的,则



也将禁用递归触发器,与 sp_dboption 的 recursive triggers 设置无关。





延迟名称解析

98



SQL Server 允许 Transact-SQL 存储过程、触发器和批处理引用编译时不存在的表。这种能力称为延迟名称解析。但是,如果 Transact-SQL 存储过程、触发器或批处理引用在存储过程或触发



器中定义的表,则只有当兼容级别设置(通过执行 sp_dbcmptlevel 设置)等于 65 时,才会在创建时发出警告。如果使用批处理,则在编译时发出警告。如果引用的表不存在,将在运行时返回



错误信息。有关更多信息,请参见延迟名称解析和编译。





权限



CREATE TRIGGER 权限默认授予定义触发器的表所有者、sysadmin 固定服务器角色成员以及 db_owner 和 db_ddladmin 固定数据库角色成员,并且不可转让。





若要检索表或视图中的数据,用户必须在表或视图中拥有 SELECT 语句权限。若要更新表或视图的内容,用户必须在表或视图中拥有 INSERT、DELETE 和 UPDATE 语句权限。





如果视图中存在 INSTEAD OF 触发器,用户必须在该视图中有 INSERT、DELETE 和 UPDATE 特权,以对该视图发出 INSERT、DELETE 和 UPDATE 语句,而不管实际上是否在视图上执行了



这样的操作。





示例



A. 使 用 带 有 提 醒 消 息 的 触 发 器



当有人试图在 titles 表中添加或更改数据时,下例将向客户端显示一条消息。





说明 消息 50009 是 sysmessages 中的用户定义消息。有关创建用户定义消息的更多信息,请参见 sp_addmessage。









USE pubs



IF EXISTS (SELECT name FROM sysobjects



WHERE name = 'reminder' AND type = 'TR')



DROP TRIGGER reminder



GO



CREATE TRIGGER reminder



ON titles



FOR INSERT, UPDATE



AS RAISERROR (50009, 16, 10)



GO



B. 使 用 带 有 提 醒 电 子 邮 件 的 触 发 器



当 titles 表更改时,下例将电子邮件发送给指定的人员 (MaryM)。







USE pubs

IF EXISTS (SELECT name FROM sysobjects

WHERE name = 'reminder' AND type = 'TR')

DROP TRIGGER reminder

GO

CREATE TRIGGER reminder

ON titles

FOR INSERT, UPDATE, DELETE

AS

EXEC master..xp_sendmail 'MaryM',

'Don''t forget to print a report for the distributors.'

GO

99



C. 在 employee 和 jobs 表 之 间 使 用 触 发 器 业 务 规 则



由于 CHECK 约束只能引用定义了列级或表级约束的列,表间的任何约束(在下例中是指业务规则)都必须定义为触发器。





下例创建一个触发器,当插入或更新雇员工作级别 (job_lvls) 时,该触发器检查指定雇员的工作级别(由此决定薪水)是否处于为该工作定义的范围内。若要获得适当的范围,必须引用 jobs 表。







USE pubs

IF EXISTS (SELECT name FROM sysobjects

WHERE name = 'employee_insupd' AND type = 'TR')

DROP TRIGGER employee_insupd

GO

CREATE TRIGGER employee_insupd

ON employee

FOR INSERT, UPDATE

AS

/* Get the range of level for this job type from the jobs table. */

DECLARE @min_lvl tinyint,

@max_lvl tinyint,

@emp_lvl tinyint,

@job_id smallint

SELECT @min_lvl = min_lvl,

@max_lvl = max_lvl,

@emp_lvl = i.job_lvl,

@job_id = i.job_id

FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id

JOIN jobs j ON j.job_id = i.job_id

IF (@job_id = 1) and (@emp_lvl 10)

BEGIN

RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)

ROLLBACK TRANSACTION

END

ELSE

IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)

BEGIN

RAISERROR ('The level for job_id:%d should be between %d and %d.',

16, 1, @job_id, @min_lvl, @max_lvl)

ROLLBACK TRANSACTION

END

D. 使 用 延 迟 名 称 解 析



下例创建两个触发器以说明延迟名称解析。







USE pubs

IF EXISTS (SELECT name FROM sysobjects

WHERE name = 'trig1' AND type = 'TR')

DROP TRIGGER trig1

GO

-- Creating a trigger on a nonexistent table.

CREATE TRIGGER trig1

on authors

FOR INSERT, UPDATE, DELETE

AS

100



SELECT a.au_lname, a.au_fname, x.info

FROM authors a INNER JOIN does_not_exist x

ON a.au_id = x.au_id

GO

-- Here is the statement to actually see the text of the trigger.

SELECT o.id, c.text

FROM sysobjects o INNER JOIN syscomments c

ON o.id = c.id

WHERE o.type = 'TR' and o.name = 'trig1'

-- Creating a trigger on an existing table, but with a nonexistent

-- column.

USE pubs

IF EXISTS (SELECT name FROM sysobjects

WHERE name = 'trig2' AND type = 'TR')

DROP TRIGGER trig2

GO

CREATE TRIGGER trig2

ON authors

FOR INSERT, UPDATE

AS

DECLARE @fax varchar(12)

SELECT @fax = phone

FROM authors

GO

-- Here is the statement to actually see the text of the trigger.

SELECT o.id, c.text

FROM sysobjects o INNER JOIN syscomments c

ON o.id = c.id

WHERE o.type = 'TR' and o.name = 'trig2'

E. 使 用 COLUMNS_UPDATED



下例创建两个表:一个 employeeData 表和一个 auditEmployeeData 表。人力资源部的成员可以修改 employeeData 表,该表包含敏感的雇员薪水信息。如果更改了雇员的社会保险号



码 (SSN)、年薪或银行帐户,则生成审核记录并插入到 auditEmployeeData 审核表。





通过使用 COLUMNS_UPDATED() 功能,可以快速测试对这些包含敏感雇员信息的列所做的更改。只有在试图检测对表中的前 8 列所做的更改时,COLUMNS_UPDATED() 才起作用。







USE pubs

IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES

WHERE TABLE_NAME = 'employeeData')

DROP TABLE employeeData

IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES

WHERE TABLE_NAME = 'auditEmployeeData')

DROP TABLE auditEmployeeData

GO

CREATE TABLE employeeData (

emp_id int NOT NULL,

emp_bankAccountNumber char (10) NOT NULL,

emp_salary int NOT NULL,

emp_SSN char (11) NOT NULL,

emp_lname nchar (32) NOT NULL,

emp_fname nchar (32) NOT NULL,

101



emp_manager int NOT NULL

)

GO

CREATE TABLE auditEmployeeData (

audit_log_id uniqueidentifier DEFAULT NEWID(),

audit_log_type char (3) NOT NULL,

audit_emp_id int NOT NULL,

audit_emp_bankAccountNumber char (10) NULL,

audit_emp_salary int NULL,

audit_emp_SSN char (11) NULL,

audit_user sysname DEFAULT SUSER_SNAME(),

audit_changed datetime DEFAULT GETDATE()

)

GO

CREATE TRIGGER updEmployeeData

ON employeeData

FOR update AS

/*Check whether columns 2, 3 or 4 has been updated. If any or all of columns 2, 3 or 4 have been changed, create an audit

record. The bitmask is: power(2,(2-1))+power(2,(3-1))+power(2,(4-1)) = 14. To check if all columns 2, 3, and 4 are updated,

use = 14 in place of >0 (below).*/

IF (COLUMNS_UPDATED() & 14) > 0

/*Use IF (COLUMNS_UPDATED() & 14) = 14 to see if all of columns 2, 3, and 4 are updated.*/

BEGIN

-- Audit OLD record.

INSERT INTO auditEmployeeData

(audit_log_type,

audit_emp_id,

audit_emp_bankAccountNumber,

audit_emp_salary,

audit_emp_SSN)

SELECT 'OLD',

del.emp_id,

del.emp_bankAccountNumber,

del.emp_salary,

del.emp_SSN

FROM deleted del

-- Audit NEW record.

INSERT INTO auditEmployeeData

(audit_log_type,

audit_emp_id,

audit_emp_bankAccountNumber,

audit_emp_salary,

audit_emp_SSN)

SELECT 'NEW',

ins.emp_id,

ins.emp_bankAccountNumber,

ins.emp_salary,

ins.emp_SSN

FROM inserted ins

102



END

GO

/*Inserting a new employee does not cause the UPDATE trigger to fire.*/

INSERT INTO employeeData

VALUES ( 101, 'USA-987-01', 23000, 'R-M53550M', N'Mendel', N'Roland', 32)

GO

/*Updating the employee record for employee number 101 to change the salary to 51000 causes the UPDATE trigger to fire

and an audit trail to be produced.*/

UPDATE employeeData

SET emp_salary = 51000

WHERE emp_id = 101

GO

SELECT * FROM auditEmployeeData

GO

/*Updating the employee record for employee number 101 to change both the bank account number and social security number

(SSN) causes the UPDATE trigger to fire and an audit trail to be produced.*/

UPDATE employeeData

SET emp_bankAccountNumber = '133146A0', emp_SSN = 'R-M53550M'

WHERE emp_id = 101

GO

SELECT * FROM auditEmployeeData

GO

F. 使 用 COLUMNS_UPDATED 测 试 8 列 以 上



如果必须测试影响到表中前 8 列以外的列的更新时,必须使用 UBSTRING 函数测试由 COLUMNS_UPDATED 返回的适当的位。下例测试影响 Northwind.dbo.Customers 表中的第 3、第



5 或第 9 列的更新。







USE Northwind

DROP TRIGGER tr1

GO

CREATE TRIGGER tr1 ON Customers

FOR UPDATE AS

IF ( (SUBSTRING(COLUMNS_UPDATED(),1,1)=power(2,(3-1))

+ power(2,(5-1)))

AND (SUBSTRING(COLUMNS_UPDATED(),2,1)=power(2,(1-1)))

)

PRINT 'Columns 3, 5 and 9 updated'

GO

UPDATE Customers

SET ContactName=ContactName,

Address=Address,

Country=Country

GO



107. 描述从 web 页面请求到页面返回的主要事件和过程

页 面 生 命 周 期 执 行 一 系 列 步 骤 :页 面 的 初 始 化 、实 例 化 控 件 、还 原 和 维 护 状 态 、运 行 事 件 处 理 程 序 代 码 、呈 现 。为 了 在 合 适 的 阶 段 执 行 所 需 的 代 码 ,所 以 要 对



页面生命周期非常熟悉。在页生命周期的各个阶段,页面会逐个引发定义的事件,通过对代码开发,在页面的生命事件中执行我们所需要的程序



页生命周期阶段



1、 页 请 求 :发 生 在 页 面 生 命 周 期 之 前 ,用 户 请 求 页 时 ,ASP.NET 将 确 定 是 否 需 要 分 析 和 编 译 页 ,从 而 确 定 是 否 开 始 页 面 的 生 命 周 期 ,或 者 是 否 可 以 在 不 运 行



页的情况下发送页面缓存以进行响应。



2、 开 始 :设 置 页 属 性 ,如 :HttpContext 以 及 其 他 属 性 ;在 此 阶 段 ,页 面 需 要 确 定 是 回 发 请 求 还 是 新 请 求 ,并 设 置 IsPostBack 属 性 ;设 置 页 面 的 UICulture

103



属性。



3、 页 面 初 始 化 : 加 载 所 有 主 题 ; 控 件 生 成 , 并 设 置 UniqueID;



注 : ViewState、 ControlState 中 的 值 还 未 加 载 至 控 件 ; 如 果 页 面 是 回 发 , 则 回 发 数 据 也 还 未 加 载 ; 故 此 时 控 件 可 以 访 问 , 但 值 可 能 出 错 。



4、 加 载 : 如 果 当 前 请 求 是 回 发 请 求 , 则 为 控 件 加 载 ViewState 和 ControlState 中 的 值 。



5、 验 证 : 调 用 所 有 验 证 程 序 控 件 的 Validate 方 法 , 此 方 法 将 设 置 验 证 程 序 控 件 和 页 的 IsValid 属 性 。



6、 回发事件处理:如果请求是回发请求,则调用所有事件处理程序。



7、 呈 现 : 首 先 对 该 页 和 所 有 控 件 进 行 保 存 视 图 状 态 , 然 后 对 每 个 控 件 调 用 Render 方 法 , 它 会 提 供 一 个 文 本 编 写 器 , 用 于 将 控 件 的 输 入 写 入 页 的 Response



属 性 的 OutputStream 中 。



8、 卸 载 : 完 成 呈 现 , 并 已 将 页 发 送 至 客 户 端 、 准 备 丢 弃 该 页 后 , 调 用 卸 载 。 将 卸 载 属 性 如 : Response 和 Request 等 等 。



页生命周期事件:



1、 PreInit



完 成 操 作 : 检 查 IsPostBack 属 性 来 确 定 是 不 是 第 一 次 处 理 该 页 ;



创建或重新创建动态控件



动态设置主控页



动 态 设 置 Theme 属 性



读取或设置配置文件属性



注:如果请求是回发请求,则控件的值尚未从视图状态恢复,即:不应该在此事件中设置控件属性。



2、 Init



完成操作:在所有控件都已经初始化并且应用所有外观设置后引发。使用该事件来读取和初始化控件属性。



3、 InitComplete



完 成 操 作 : 由 Page 对 象 引 发 , 使 用 该 事 件 来 处 理 要 求 完 成 所 有 初 始 化 工 作 的 任 务 。



4、 PreLoad



完 成 操 作 : 为 页 和 所 有 控 件 加 载 视 图 状 态 , 然 后 处 理 Request 实 例 包 括 的 任 何 回 发 数 据 。



注 : 如 果 需 要 在 Load 之 前 对 页 或 控 件 进 行 处 理 , 需 要 该 事 件 。



5、 Load



完成操作:以递归方式加载所有控件。



6、 控 件 事 件



完成操作:处理控件引发的事件。



注 : 在 回 发 请 求 中 , 如 果 页 包 含 验 证 程 序 控 件 , 请 在 执 行 任 何 处 理 之 前 检 查 Page 和 各 个 验 证 控 件 的 IsValid 属 性 。



7、 LoadComplete



8、 PreRender



在控件输出前最后的修改机会。



注:在该事件发生前的操作:



Page 对 所 有 控 件 递 归 进 行 EnsureChildControl 操 作



设 置 了 DataSourceID 属 性 的 数 据 绑 定 控 件 会 调 用 DataBind 方 法 。



9、 SaveStateComplete



在 该 事 件 发 生 前 , 已 经 对 控 件 的 ViewState 进 行 了 保 存 。 将 忽 略 对 页 和 控 件 所 作 的 所 有 修 改 。



10、 Render



方法,便于开发



11、 UnLoad







应用程序生命周期







在 应 用 程 序 的 生 命 周 期 期 间 ,应 用 程 序 会 引 发 可 处 理 的 事 件 并 调 用 可 重 写 的 特 定 方 法 。若 要 处 理 应 用 程 序 事 件 或 方 法 ,可 以 在 应 用 程 序 根 目 录 中 创 建 一 个 名 为



Global.asax 的 文 件 。 如 果 创 建 了 Global.asax 文 件 , ASP.NET 会 将 其 编 译 为 从 HttpApplication 类 派 生 的 类 , 然 后 使 用 该 派 生 类 表 示 应 用 程 序 。



HttpApplication 进 程 的 一 个 实 例 每 次 只 处 理 一 个 请 求 。 由 于 在 访 问 应 用 程 序 类 中 的 非 静 态 成 员 时 不 需 要 将 其 锁 定 , 这 样 可 以 简 化 应 用 程 序 的 事 件 处 理 过 程 。



这 样 还 可 以 将 特 定 于 请 求 的 数 据 存 储 在 应 用 程 序 类 的 非 静 态 成 员 中 。例 如 ,可 以 在 Global.asax 文 件 中 定 义 一 个 属 性 ,然 后 为 该 属 性 赋 一 个 特 定 于 请 求 的 值 。





Application_Start 和 Application_End 方 法 是 不 表 示 HttpApplication 事 件 的 特 殊 方 法 。 应 用 程 序 域 的 生 命 周 期 期 间 ,ASP.NET 仅 调 用 这 些 方 法 一 次 ,



而 不 是 对 每 个 HttpApplication 实 例 都 调 用 一 次 。

104





108. 怎样理解 web 应用程序的“无状态编程”?

http 是 hyper text transfer protocol 的 缩 写 ,顾 名 思 义 ,这 个 协 议 支 持 着 超 文 本 的 传 输 。那 么 什 么 是 超 文 本 呢 ? 说 白 了 就 是 使 用 html 编 写 的 页 面 。通 常 ,



我 们 使 用 客 户 端 浏 览 器 访 问 服 务 器 的 资 源 , 最 常 见 的 url 也 是 以 html 为 后 缀 的 文 件 。 因 此 , 我 们 可 以 说 超 文 本 是 网 络 上 最 主 要 的 资 源 。



既 然 http 协 议 的 目 的 在 于 支 持 超 文 本 的 传 输 , 更 加 广 义 一 些 就 是 支 持 资 源 的 传 输 , 那 么 在 客 户 端 浏 览 器 向 http 服 务 器 发 送 请 求 , 继 而 http



服务器将相应 的资源发回给客户端这样一个过程中,无论对于客户端还是服务器,都没有必要记录这个过程,因为每一次请求和响应都是相对独立的,就好



像 你 在 自 动 售 货 机 前 投 下 硬 币 购 买 商 品 一 样 ,谁 都 不 会 也 不 需 要 记 住 这 样 一 个 交 易 过 程 。一 般 而 言 ,一 个 url 对 应 着 唯 一 的 超 文 本 ,而 http 服 务 器 也 绝 对 公



不 还 它 正 使

平 公 正 , 管 你 是 michael, 是 jordon, 都 会 根 据 接 收 到 的 url 请 求 返 回 相 同 的 超 文 本 。 是 因 为 这 样 的 唯 一 性 , 得 记 录 用 户 的 行 为 状 态 变 得 毫 无 意 义 ,



所 以 ,http 协 议 被 设 计 为 无 状 态 的 连 接 协 议 符 合 它 本 身 的 需 求 。 然 而 ,随 着 时 间 的 推 移 ,人 们 发 现 静 态 的 html 着 实 无 聊 而 乏 味 ,增 加 动 态 生 成 的 内 容 才 会



令 web 应 用 程 序 变 得 更 加 有 用 。 于 是 乎 , html 的 语 法 在 不 断 膨 胀 , 其 中 最 重 要 的 是 增 加 了 表 单 ( form) ; 客 户 端 也 增 加 了 诸 如 脚 本 处 理 、 dom 处 理 等 功 能 ;



对 于 服 务 器 , 则 相 应 的 出 现 了 cgi( common gateway interface) 以 处 理 包 含 表 单 提 交 在 内 的 动 态 请 求 。 在 这 种 客 户 端 与 服 务 器 进 行 动 态 交 互 的 web 应 用 程 序



出 现 之 后 , http 无 状 态 的 特 性 严 重 阻 碍 了 这 些 应 用 程 序 的 实 现 , 毕 竟 交 互 是 需 要 承 前 启 后 的 , 简 单 的 购 物 车 程 序 也 要 知 道 用 户 到 底 在 之 前 选 择 了 什 么 商 品 。



于 是 ,两 种 用 于 保 持 http 连 接 状 态 的 技 术 就 应 运 而 生 了 ,一 个 是 cookie,而 另 一 个 则 是 session。 cookie 是 通 过 客 户 端 保 持 状 态 的 解 决 方 案 。从 定 义 上 来



说 , cookie 就 是 由 服 务 器 发 给 客 户 端 的 特 殊 信 息 , 而 这 些 信 息 以 文 本 文 件 的 方 式 存 放 在 客 户 端 , 然 后 客 户 端 每 次 向 服 务 器 发 送 请 求 的 时 候 都 会 带 上 这 些 特 殊



的 信 息 。 让 我 们 说 得 更 具 体 一 些 : 当 用 户 使 用 浏 览 器 访 问 一 个 支 持 cookie 的 网 站 的 时 候 , 用 户 会 提 供 包 括 用 户 名 在 内 的 个 人 信 息 并 且 提 交 至 服 务 器 ; 接 着 ,



服 务 器 在 向 客 户 端 回 传 相 应 的 超 文 本 的 同 时 也 会 发 回 这 些 个 人 信 息 , 当 然 这 些 信 息 并 不 是 存 放 在 http 响 应 体 ( response body) 中 的 , 而 是 存 放 于 http 响 应



头 ( response header) ; 当 客 户 端 浏 览 器 接 收 到 来 自 服 务 器 的 响 应 之 后 , 浏 览 器 会 将 这 些 信 息 存 放 在 一 个 统 一 的 位 置 , 对 于 windows 操 作 系 统 而 言 , 我 们 可



以 从 : [系 统 盘 ]:\documents and settings\[用 户 名 ]\cookies 目 录 中 找 到 存 储 的 cookie; 自 此 , 客 户 端 再 向 服 务 器 发 送 请 求 的 时 候 , 都 会 把 相 应 的 cookie



再 次 发 回 至 服 务 器 。 而 这 次 , cookie 信 息 则 存 放 在 http 请 求 头 ( request header) 了 。



服 就

有 了 cookie 这 样 的 技 术 实 现 , 务 器 在 接 收 到 来 自 客 户 端 浏 览 器 的 请 求 之 后 , 能 够 通 过 分 析 存 放 于 请 求 头 的 cookie 得 到 客 户 端 特 有 的 信 息 ,



从而动态 生成与该客户端相对应的内容。通常,我们可以从很多网站的登录界面中看到 “请记住我”这样的选项,如果你勾选了它之后再登录,那么在下一



次 访 问 该 网 站 的 时 候 就 不 需 要 进 行 重 复 而 繁 琐 的 登 录 动 作 了 , 而 这 个 功 能 就 是 通 过 cookie 实 现 的 。



它 由 因

与 cookie 相 对 的 一 个 解 决 方 案 是 session, 是 通 过 服 务 器 来 保 持 状 态 的 。 于 session 这 个 词 汇 包 含 的 语 义 很 多 , 此 需 要 在 这 里 明 确 一 下 session



的 含 义 。首 先 ,我 们 通 常 都 会 把 session 翻 译 成 会 话 ,因 此 我 们 可 以 把 客 户 端 浏 览 器 与 服 务 器 之 间 一 系 列 交 互 的 动 作 称 为 一 个 session。从 这 个 语 义 出 发 ,我



们 会 提 到 session 持 续 的 时 间 ,会 提 到 在 session 过 程 中 进 行 了 什 么 操 作 等 等 ;其 次 ,session 指 的 是 服 务 器 端 为 客 户 端 所 开 辟 的 存 储 空 间 ,在 其 中 保 存 的 信



息 就 是 用 于 保 持 状 态 。 从 这 个 语 义 出 发 , 我 们 则 会 提 到 往 session 中 存 放 什 么 内 容 , 如 何 根 据 键 值 从 session 中 获 取 匹 配 的 内 容 等 。



要 使 用 session,第 一 步 当 然 是 创 建 session 了 。那 么 session 在 何 时 创 建 呢 ? 当 然 还 是 在 服 务 器 端 程 序 运 行 的 过 程 中 创 建 的 ,不 同 语 言 实 现 的 应



用 程 序 有 不 同 创 建 session 的 方 法 , 而 在 java 中 是 通 过 调 用 httpservletrequest 的 getsession 方 法 ( 使 用 true 作 为 参 数 ) 创 建 的 。 在 创 建 了 session 的



同 时 , 服 务 器 会 为 该 session 生 成 唯 一 的 session id, 而 这 个 session id 在 随 后 的 请 求 中 会 被 用 来 重 新 获 得 已 经 创 建 的 session; 在 session 被 创 建 之 后 ,



就 可 以 调 用 session 相 关 的 方 法 往 session 中 增 加 内 容 了 ,而 这 些 内 容 只 会 保 存 在 服 务 器 中 ,发 到 客 户 端 的 只 有 session id;当 客 户 端 再 次 发 送 请 求 的 时 候 ,



会 将 这 个 session id 带 上 , 服 务 器 接 受 到 请 求 之 后 就 会 依 据 session id 找 到 相 应 的 session, 从 而 再 次 使 用 之 。 正 式 这 样 一 个 过 程 , 用 户 的 状 态 也 就 得 以 保



持 了 。 有 关 session 的 内 容 还 比 较 多 , 在 以 后 的 post 中 , 我 还 将 继 续 讲 述 。



综 上 所 述 , http 本 身 是 一 个 无 状 态 的 连 接 协 议 , 为 了 支 持 客 户 端 与 服 务 器 之 间 的 交 互 , 我 们 就 需 要 通 过 不 同 的 技 术 为 交 互 存 储 状 态 , 而 这 些 不 同 的 技 术



就 是 cookie 和 session 了 。







109. 请你列举你最熟悉的.net 名称空间以及他们的主要内容.

.NET Framework 中的基本编程命名空间包括:





 System.Collections — 它包含的接口和类定义各种对象(如列表、队列、数组、哈希表和字典)集合。





 System.Collections.Generic -- 包含定义泛型集合的接口和类;泛型集合允许用户创建强类型的集合,这种集合在类型安全和性能上均优于非泛型强类型集合。





 System.IO — 它包含的类型用于支持数据流和文件的同步和异步读写。





 System.Text — 它包含表示 ASCII、Unicode、UTF-7 和 UTF-8 字符编码的类;用于来回转换字符块和字节块的抽象基类;以及在不创建 String 中间实例





的情况下操作和格式化 String 对象的帮助器类。







 System.Threading — 提供启用多线程编程的类和接口。此命名空间包括一个管理线程组的 ThreadPool 类,一个启用在指定时间后要调用的委托的 Timer 类,





以及一个同步互斥线程的 Mutex 类。System.Threading 还为线程调度和等待通知提供了相应的类。





.NET Framework 中的数据和 XML 命名空间包括:





 System.Data — 由构成 ADO.NET 结构的类组成,该结构是托管应用程序的主要数据访问方法。ADO.NET 结构使您可以生成可用于有效管理来自多个数据源的





数据的组件。ADO.NET 还提供对分布式应用程序中的数据进行请求、更新和协调的工具。

105





 System.Data.Common — 包含由 .NET Framework 数据提供程序共享的类。数据提供程序描述一个类的集合,这些类用于在托管空间中访问数据源,例如数





据库。







 System.Xml — 根据标准来支持 XML 处理的类。





 System.Data.SqlClient — 构成 SQL Server .NET Framework 数据提供程序的类,该提供程序允许您连接到 SQL Server 7.0、执行命令并读取结果。





System.Data.SqlClient 命名空间与 System.Data.OleDb 命名空间类似,但为访问 SQL Server 7.0 和更高版本进行了优化。





 System.Transactions -- 允许您编写自己的事务性应用程序和资源管理器的类。具体来说,您可以创建事务并和一个或多个参与者参与事务(本地或分布式







110. 您经常访问哪些有关.net 的网站?



111. .net framework 逻辑上分几个部分.



112. SQLSERVER 服务器中,给定表 table1 中有两个字段 ID、LastUpdateDate,ID 表示

更新的事务号,LastUpdateDate 表示更新时的服务器时间,请使用一句 SQL 语句获得最后

更新的事务号.(10)

Select ID



FROM table1



Where LastUpdateDate = (Select MAX(LastUpdateDate) FROM table1)







113. 简要谈一下您对微软.NET 构架下 remoting 和 webservice 两项技术的理解以及实际中

的应用.(10)

WS 主 要 是 可 利 用 HTTP, 穿 透 防 火 墙 .而 Remoting 可 以 利 用 TCP/IP, 二 进 制 传 送 提 高 效 率 .







114. 公司要求开发一个继承 System.Windows.Forms.ListView 类的组件,要求达到以下的

特殊功能:点击 ListView 各列列头时,能按照点击列的每行值进行重排视图中的所有行 (排

序的方式如 DataGrid 相似).根据您的知识,请简要谈一下您的思路:(10)

根 据 点 击 的 列 头 ,包 该 列 的 ID 取 出 ,按 照 该 ID 排 序 后 ,在 给 绑 定 到 ListView 中









115. 在.net 中,配件的意思是?

程 序 集 .( 中 间 语 言 , 源 数 据 , 资 源 , 装 配 清 单 )









116. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i>10 时是否会引起死锁?

并简要说明理由.

public void test(int i)



{



lock(this)



{



if (i>10)



{



i--;



test(i);



}



}



}



不 会 发 生 死 锁 , ( 但 有 一 点 int 是 按 值 传 递 的 , 所 以 每 次 改 变 的 都 只 是 一 个 副 本 , 因 此 不 会 出 现 死 锁 .但 如 果 把 int 换 做 一 个 object, 那 么 死 锁 会 发 生 )







117. 如何处理几十万条并发数据?

用 存 储 过 程 或 事 务 .取 得 最 大 标 识 的 时 候 同 时 更 新 ..注 意 主 键 不 是 自 增 量 方 式 这 种 方 法 并 发 的 时 候 是 不 会 有 重 复 主 键 的 ..取 得 最 大 标 识 要 有 一 个 存 储 过 程 来 获

106



取.







118. Session 相关问题

asp 中 Session 的 工 作 原 理 :



asp 的 Session 是 具 有 进 程 依 赖 性 的 。 ASP Session 状 态 存 于 IIS 的 进 程 中 , 也 就 是 inetinfo.exe 这 个 程 序 。 所 以 当 inetinfo.exe 进 程 崩 溃 时 , 这 些 信 息 也



就 丢 失 。 另 外 , 重 起 或 者 关 闭 IIS 服 务 都 会 造 成 信 息 的 丢 失 。







asp.net Session 的 实 现



asp.net 的 Session 是 基 于 HttpModule 技 术 做 的 , HttpModule 可 以 在 请 求 被 处 理 之 前 , 对 请 求 进 行 状 态 控 制 , 由 于 Session 本 身 就 是 用 来 做 状 态 维 护 的 , 因



此 用 HttpModule 做 Session 是 再 合 适 不 过 了 。







存储位置



InProc: session 在 服 务 器 中 以 活 动 对 象 方 式 存 储 ( aspnet_wp.exe)



StateServer: session 被 序 列 化 并 保 存 在 单 独 的 aspnet_state.exe 的 内 存 中 。 StateServer 能 够 运 行 在 另 一 台 服 务 器 上



SQLServer: session 被 序 列 化 并 保 存 在 SQL Server 中



性能:



InProc: 最 快 , 但 是 session 数 据 越 多 , web 服 务 器 上 消 耗 的 内 存 也 越 多 , 它 可 能 影 响 性 能 。



StateServer: 如 果 你 存 储 大 量 对 象 , 序 列 化 和 反 序 列 化 可 能 影 响 到 性 能



SQLServer: 它 也 有 与 StateServer 一 样 的 序 列 化 性 能 问 题 。



健壮性



InProc: 如 果 工 作 者 进 程 ( aspnet_wp.exe) 进 行 资 源 回 收 或 者 应 用 程 序 域 (appdomain)重 启 动 , session state 就 会 丢 失 。 这 是 因 为 session state 是 保 存 在



一 个 应 用 程 序 域 的 内 存 空 间 中 的 。 对 配 置 文 件 ( 如 web.config 和 machine.config) 的 修 改 或 者 \bin 目 录 的 任 何 改 变 ( 例 如 在 你 使 用 VS 编 译 应 用 程 序 后 产 生



了 一 个 新 的 dll) 都 可 能 引 起 重 启 动 。



StateServer: 解 决 了 InProc 模 式 的 session state 丢 失 问 题 。 允 许 一 个 webfarm 在 中 央 服 务 器 中 存 储 session。 只 能 在 State Server 上 出 现 失 败 。



SQLServer: 与 StateServer 相 似 。 session state 的 数 据 在 SQL Server 重 启 后 仍 然 保 留 着 。



警告



InProc: 它 不 能 在 web garden 模 式 下 工 作 , 因 为 在 这 个 模 式 下 会 有 多 个 aspnet_wp.exe 在 同 一 台 机 器 上 运 行 。 建 议 在 使 用 web garden 使 切 换 到 State Server



或 SQL Server。 仅 在 InProc 模 式 下 支 持 Session_End 事 件 。







Session 丢 失 原 因



原 因 1: bin 目 录 中 的 文 件 被 改 写 , asp.net 有 一 种 机 制 , 为 了 保 证 dll 重 新 编 译 之 后 , 系 统 正 常 运 行 , 它 会 重 新 启 动 一 次 网 站 进 程 , 这 时 就 会 导 致 Session



丢 失 , 所 以 如 果 有 access 数 据 库 位 于 bin 目 录 , 或 者 有 其 他 文 件 被 系 统 改 写 , 就 会 导 致 Session 丢 失



文 如 一 系 而

原 因 2: 件 夹 选 项 中 , 果 没 有 打 开 “ 在 单 独 的 进 程 中 打 开 文 件 夹 窗 口 ” , 旦 新 建 一 个 窗 口 , 统 可 能 认 为 是 新 的 Session 会 话 , 无 法 访 问 原 来 的 Session,



所 以 需 要 打 开 该 选 项 , 否 则 会 导 致 Session 丢 失



原 因 3: 似 乎 大 部 分 的 Session 丢 失 是 客 户 端 引 起 的 , 所 以 要 从 客 户 端 下 手 , 看 看 cookie 有 没 有 打 开



原 因 4: Session 的 时 间 设 置 是 不 是 有 问 题 , 会 不 会 因 为 超 时 造 成 丢 失



原 因 5: IE 中 的 cookie 数 量 限 制 ( 每 个 域 20 个 cookie) 可 能 导 致 session 丢 失



原 因 6: 使 用 web garden 模 式 , 且 使 用 了 InProc mode 作 为 保 存 session 的 方 式







解决丢失的经验



1. 判 断 是 不 是 原 因 1 造 成 的 , 可 以 在 每 次 刷 新 页 面 的 时 候 , 跟 踪 bin 中 某 个 文 件 的 修 改 时 间



2. 做 Session 读 写 日 志 ,每 次 读 写 Session 都 要 记 录 下 来 ,并 且 要 记 录 SessionID、Session 值 、所 在 页 面 、当 前 函 数 、函 数 中 的 第 几 次 Session 操 作 ,这 样



找丢失的原因会方便很多



3. 如 果 允 许 的 话 , 建 议 使 用 state server 或 sql server 保 存 session, 这 样 不 容 易 丢 失



4. 在 global.asa 中 加 入 代 码 记 录 Session 的 创 建 时 间 和 结 束 时 间 , 超 时 造 成 的 Session 丢 失 是 可 以 在 SessionEnd 中 记 录 下 来 的 。



5. 如 果 有 些 代 码 中 使 用 客 户 端 脚 本 , 如 javascript 维 护 Session 状 态 , 就 要 尝 试 调 试 脚 本 , 是 不 是 因 为 脚 本 错 误 引 起 Session 丢 失







119. 堆和栈的区别?

堆 ( Heap) 栈 ( Stack)



1、 内 存 分 配 方 面 :

107



堆 :一 般 由 程 序 员 分 配 释 放 , 若 程 序 员 不 释 放 ,程 序 结 束 时 可 能 由 OS 回 收 。注 意 它 与 数 据 结 构 中 的 堆 是 两 回 事 ,分 配 方 式 是 类 似 于 链 表 。可 能 用 到



的 关 键 字 如 下 : new、 malloc、 delete、 free 等 等 。



栈 : 由 编 译 器 (Compiler)自 动 分 配 释 放 , 存 放 函 数 的 参 数 值 , 局 部 变 量 的 值 等 。 其 操 作 方 式 类 似 于 数 据 结 构 中 的 栈 。



2、 申 请 方 式 方 面 :



堆 :需 要 程 序 员 自 己 申 请 ,并 指 明 大 小 。在 c 中 malloc 函 数 如 p1 = (char *)malloc(10);在 C++中 用 new 运 算 符 ,但 是 注 意 p1、p2 本 身 是 在 栈 中 的 。



因为他们还是可以认为是局部变量。



栈 : 由 系 统 自 动 分 配 。 例 如 , 声 明 在 函 数 中 一 个 局 部 变 量 int b; 系 统 自 动 在 栈 中 为 b 开 辟 空 间 。



3、 系 统 响 应 方 面 :



堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点



从 空 闲 结 点 链 表 中 删 除 ,并 将 该 结 点 的 空 间 分 配 给 程 序 ,另 外 ,对 于 大 多 数 系 统 ,会 在 这 块 内 存 空 间 中 的 首 地 址 处 记 录 本 次 分 配 的 大 小 ,这 样 代 码 中 的 delete



语句才能正确的释放本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。



栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。



4、 大 小 限 制 方 面 :



堆:是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由



低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。



栈 : 在 Windows 下 , 栈 是 向 低 地 址 扩 展 的 数 据 结 构 , 是 一 块 连 续 的 内 存 的 区 域 。 这 句 话 的 意 思 是 栈 顶 的 地 址 和 栈 的 最 大 容 量 是 系 统 预 先 规 定 好 的 , 在



WINDOWS 下 , 栈 的 大 小 是 固 定 的 ( 是 一 个 编 译 时 就 确 定 的 常 数 ) , 如 果 申 请 的 空 间 超 过 栈 的 剩 余 空 间 时 , 将 提 示 overflow。 因 此 , 能 从 栈 获 得 的 空 间 较 小 。



5、 效 率 方 面 :



堆 : 是 由 new 分 配 的 内 存 , 一 般 速 度 比 较 慢 , 而 且 容 易 产 生 内 存 碎 片 , 不 过 用 起 来 最 方 便 , 另 外 , 在 WINDOWS 下 , 最 好 的 方 式 是 用 VirtualAlloc 分 配



内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存 ,虽然用起来最不方便。但是速度快,也最灵活。



栈:由系统自动分配,速度较快。但程序员是无法控制的。



6、 存 放 内 容 方 面 :



堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。



栈:在函数调用时第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址然后是函数的各个参数,在大多数的 C 编译



器中,参数是由右往左入栈,然后是函数中的局部变量。 注意: 静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指



针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。



7、 存 取 效 率 方 面 :



堆 : char *s1 = "Hellow Word"; 是 在 编 译 时 就 确 定 的 ;



栈 : char s1[] = "Hellow Word"; 是 在 运 行 时 赋 值 的 ; 用 数 组 比 用 指 针 速 度 要 快 一 些 , 因 为 指 针 在 底 层 汇 编 中 需 要 用 edx 寄 存 器 中 转 一 下 , 而 数 组



在栈上直接读取。







120. 成员变量和成员函数前加 static 的作用?

它 们 被 称 为 常 成 员 变 量 和 常 成 员 函 数 ,又 称 为 类 成 员 变 量 和 类 成 员 函 数 .分 别 用 来 反 映 类 的 状 态 .比 如 类 成 员 变 量 可 以 用 来 统 计 类 实 例 的 数 量 ,类 成 员 函 数 负 责



这种统计的动作.







121. DataReader 与 Dataset 有什么区别?

一 个 是 只 能 向 前 的 只 读 游 标 , 一 个 是 内 存 中 的 表 . DataReader 和 DataSet 最 大 的 区 别 在 于 ,DataReader 使 用 时 始 终 占 用 SqlConnection,在 线 操 作 数 据 库 ..任 何 对



SqlConnection 的 操 作 都 会 引 发 DataReader 的 异 常 ..因 为 DataReader 每 次 只 在 内 存 中 加 载 一 条 数 据 ,所 以 占 用 的 内 存 是 很 小 的 ..因 为 DataReader 的 特 殊 性 和



高 性 能 .所 以 DataReader 是 只 进 的 ..你 读 了 第 一 条 后 就 不 能 再 去 读 取 第 一 条 了 ..



DataSet 则 是 将 数 据 一 次 性 加 载 在 内 存 中 .抛 弃 数 据 库 连 接 ..读 取 完 毕 即 放 弃 数 据 库 连 接 ..因 为 DataSet 将 数 据 全 部 加 载 在 内 存 中 .所 以 比 较 消 耗 内 存 ...但 是



确 比 DataReader 要 灵 活 ..可 以 动 态 的 添 加 行 ,列 ,数 据 .对 数 据 库 进 行 回 传 更 新 操 作







122. 什么是 SOAP,有哪些应用.

SOAP( Simple Object Access Protocol ) 简 单 对 象 访 问 协 议 是 在 分 散 或 分 布 式 的 环 境 中 交 换 信 息 并 执 行 远 程 过 程 调 用 的 协 议 , 是 一 个 基 于 XML 的 协 议 .使 用



SOAP,不 用 考 虑 任 何 特 定 的 传 输 协 议( 最 常 用 的 还 是 HTTP 协 议 ),可 以 允 许 任 何 类 型 的 对 象 或 代 码 ,在 任 何 平 台 上 ,以 任 何 一 直 语 言 相 互 通 信 .这 种 相 互 通 信



采 用 的 是 XML 格 式 的 消 息 . SOAP 也 被 称 作 XMLP, 为 两 个 程 序 交 换 信 息 提 供 了 一 种 标 准 的 工 作 机 制 .在 各 类 机 构 之 间 通 过 电 子 方 式 相 互 协 作 的 情 况 下 完 全 有 必 要



为 此 制 定 相 应 的 标 准 . SOAP 描 述 了 把 消 息 捆 绑 为 XML 的 工 作 方 式 .它 还 说 明 了 发 送 消 息 的 发 送 方 、 消 息 的 内 容 和 地 址 以 及 发 送 消 息 的 时 间 .SOAP 是 Web Service



( ,怎 ( .大

的 基 本 通 信 协 议 .SOAP 规 范 还 定 义 了 怎 样 用 XML 来 描 述 程 序 数 据 Program Data) 样 执 行 RPC Remote Procedure Call) 多 数 SOAP 解 决 方 案 都 支 持 RPC-style



应 用 程 序 .SOAP 还 支 持 Document-style 应 用 程 序 ( SOAP 消 息 只 包 含 XML 文 本 信 息 ) .最 后 SOAP 规 范 还 定 义 了 HTTP 消 息 是 怎 样 传 输 SOAP 消 息 的 .MSMQ、 SMTP、



TCP/IP 都 可 以 做 SOAP 的 传 输 协 议 . SOAP 是 一 种 轻 量 级 协 议 , 用 于 在 分 散 型 、 分 布 式 环 境 中 交 换 结 构 化 信 息 . SOAP 利 用 XML 技 术 定 义 一 种 可 扩 展 的 消 息 处

108



理框架,它提供了一种可通过多种底层协议进行交换的消息结构. 这种框架的设计思想是要独立于任何一种特定的编程模型和其他特定实现的语义 .



SOAP 定 义 了 一 种 方 法 以 便 将 XML 消 息 从 A 点 传 送 到 B 点 . 为 此 ,它 提 供 了 一 种 基 于 XML 且 具 有 以 下 特 性 的 消 息 处 理 框 架 : 1) 可 扩 展 , 2) 可 通 过 多 种



底 层 网 络 协 议 使 用 , 3) 独 立 于 编 程 模 型 .







123. XML 与 HTML 的主要区别

1. XML 是 区 分 大 小 写 字 母 的 , HTML 不 区 分 .



2. 在 HTML 中 , 如 果 上 下 文 清 楚 地 显 示 出 段 落 或 者 列 表 键 在 何 处 结 尾 , 那 么 你 可 以 省 略 或 者 之



类 的 结 束 标 记 .在 XML 中 , 绝 对 不 能 省 略 掉 结 束 标 记 .



3. 在 XML 中 , 拥 有 单 个 标 记 而 没 有 匹 配 的 结 束 标 记 的 元 素 必 须 用 一 个 / 字 符 作 为 结 尾 .这 样 分 析 器 就 知



道不用 查找结束标记了.



4. 在 XML 中 , 属 性 值 必 须 分 装 在 引 号 中 .在 HTML 中 , 引 号 是 可 用 可 不 用 的 .



5. 在 HTML 中 , 可 以 拥 有 不 带 值 的 属 性 名 .在 XML 中 , 所 有 的 属 性 都 必 须 带 有 相 应 的 值 .







124. .net 的错误处理机制

在 ASP.NET 中 ,错 误 处 理 分 为 两 个 层 面 :页 面 输 入 信 息 验 证 、CLR 提 供 的 结 构 化 错 误 处 理 机 制 。在 页 面 输 入 信 息 判 断 中 ,可 以 采 用 JavaScript、VBScript 等 脚



本 进 行 判 断 ,也 可 以 使 用 .NET 环 境 下 带 有 的 Validation 和 正 则 表 达 式 的 方 式 将 信 息 在 提 交 执 行 处 理 前 ,进 行 格 式 判 断 。格 式 判 断 包 括 :网 络 地 址 、电 子 信 箱 、



输入字符串长短和输入信息的数据类型的判断等。



结 构 化 异 常 处 理 是 CLR 的 基 础 部 分 ,具 有 以 下 特 点 :可 以 跨 语 言 ,异 常 可 以 在 一 种 语 言 中 引 发 ,在 另 外 一 种 语 言 中 捕 捉 处 理 ;分 层 处 理 ,一 个 异 常 可 以



包 括 另 外 一 个 异 常 ,这 意 味 着 系 统 可 以 捕 获 底 层 对 象( 如 数 据 层 和 业 务 层 )的 异 常 ,引 发 自 己 的 异 常 ,包 含 有 底 层 对 象 的 异 常 。这 样 可 以 将 异 常 进 行 细 致 分 类 ,



使得异常更容易处理。



结构化异常处理,一般要使用 3 部分代码。



( 1) Try: 是 程 序 中 可 能 出 现 错 误 的 操 作 部 分 。



( 2) Catch: 是 用 来 处 理 各 种 错 误 的 部 分 ( 可 以 有 多 个 ) 。 必 须 正 确 排 列 捕 获 异 常 的 Catch 子 句 , 范 围 小 的 Exception 放 在 前 面 的 Catch。 即 如 果 Exception



之 间 存 在 继 承 关 系 , 就 应 把 子 类 的 Exception 放 在 前 面 的 Catch 子 句 中 。



( 3) Finally 块 的 代 码 用 来 清 理 资 源 或 执 行 要 在 Try 块 末 尾 执 行 的 其 他 操 作 ( 可 以 省 略 ) 。 无 论 是 否 产 生 异 常 , Finally 块 都 会 执 行 。



结构化异常处理



设 所 将

异 常 是 在 程 序 设 计 中 无 法 避 免 的 错 误 , 计 的 程 序 必 须 能 够 处 理 所 有 可 能 出 现 的 错 误 。 以 要 全 面 考 虑 异 常 处 理 , 一 切 可 能 出 现 异 常 的 代 码 都 进 行 try



的捕捉,然后建立自己的异常处理机制,按照不同的异常情况进行分类处理。



异 常 产 生 的 时 候 ,需 要 知 道 是 什 么 原 因 造 成 的 错 误 以 及 错 误 的 相 关 信 息 。根 据 实 际 情 况 抛 出 具 体 类 型 的 异 常 ,然 后 建 立 捕 捉 机 制 ,捕 捉 到 异 常 时 做 出 具



体的处理。在编写代码过程中,可以使用系统已定义的相关异常类以及自定义的异常类来实例化并抛出需要的异常。如一个不可能实现的接口,可以抛出



System.NotSupportedExceptiion 的 异 常 来 告 诉 接 口 的 调 用 者 。



在 处 理 异 常 的 时 候 ,应 该 将 可 处 理 的 具 体 异 常 分 别 在 catch 块 中 作 出 相 应 处 理 ,否 则 程 序 将 终 止 运 行 。针 对 每 一 种 异 常 ,以 不 同 方 式 处 理 ,避 免 对 所 有



异常做出一样的处理。并且在异常产生时,给用户一个友好的提示(最终用户对 系统异常的具体内容是不明白的,这就 需 要 给 出 相关 的 信 息 提 示和 解 决 方案 ,



或 告 诉 联 系 管 理 员 等 ), 并 在 可 能 的 情 况 下 给 用 户 提 供 选 择 ( 确 定 和 取 消 ), 让 用 户 来 决 定 系 统 的 运 行 方 向 。 同 时 , 程 序 中 要 将 异 常 做 日 志 记 录 。 不 是 所 有 异



常都是必须记录的,例如一些可捕捉并且已经安排程序进行处理的异常就不需要记录它。







可以采取如下形式记录异常:



在文件中记录异常。便于技术人员查看所发生的异常,从而日后对程序进行改进。



在数据库中记录异常。数据库支持查询,这样在后期就能够对异常进行分类查询等操作,便于查看与管理。



在 Eventlog 中 记 录 异 常 。 能 够 远 程 操 作 , 方 便 系 统 管 理 员 监 控 所 有 计 算 机 的 异 常 。



除 了 可 预 料 到 的 异 常 外 ,还 有 未 预 料 到 的 异 常 。这 类 异 常 是 无 法 进 行 管 理 的 ,发 生 后 将 系 统 程 序 转 移 到 特 定 的 错 误 页 面 ,提 示 用 户 系 统 出 错 ,结 束 程 序 。做 好



日 志 能 为 解 决 和 调 试 问 题 带 来 很 多 方 便 。 要 避 免 使 用 了 Try-Catch 但 没 有 处 理 异 常 的 情 况 , 否 则 就 相 当 于 给 异 常 放 行 (这 种 情 况 还 不 如 根 本 不 去 捕 获 )。



异 常 处 理 , 还 应 该 注 意 在 Finally 块 中 释 放 相 关 资 源 、 还 原 相 关 设 置 信 息 等 收 尾 工 作 。 基 本 程 序 结 构 如 下 :



try



{



int x = 123/0; //会 抛 出 一 个 除 数 为 0 的 异 常



}



catch



{



// ... //在 这 里 捕 捉 到 这 个 异 常 , 然 后 进 行 相 关 处 理

109



}



finally



{



// ... //在 这 里 做 最 后 的 清 理 或 是 其 他 工 作



}



但 给 并

本 章 的 异 常 处 理 是 针 对 容 易 出 现 错 误 的 地 方 (操 作 数 据 库 )应 用 此 技 术 , 是 只 是 捕 捉 到 SQL 异 常 后 , 用 户 一 个 错 误 提 示 , 没 有 进 一 步 作 程 序 的 处 理 。







C#中 常 见 异 常 类



Exception: 所 有 异 常 对 象 的 基 类 。



SystemException: 运 行 时 产 生 的 所 有 错 误 的 基 类 。



IndexOutOfRangeException: 当 一 个 数 组 的 下 标 超 出 范 围 时 运 行 时 引 发 。



NullReferenceException: 当 一 个 空 对 象 被 引 用 时 运 行 时 引 发 。



InvalidOperationException: 当 对 方 法 的 调 用 对 对 象 的 当 前 状 态 无 效 时 , 由 某 些 方 法 引 发 。



ArgumentException: 所 有 参 数 异 常 的 基 类 。



ArgumentNullException: 在 参 数 为 空 (不 允 许 )的 情 况 下 , 由 方 法 引 发 。



ArgumentOutOfRangeException: 当 参 数 不 在 一 个 给 定 范 围 之 内 时 , 由 方 法 引 发 。



InteropException: 目 标 在 或 发 生 在 CLR 外 面 环 境 中 的 异 常 的 基 类 。



ComException: 包 含 COM 类 的 HRESULT 信 息 的 异 常 。



SEHException: 封 装 Win32 结 构 异 常 处 理 信 息 的 异 常 。



SqlException: 封 装 了 SQL 操 作 异 常 。







125. 利用 operator 声明且仅声明了==,有什么错误么?

要 同 时 修 改 Equale 和 GetHash() ? 重 载 了 "==" 就 必 须 重 载 "!="









126. ADO.NET 相对于 ADO 等主要有什么改进?

1:ado.net 不 依 赖 于 ole db 提 供 程 序 ,而 是 使 用 .net 托 管 提 供 的 程 序 ;



2:不 使 用 com



3:不 在 支 持 动 态 游 标 和 服 务 器 端 ;



4:可 以 断 开 connection 而 保 留 当 前 数 据 集 可 用



5:强 类 型 转 换



6:xml 支 持







127. 写一个 HTML 页面,实现以下功能,左键点击页面时显示“您好”,右键点击时显示“禁

止右键”并在 2 分钟后自动关闭页面.





setTimeout('window.close();',3000);



function show()



{



if (window.event.button == 1)



{



alert("左 ");



}



else if (window.event.button == 2)



{



alert("右 ");



}



}





110





128. 大概描述一下 ASP.NET 服务器控件的生命周期

初始化 加载视图状态 处理回发数据 加载 发送回发更改通知 处理回发事件 预呈现 保存状态呈现处置 卸载







129. 和 有什么区别?

表 示 绑 定 的 数 据 源



是 服 务 器 端 代 码 块







130. UML 基本概念

用 例 图 ( use case diagram) 就 是 由 主 角 、 用 例 以 及 它 们 之 间 的 关 系 构 成 的 图 。 该 图 说 明 了 用 例 模 型 中 的 关 系 。



可以将用例图组织到用例包中,并归用例包所有,让特定包中仅显示互为关联关系的内容。



用 例 图 由 参 与 者 ( Actor) 、 用 例 ( Use Case) 、 系 统 边 界 、 箭 头 组 成 , 用 画 图 的 方 法 来 完 成 。



参 与 者 不 是 特 指 人 ,是 指 系 统 以 外 的 ,在 使 用 系 统 或 与 系 统 交 互 中 所 扮 演 的 角 色 。因 此 参 与 者 可 以 是 人 ,可 以 是 事 物 ,也 可 以 是 时 间 或 其 他 系 统等



等。还有一点要注意的是,参与者不是指人或事物本身,而是表示人或事物当时所扮演的角色。比如小明是图书馆的管 理 员 , 他 参与 图 书 馆 管 理系 统 的 交互 ,



这 时 他 既 可 以 作 为 管 理 员 这 个 角 色 参 与 管 理 ,也 可 以 作 为 借 书 者 向 图 书 馆 借 书 ,在 这 里 小 明 扮 演 了 两 个 角 色 ,是 两 个 不 同 的 参 与 者 。参 与 者 在 画 图 中 用 简 笔 人



物画来表示,人物下面附上参与者的名称。



用 例 是 对 包 括 变 量 在 内 的 一 组 动 作 序 列 的 描 述 ,系 统 执 行 这 些 动 作 ,并 产 生 传 递 特 定 参 与 者 的 价 值 的 可 观 察 结 果 。这 是 UML 对 用 例 的 正 式 定 义 ,对



我 们 初 学 者 可 能 有 点 难 懂 。我 们 可 以 这 样 去 理 解 ,用 例 是 参 与 者 想 要 系 统 做 的 事 情 。对 于 对 用 例 的 命 名 ,我 们 可 以 给 用 例 取 一 个 简 单 、描 述 性 的 名 称 ,一 般 为



带有动作性的词。用例在画图中用椭圆来表示,椭圆下面附上用例的名称。



系 统 边 界 是 用 来 表 示 正 在 建 模 系 统 的 边 界 。边 界 内 表 示 系 统 的 组 成 部 分 ,边 界 外 表 示 系 统 外 部 。系 统 边 界 在 画 图 中 方 框 来 表 示 ,同 时 附 上 系 统 的 名



称,参与者画在边界的外面,用例画在边界里面。因为系统边界的作用有时候不是很明显,所以我个人理解,在画图时可省略。



箭头用来表示参与者和系统通过相互发送信号或 消息进行交互的关联关系。箭头尾部用来表示启动交互的一方,箭头头部用来表示被启动的一方 ,



其中用例总是要由参与者来启动。







131. 绑定表达式与的区别

1.Bind 是 可 更 新 的 , 是 Bind 绑 定 列 可 编 辑 , 并 且 可 以 和 数 据 源 控 件 交 互 , 直 接 和 数 据 库 交 互 , 但 是 用 Bind 的 话 , 程 序 端 的 自 定 义 操 作 就 不 能 用 了 , 比 如



Convert,ToString()等 ,或 自 己 写 的 函 数 ,在 程 序 端 都 不 可 以



2.eval 是 只 读 数 据 , 用 eval 的 话 不 可 以 和 数 据 源 控 件 交 互 , 是 单 向 的 , 但 是 可 以 自 定 义 操 作



比 如 上 面 的







132. 页面重定 Response.Redirect(),Server.Transfer(),Server.Execute()

Response.Redirect 方 法 导 致 浏 览 器 链 接 到 一 个 指 定 的 URL。当 Response.Redirect()方 法 被 调 用 时 ,它 会 创 建 一 个 应 答 ,应 答 头 中 指 出 了 状 态 代 码 302( 表 示



目 标 已 经 改 变 )以 及 新 的 目 标 URL。浏 览 器 从 服 务 器 收 到 该 应 答 ,利 用 应 答 头 中 的 信 息 发 出 一 个 对 新 URL 的 请 求 。 这 就 是 说 ,使 用 Response.Redirect 方 法



时 重 定 向 操 作 发 生 在 客 户 端 ,总 共 涉 及 到 两 次 与 服 务 器 的 通 信( 两 个 来 回 ):第 一 次 是 对 原 始 页 面 的 请 求 ,得 到 一 个 302 应 答 ,第 二 次 是 请 求 302 应 答 中 声 明



的新页面,得到重定向之后的页面。



Server.Transfer 方 法 把 执 行 流 程 从 当 前 的 ASPX 文 件 转 到 同 一 服 务 器 上 的 另 一 个 ASPX 页 面 。调 用 Server.Transfer 时 ,当 前 的 ASPX 页 面 终 止 执 行 ,执 行 流 程



转 入 另 一 个 ASPX 页 面 , 但 新 的 ASPX 页 面 仍 使 用 前 一 ASPX 页 面 创 建 的 应 答 流 。 如 果 用 Server.Transfer 方 法 实 现 页 面 之 间 的 导 航 , 浏 览 器 中 的 URL 不 会 改



变 , 因 为 重 定 向 完 全 在 服 务 器 端 进 行 , 浏 览 器 根 本 不 知 道 服 务 器 已 经 执 行 了 一 次 页 面 变 换 。 默 认 情 况 下 , Server.Transfer 方 法 不 会 把 表 单 数 据 或 查 询 字 符 串



从 一 个 页 面 传 递 到 另 一 个 页 面 , 但 只 要 把 该 方 法 的 第 二 个 参 数 设 置 成 True, 就 可 以 保 留 第 一 个 页 面 的 表 单 数 据 和 查 询 字 符 串 。 同 时 , 使 用 Server.Transfer



时 应 注 意 一 点 : 目 标 页 面 将 使 用 原 始 页 面 创 建 的 应 答 流 , 这 导 致 ASP.NET 的 机 器 验 证 检 查 ( Machine Authentication Check, MAC) 认 为 新 页 面 的 ViewState



已 被 篡 改 。 因 此 , 如 果 要 保 留 原 始 页 面 的 表 单 数 据 和 查 询 字 符 串 集 合 , 必 须 把 目 标 页 面 Page 指 令 的 EnableViewStateMac 属 性 设 置 成 False。



Server.Execute 方 法 允 许 当 前 的 ASPX 页 面 执 行 一 个 同 一 Web 服 务 器 上 的 指 定 ASPX 页 面 , 当 指 定 的 ASPX 页 面 执 行 完 毕 , 控 制 流 程 重 新 返 回 原 页 面 发 出





Server.Execute 调 用 的 位 置 。 这 种 页 面 导 航 方 式 类 似 于 针 对 ASPX 页 面 的 一 次 函 数 调 用 , 调 用 的 页 面 能 够 访 问 发 出 调 用 页 面 的 表 单 数 据 和 查 询 字 符 串 集 合 ,



所 以 要 把 被 调 用 页 面 Page 指 令 的 EnableViewStateMac 属 性 设 置 成 False。默 认 情 况 下 ,被 调 用 页 面 的 输 出 追 加 到 当 前 应 答 流 。但 是 ,Server.Execute 方 法 有



一 个 重 载 的 方 法 ,允 许 通 过 一 个 TextWriter 对 象( 或 者 它 的 子 对 象 ,例 如 StringWriter 对 象 )获 取 被 调 用 页 面 的 输 出 ,而 不 是 直 接 追 加 到 输 出 流 ,这 样 ,在



原始页面中可以方便地调整被调用页面输出结果的位置。







133. 页面间传值的几种方式

1. 使 用 QueryString 变 量



QueryString 是 一 种 非 常 简 单 的 传 值 方 式 , 他 可 以 将 传 送 的 值 显 示 在 浏 览 器 的 地 址 栏 中 。 如 果 是 传 递 一 个 或 多 个 安 全 性 要 求 不 高 或 是 结 构 简 单 的 数 值



时,可以使用这个方法。但是对于传递数组或对象的话,就不能用这个方法了。下面是一个例子:



a.aspx 的 C#代 码

111



private void Button1_Click(object sender, System.EventArgs e)



{



string s_url;



s_url = "b.aspx?name=" + Label1.Text;



Response.Redirect(s_url);



}







b.aspx 中 C#代 码



private void Page_Load(object sender, EventArgs e)



{



Label2.Text = Request.QueryString["name"];



}







2. 使 用 Application 对 象 变 量



Application 对 象 的 作 用 范 围 是 整 个 全 局 , 也 就 是 说 对 所 有 用 户 都 有 效 。 其 常 用 的 方 法 用 Lock 和 UnLock。



a.aspx 的 C#代 码



private void Button1_Click(object sender, System.EventArgs e)



{



Application["name"] = Label1.Text;



Server.Transfer("b.aspx");



}







b.aspx 中 C#代 码



private void Page_Load(object sender, EventArgs e)



{



string name;



Application.Lock();



name = Application["name"].ToString();



Application.UnLock();



}







3. 使 用 Session 变 量



想 必 这 个 肯 定 是 大 家 使 用 中 最 常 见 的 用 法 了 , 其 操 作 与 Application 类 似 , 作 用 于 用 户 个 人 , 所 以 , 过 量 的 存 储 会 导 致 服 务 器 内 存 资 源 的 耗 尽 。



a.aspx 的 C#代 码



private void Button1_Click(object sender, System.EventArgs e)



{



Session["name"] = Label.Text;



}







b.aspx 中 C#代 码



private void Page_Load(object sender, EventArgs e)



{



string name;



name = Session["name"].ToString();



}







4. 使 用 Cookie 对 象 变 量



这 个 也 是 大 家 常 使 用 的 方 法 , 与 Session 一 样 , 其 是 什 对 每 一 个 用 户 而 言 的 , 但 是 有 个 本 质 的 区 别 , 即 Cookie 是 存 放 在 客 户 端 的 , 而 session 是 存



放 在 服 务 器 端 的 。 而 且 Cookie 的 使 用 要 配 合 ASP.NET 内 置 对 象 Request 来 使 用 。

112







a.aspx 的 C#代 码



private void Button1_Click(object sender, System.EventArgs e)



{



HttpCookie cookie_name = new HttpCookie("name");



cookie_name.Value = Label1.Text;



Reponse.AppendCookie(cookie_name);



Server.Transfer("b.aspx");



}







b.aspx 中 C#代 码



private void Page_Load(object sender, EventArgs e)



{



string name;



name = Request.Cookie["name"].Value.ToString();



}







5. 使 用 Server.Transfer 方 法



这 个 才 可 以 说 是 面 象 对 象 开 发 所 使 用 的 方 法 ,其 使 用 Server.Transfer 方 法 把 流 程 从 当 前 页 面 引 导 到 另 一 个 页 面 中 ,新 的 页 面 使 用 前 一 个 页 面 的 应 答



流,所以这个方法是完全面象对象的,简洁有效。



a.aspx 的 C#代 码



public string Name



{



get{ return Label1.Text;}



}



private void Button1_Click(object sender, System.EventArgs e)



{



Server.Transfer("b.aspx");



}







b.aspx 中 C#代 码



private void Page_Load(object sender, EventArgs e)



{



a newWeb; //实 例 a 窗 体



newWeb = (source)Context.Handler;



string name;



name = newWeb.Name;



}







134. IList、ICollection、IEnumerable 之辨析

祖 宗 :IEnumerable



此 接 口 只 有 一 个 方 法 GetEnumerator();



是 FrameWork 为 了 实 现 迭 代 器 模 式 设 计 的 接 口 。所 有 继 承 了 IEnumerable 的 类 ,要 使 用 foreach 迭 代 器 时 ,就 需 要 使 用 该 方 法 。因 此 也 只 有 实 现 了 该 接 口 的 类



才 可 以 使 用 foreach。



ICollection 继 承 自 IEnumerable, IList 继 承 自 ICollection



这 两 个 接 口 都 是 为 了 给 集 合 提 供 一 些 公 用 的 方 法 。 只 是 分 了 两 个 层 次 , IList 比 ICollection 多 几 个 方 法 , 增 加 , 移 除 成 员 。 可 以 简 单 理 解 为 : ICollection



主 要 针 对 静 态 集 合 ; IList 主 要 针 对 动 态 集 合 。



IList,ICollection,IEnumerable 在 命 名 空 间 System.Collections 中 。



IList,ICollection,IEnumerable 在 System.Collections.Generic 命 名 空 间 中 。

113



IList,ICollection,IEnumerab le 是 2.0 引 入 泛 型 以 后 新 增 的 。 主 要 是 提 高 重 用 性 与 类 型 安 全 。



IEnumerable继 承 自 IEnumerable



ICollection继 承 自 IEnumerable



IList继 承 自 ICollection



因 此 可 以 完 全 使 用 泛 型 接 口 , 而 放 弃 使 用 ICollection 和 IList。 泛 型 接 口 提 供 了 更 好 的 类 型 安 全 和 编 译 时 的 检 验 。



补充:



IEnumerable和 IEnumerable 都 只 有 一 个 方 法 。ICollection和 ICollection 的 结 构 是 不 一 样 的 。ICollection比 ICollection 多 几 个 方 法 。它 包 含 了



几 个 IList 中 的 几 个 方 法 。 也 许 是 对 以 前 的 改 进 。









135. IEnumerable 与 IEnumerator 区别

public interface IEnumerable



{



IEnumerator GetEnumerator();



}







public interface IEnumerator



{



bool MoveNext();



void Reset();







Object Current { get; }



}







IEnumerable 和 IEnumerator 有 什 么 区 别 ? 这 是 一 个 很 让 人 困 惑 的 问 题 ( 在 很 多 forum 里 都 看 到 有 人 在 问 这 个 问 题 ) 。 研 究 了 半 天 , 得 到 以 下 几 点 认 识 :



1、 一 个 Collection 要 支 持 foreach 方 式 的 遍 历 , 必 须 实 现 IEnumerable 接 口 ( 亦 即 , 必 须 以 某 种 方 式 返 回 IEnumerator object) 。



2、 IEnumerator object 具 体 实 现 了 iterator( 通 过 MoveNext(), Reset(), Current) 。



3、从 这 两 个 接 口 的 用 词 选 择 上 ,也 可 以 看 出 其 不 同 :IEnumerable 是 一 个 声 明 式 的 接 口 ,声 明 实 现 该 接 口 的 class 是 “ 可 枚 举( enumerable)” 的 ,但 并 没 有



说 明 如 何 实 现 枚 举 器 ( iterator) ; IEnumerator 是 一 个 实 现 式 的 接 口 , IEnumerator object 就 是 一 个 iterator。



4、 IEnumerable 和 IEnumerator 通 过 IEnumerable 的 GetEnumerator()方 法 建 立 了 连 接 , client 可 以 通 过 IEnumerable 的 GetEnumerator()得 到 IEnumerator



object, 在 这 个 意 义 上 , 将 GetEnumerator()看 作 IEnumerator object 的 factory method 也 未 尝 不 可 。



IEnumerator 是所有枚举数的基接口。



枚举数只允许读取集合中的数据。枚举数无法用于修改基础集合。



最 初 , 枚 举 数 被 定 位 于 集 合 中 第 一 个 元 素 的 前 面 。 Reset 也将枚举数返回到此位置。在此位置,调用 Current 会引发异常。因此,在读



取 Current 的值之前,必须调用 MoveNext 将枚举数提前到集合的第一个元素。



在调用 MoveNext 或 Reset 之 前 , Current 返 回 同 一 对 象 。 MoveNext 将 Current 设置为下一个元素。



在 传 递 到 集 合 的 末 尾 之 后 ,枚 举 数 放 在 集 合 中 最 后 一 个 元 素 后 面 ,且 调 用 MoveNext 会返回 false。如 果 最 后 一 次 调 用 MoveNext 返



回 false, 则 调 用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset, 然 后 再 调



用 MoveNext。



只要集合保持不变,枚举数就将保持有效。如果对集合进行了更改(例如添加、修改或删除元素),则该枚举数将失效且不可恢复,并且下一次



对 MoveNext 或 Reset 的调用将引发 InvalidOperationException。 如 果 在 MoveNext 和 Current 之间修改集合,



那 么 即 使 枚 举 数 已 经 无 效 , Current 也将返回它所设置成的元素。



枚举数没有对集合的独占访问权;因此,枚举一个集合在本质上不是一个线程安全的过程。甚至在对集合进行同步处理时,其他线程仍可以修改该集合,



这会导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。







136. 如何自己实现 IEnumerable 和 IEnumerator 接口以支持 foreach 语句

在 C#中 ,凡 是 实 现 了 IEnumerator 接 口 的 数 据 类 型 都 可 以 用 foreach 语 句 进 行 迭 代 访 问 ,可 是 ,对 于 自 定 义 类 型 如 何 实 现 这 个 接 口 以 支 持 foreach 的 迭 代 呢 ? 要



实 现 这 个 功 能 , 先 来 看 看 IEnumerable 和 IEnumerator 接 口 的 定 义 :



public interface IEnumerable



{

114



//IEnumerable 只 有 一 个 方 法 , 返 回 可 循 环 访 问 集 合 的 枚 举 数 。



IEnumerator GetEnumerator() ;



}



public interface IEnumerator



{



// 方 法



//移 到 集 合 的 下 一 个 元 素 。 如 果 成 功 则 返 回 为 true; 如 果 超 过 集 合 结 尾 , 则 返 回 false。



bool MoveNext();



// 将 集 合 设 置 为 初 始 位 置 , 该 位 置 位 于 集 合 中 第 一 个 元 素 之 前



void Reset();



// 属 性 : 获 取 集 合 中 的 当 前 元 素



object Current { get; }



}



如 果 我 们 的 自 定 义 数 据 类 型 派 生 于 这 两 个 接 口 , 并 实 现 接 口 中 的 方 法 , 即 可 用 foreach 进 行 迭 代 。



下面是一个实现的简单例子:



using System;



using System.Collections ;



using System.Collections.Generic;



using System.Text;



namespace IEnumberableTest



{



class Program



{



static void Main(string[] args)



{



myEnum enum1 = new myEnum(20);



foreach ( point p in enum1)



{



Console.WriteLine("("+p.x .ToString ()+","+p.y.ToString ()+","+p.z.ToString ()+")");



}



Console.Read();



}



}



//一 个 结 构 体 , 用 于 类 myEnum



struct point



{



public int x;



public int y;



public int z;



}



//我 们 的 一 个 派 生 于 IEnumerable 和 IEnumerator 接 口 的 自 定 义 类



class myEnum :IEnumerable,IEnumerator



{



//定 义 索 引



private int index;



//定 义 一 个 point 结 构 的 数 组



private point[] points;



//类 的 构 造 函 数 , 用 于 初 始 化 point 结 构 数 组



public myEnum(int numofpoint)

115



{



this.index = -1;



points = new point[numofpoint];



for (int i = 0; i = points.Length ? false : true;



}



}



}







137. 使用 ASP.NET Global.asax 文件

Global.asax 文 件 , 有 时 候 叫 做 ASP.NET 应 用 程 序 文 件 , 提 供 了 一 种 在 一 个 中 心 位 置 响 应 应 用 程 序 级 或 模 块 级 事 件 的 方 法 。 你 可 以 使 用 这 个 文 件 实 现 应 用 程



序安全性以及其它一些任务。下面让我们详细看一下如何在应用程序开发工作中使用这个文件。



概述



Global.asax 位 于 应 用 程 序 根 目 录 下 。 虽 然 Visual Studio .NET 会 自 动 插 入 这 个 文 件 到 所 有 的 ASP.NET 项 目 中 , 但 是 它 实 际 上 是 一 个 可 选 文 件 。 删 除 它 不



会 出 问 题 — — 当 然 是 在 你 没 有 使 用 它 的 情 况 下 。 .asax 文 件 扩 展 名 指 出 它 是 一 个 应 用 程 序 文 件 , 而 不 是 一 个 使 用 aspx 的 ASP.NET 文 件 。







Global.asax 文 件 被 配 置 为 任 何 ( 通 过 URL 的 ) 直 接 HTTP 请 求 都 被 自 动 拒 绝 , 所 以 用 户 不 能 下 载 或 查 看 其 内 容 。 ASP.NET 页 面 框 架 能 够 自 动 识 别 出 对



Global.asax 文 件 所 做 的 任 何 更 改 。在 Global.asax 被 更 改 后 ASP.NET 页 面 框 架 会 重 新 启 动 应 用 程 序 ,包 括 关 闭 所 有 的 浏 览 器 会 话 ,去 除 所 有 状 态 信 息 ,并



重新启动应用程序域。



编程



Global.asax 文 件 继 承 自 HttpApplication 类 , 它 维 护 一 个 HttpApplication 对 象 池 , 并 在 需 要 时 将 对 象 池 中 的 对 象 分 配 给 应 用 程 序 。 Global.asax 文 件 包

116



含以下事件:



· Application_Init: 在 应 用 程 序 被 实 例 化 或 第 一 次 被 调 用 时 , 该 事 件 被 触 发 。 对 于 所 有 的 HttpApplication 对 象 实 例 , 它 都 会 被 调 用 。



· Application_Disposed: 在 应 用 程 序 被 销 毁 之 前 触 发 。 这 是 清 除 以 前 所 用 资 源 的 理 想 位 置 。



· Application_Error: 当 应 用 程 序 中 遇 到 一 个 未 处 理 的 异 常 时 , 该 事 件 被 触 发 。



· Application_Start:在 HttpApplication 类 的 第 一 个 实 例 被 创 建 时 ,该 事 件 被 触 发 。它 允 许 你 创 建 可 以 由 所 有 HttpApplication 实 例 访



问的对象。



· Application_End: 在 HttpApplication 类 的 最 后 一 个 实 例 被 销 毁 时 , 该 事 件 被 触 发 。 在 一 个 应 用 程 序 的 生 命 周 期 内 它 只 被 触 发 一 次 。



· Application_BeginRequest:在 接 收 到 一 个 应 用 程 序 请 求 时 触 发 。对 于 一 个 请 求 来 说 ,它 是 第 一 个 被 触 发 的 事 件 ,请 求 一 般 是 用 户 输 入的



一 个 页 面 请 求 ( URL) 。



· Application_EndRequest: 针 对 应 用 程 序 请 求 的 最 后 一 个 事 件 。



· 在 该

Application_PreRequestHandlerExecute: ASP.NET 页 面 框 架 开 始 执 行 诸 如 页 面 或 Web 服 务 之 类 的 事 件 处 理 程 序 之 前 , 事 件 被 触 发 。



· Application_PostRequestHandlerExecute : 在 ASP.NET 页 面 框 架 结 束 执 行 一 个 事 件 处 理 程 序 时 , 该 事 件 被 触 发 。



· Applcation_PreSendRequestHeaders : 在 ASP.NET 页 面 框 架 发 送 HTTP 头 给 请 求 客 户 ( 浏 览 器 ) 时 , 该 事 件 被 触 发 。



· Application_PreSendContent: 在 ASP.NET 页 面 框 架 发 送 内 容 给 请 求 客 户 ( 浏 览 器 ) 时 , 该 事 件 被 触 发 。



· Application_AcquireRequestState : 在 ASP.NET 页 面 框 架 得 到 与 当 前 请 求 相 关 的 当 前 状 态 ( Session 状 态 ) 时 , 该 事 件 被 触 发 。



· Application_ReleaseRequestState:在 ASP.NET 页 面 框 架 执 行 完 所 有 的 事 件 处 理 程 序 时 ,该 事 件 被 触 发 。这 将 导 致 所 有 的 状 态 模 块 保 存



它们当前的状态数据。



· Application_ResolveRequestCache:在 ASP.NET 页 面 框 架 完 成 一 个 授 权 请 求 时 ,该 事 件 被 触 发 。它 允 许 缓 存 模 块 从 缓 存 中 为 请 求 提 供 服



务,从而绕过事件处理程序的执行。



· Application_UpdateRequestCache : 在 ASP.NET 页 面 框 架 完 成 事 件 处 理 程 序 的 执 行 时 , 该 事 件 被 触 发 , 从 而 使 缓 存 模 块 存 储 响 应 数 据 ,



以供响应后续的请求时使用。



· Application_AuthenticateRequest:在 安 全 模 块 建 立 起 当 前 用 户 的 有 效 的 身 份 时 ,该 事 件 被 触 发 。在 这 个 时 候 ,用 户 的 凭 据 将 会 被 验 证 。



· Application_AuthorizeRequest: 当 安 全 模 块 确 认 一 个 用 户 可 以 访 问 资 源 之 后 , 该 事 件 被 触 发 。



· Session_Start: 在 一 个 新 用 户 访 问 应 用 程 序 Web 站 点 时 , 该 事 件 被 触 发 。



· Session_End: 在 一 个 用 户 的 会 话 超 时 、 结 束 或 他 们 离 开 应 用 程 序 Web 站 点 时 , 该 事 件 被 触 发 。



这个事件列表看起来好像多得吓人,但是在不同环境下这些事件可能会非常有用。







使 用 这 些 事 件 的 一 个 关 键 问 题 是 知 道 它 们 被 触 发 的 顺 序 。 Application_Init 和 Application_Start 事 件 在 应 用 程 序 第 一 次 启 动 时 被 触 发 一 次 。 相 似 地 ,



Application_Disposed 和 Application_End 事 件 在 应 用 程 序 终 止 时 被 触 发 一 次 。此 外 ,基 于 会 话 的 事 件( Session_Start 和 Session_End)只 在 用 户 进 入 和



离开站点时被使用。其余的事件则处理应用程序请求,这些事件被触发的顺序是:



· Application_BeginRequest



· Application_AuthenticateRequest



· Application_AuthorizeRequest



· Application_ResolveRequestCache



· Application_AcquireRequestState



· Application_PreRequestHandlerExecute



· Application_PreSendRequestHeaders



· Application_PreSendRequestContent



· >



· Application_PostRequestHandlerExecute



· Application_ReleaseRequestState



· Application_UpdateRequestCache



· Application_EndRequest



这些事件常被用于安全性方面。







138. ASP.NET VIEWSTATE

一 、 ViewState 的 作 用



ViewState 用 于 维 护 页 面 的 UI 状 态



Web 是 没 有 状 态 的 , ASP.NET 页 面 也 没 有 状 态 , 它 们 在 到 服 务 器 的 每 个 往 返 过 程 中 被 实 例 化 、 执 行 、 呈 现 和 处 理 。 作 为 Web 开 发 人 员 , 您 可 以 使 用 众 所 周 知

117



的技术(如以会话状态将状态存储在服务器上,或将页面回传到自身)来添加状态 。



在 ASP.NET 之 前 , 通 过 多 次 回 传 将 值 恢 复 到 窗 体 字 段 中 完 全 是 页 面 开 发 人 员 的 责 任 , 他 们 将 不 得 不 从 HTTP 窗 体 中 逐 个 拾 取 回 传 值 , 然 后 再 将 其 推 回 字 段



中 。 幸 运 的 是 , 现 在 ASP.NET 可 以 自 动 完 成 这 项 任 务 , 从 而 为 开 发 人 员 免 除 了 一 项 令 人 厌 烦 的 工 作 , 同 时 也 无 需 再 为 窗 体 编 写 大 量 的 代 码 。 但 这 并 不 是



ViewState。



ViewState( 英 文 )是 一 种 机 制 ,ASP.NET 使 用 这 种 机 制 来 跟 踪 服 务 器 控 件 状 态 值 ,否 则 这 些 值 将 不 作 为 HTTP 窗 体 的 一 部 分 而 回 传 。例 如 ,由 Label



控 件 显 示 的 文 本 默 认 情 况 下 就 保 存 在 ViewState 中 。 作 为 开 发 人 员 , 您 可 以 绑 定 数 据 , 或 在 首 次 加 载 该 页 面 时 仅 对 Label 编 程 设 置 一 次 , 在 后 续 的 回 传 中 ,



该 标 签 文 本 将 自 动 从 ViewState 中 重 新 填 充 。 因 此 , 除 了 可 以 减 少 繁 琐 的 工 作 和 代 码 外 , ViewState 通 常 还 可 以 减 少 数 据 库 的 往 返 次 数 。



二 、 ViewState 的 工 作 原 理



它 当 该

ViewState 确 实 没 有 什 么 神 秘 之 处 , 是 由 ASP.NET 页 面 框 架 管 理 的 一 个 隐 藏 的 窗 体 字 段 。 ASP.NET 执 行 某 个 页 面 时 , 页 面 上 的 ViewState 值



和 所 有 控 件 将 被 收 集 并 格 式 化 成 一 个 编 码 字 符 串 ,然 后 被 分 配 给 隐 藏 窗 体 字 段 的 值 属 性( 即 )。由 于 隐 藏 窗 体 字 段 是 发 送 到 客 户 端 的 页 面



的 一 部 分 ,所 以 ViewState 值 被 临 时 存 储 在 客 户 端 的 浏 览 器 中 。如 果 客 户 端 选 择 将 该 页 面 回 传 给 服 务 器 ,则 ViewState 字 符 串 也 将 被 回 传 。在 上 面 的 图 2 中



可 以 看 到 ViewState 窗 体 字 段 及 其 回 传 的 值 。



回 传 后 , ASP.NET 页 面 框 架 将 解 析 ViewState 字 符 串 , 并 为 该 页 面 和 各 个 控 件 填 充 ViewState 属 性 。 然 后 , 控 件 再 使 用 ViewState 数 据 将 自 己 重新



恢复为以前的状态。



关 于 ViewState 还 有 三 个 值 得 注 意 的 小 问 题 。



1) 如 果 要 使 用 ViewState, 则 在 ASPX 页 面 中 必 须 有 一 个 服 务 器 端 窗 体 标 记 ()。 窗 体 字 段 是 必 需 的 , 这 样 包 含 ViewState 信 息



的 隐 藏 字 段 才 能 回 传 给 服 务 器 。 而 且 , 该 窗 体 还 必 须 是 服 务 器 端 的 窗 体 , 这 样 在 服 务 器 上 执 行 该 页 面 时 , ASP.NET 页 面 框 架 才 能 添 加 隐 藏 的 字 段 。



2) 页 面 本 身 将 20 字 节 左 右 的 信 息 保 存 在 ViewState 中 , 用 于 在 回 传 时 将 PostBack 数 据 和 ViewState 值 分 发 给 正 确 的 控 件 。 因 此 , 即 使 该 页 面 或



应 用 程 序 禁 用 了 ViewState, 仍 可 以 在 ViewState 中 看 到 少 量 的 剩 余 字 节 。



3) 在 页 面 不 回 传 的 情 况 下 , 可 以 通 过 省 略 服 务 器 端 的 标 记 来 去 除 页 面 中 的 ViewState。







三 、 充 分 利 用 ViewState



ViewState 为 跨 回 传 跟 踪 控 件 的 状 态 提 供 了 一 条 神 奇 的 途 径 , 因 为 它 不 使 用 服 务 器 资 源 、 不 会 超 时 , 并 且 适 用 于 任 何 浏 览 器 。 如 果 您 要 编 写 控 件 , 那



么肯定需要了解如何在控件中维护状态(英文)。



开 发 人 员 在 编 写 页 面 时 同 样 可 以 按 照 几 乎 相 同 的 方 式 来 利 用 ViewState, 只 是 有 时 页 面 会 包 含 不 由 控 件 存 储 的 UI 状 态 值 。 您 可 以 跟 踪 ViewState 中



的值,使用的编程语法与会话和高速缓存的语法类似。



四 、 选 择 会 话 状 态 还 是 ViewState?



在 某 些 情 况 下 , 将 状 态 值 保 存 在 ViewState 中 并 不 是 最 佳 选 择 , 最 常 用 的 替 代 方 法 就 是 会 话 状 态 , 它 通 常 更 适 用 于 :



1) 大 量 的 数 据 。 由 于 ViewState 增 加 了 发 送 到 浏 览 器 的 页 面 的 大 小 ( HTML 有 效 负 载 ) , 同 时 也 增 加 了 回 传 的 窗 体 的 大 小 , 因 此 不 适 合 存 储 大 量 数



据。



2) 未 在 UI 中 显 示 的 安 全 数 据 。 尽 管 ViewState 数 据 已 被 编 码 , 并 且 可 以 选 择 对 其 进 行 加 密 , 但 始 终 不 将 数 据 发 送 到 客 户 端 才 是 最 安 全 的 。 因 此 ,





会 话 是 更 安 全 的 选 择 。 由 于 数 据 库 需 要 额 外 的 凭 据 进 行 验 证 ,因 此 将 数 据 存 储 在 数 据 库 中 会 更 安 全 。可 以 添 加 SSL 以 获 得 更 安 全 的 链 接 。)但 是 ,如 果 在 UI



中 已 经 显 示 了 该 专 用 数 据 , 那 么 您 应 该 已 经 确 认 了 链 接 的 安 全 性 。 在 这 种 情 况 下 , 将 同 样 的 值 放 入 ViewState 不 会 降 低 安 全 性 。



3) 尚 未 序 列 化 到 ViewState 中 的 对 象 , 如 DataSet。 ViewState 序 列 化 程 序 只 为 一 小 部 分 常 用 的 对 象 类 型 进 行 了 优 化 , 如 下 所 示 。 其 他 可 序 列 化 的 类



型 或 许 可 以 保 留 在 ViewState 中 , 但 速 度 会 变 慢 , 并 会 生 成 一 个 非 常 大 的 ViewState。



五 、 使 用 ViewState 获 得 最 佳 性 能



使 用 ViewState 时 , 每 个 对 象 都 必 须 先 序 列 化 到 ViewState 中 , 然 后 再 通 过 回 传 进 行 反 序 列 化 , 因 此 使 用 ViewState 并 非 是 没 有 代 价 的 。 但 是 , 如



果 遵 循 某 些 简 单 的 原 则 对 ViewState 的 成 本 加 以 控 制 , 则 通 常 不 会 产 生 明 显 的 性 能 影 响 。



1) 在 不 需 要 时 禁 用 ViewState。 下 面 的 “减 少 使 用 ViewState”一 节 将 详 细 介 绍 这 一 问 题 。



使 上 这 并 可

2) 用 优 化 过 的 ViewState 序 列 化 程 序 。 面 列 出 的 类 型 具 有 专 门 的 序 列 化 程 序 , 些 程 序 运 行 速 度 很 快 , 已 经 过 优 化 , 以 生 成 很 小 的 ViewState。



如 果 要 序 列 化 一 个 未 在 上 面 列 出 的 类 型 , 可 以 创 建 一 个 自 定 义 TypeConverter 来 显 著 提 高 它 的 性 能 。



3) 尽 量 减 少 使 用 对 象 , 如 果 可 能 , 尽 量 减 少 放 入 ViewState 中 的 对 象 的 数 目 。 例 如 , 不 要 使 用 二 维 字 符 串 数 组 ( 名 称 /值 , 其 对 象 的 数 目 与 数 组 的 长



度 一 样 多 ) , 而 应 使 用 两 个 字 符 串 数 组 ( 只 有 两 个 对 象 ) 。 但 是 , 在 将 两 个 已 知 类 型 存 储 在 ViewState 中 之 前 , 在 这 两 者 之 间 转 换 不 会 获 得 任 何 性 能 提 高 ,



因为这样做实际上相当于付出了两次转换的代价。



六 、 减 少 使 用 ViewState



默 认 情 况 下 ViewState 将 被 启 用 ,并 且 是 由 每 个 控 件( 而 非 页 面 开 发 人 员 )来 决 定 存 储 在 ViewState 中 的 内 容 。有 时 ,这 一 信 息 对 应 用 程 序 并 没 有 什



么 用 处 。尽 管 也 没 什 么 害 处 ,但 却 会 明 显 增 加 发 送 到 浏 览 器 的 页 面 的 大 小 。因 此 如 果 不 需 要 使 用 ViewState,最 好 还 是 将 它 关 闭 ,特 别 是 当 ViewState 很 大 的



时候。



可 以 基 于 每 个 控 件 、 每 个 页 面 或 每 个 应 用 程 序 来 关 闭 ViewState。 在 以 下 情 况 中 将 不 再 需 要 ViewState:

118



页面:页面不回传给自身。



控 件 : 1) 处 理 的 不 是 控 件 的 事 件 。 2) 控 件 没 有 动 态 的 或 数 据 绑 定 的 属 性 值 ( 或 对 于 每 一 个 请 求 它 们 都 设 置 在 代 码 中 ) 。



DataGrid 控 件 是 ViewState 的 一 个 重 量 级 用 户 。 默 认 情 况 下 , 在 网 格 中 显 示 的 所 有 数 据 也 都 存 储 在 ViewState 中 , 当 需 要 一 个 复 杂 的 操 作 ( 如 复 杂



的 搜 索 ) 来 获 取 数 据 时 , 这 是 非 常 有 用 的 。 但 是 , DataGrid 的 这 种 行 为 有 时 也 使 得 ViewState 成 为 累 赘 。



七 、 禁 用 ViewState



通 过 将 对 象 的 EnableViewState 属 性 设 置 为 False 禁 用 了 ViewState。 可 以 针 对 单 个 控 件 、 整 个 页 面 或 整 个 应 用 程 序 禁 用 ViewState, 如 下 所 示 :



每 个 控 件 ( 在 标 记 上 )



每 个 页 面 ( 在 指 令 中 )



每 个 应 用 程 序 ( 在 web.config 中 )



八 、 使 ViewState 更 安 全



由 于 ViewState 没 有 被 格 式 化 为 清 晰 的 文 本 , 某 些 人 有 时 会 认 为 它 被 加 密 了 , 其 实 并 没 有 。 相 反 , ViewState 只 是 进 行 了 Base64 编 码 , 以 确 保 值 在



往 返 过 程 中 不 会 发 生 变 化 , 而 并 不 考 虑 应 用 程 序 使 用 的 响 应 /请 求 编 码 。



可 以 向 应 用 程 序 中 添 加 两 种 ViewState 安 全 级 别 :



1) 防 篡 改



2) 加 密



需 要 注 意 的 是 ,ViewState 安 全 性 对 于 处 理 和 呈 现 ASP.NET 页 面 所 需 的 时 间 有 直 接 的 影 响 。简 单 地 说 ,安 全 性 越 高 ,速 度 越 慢 。因 此 如 果 不 需 要 ,请



不 要 为 ViewState 添 加 安 全 性 。



九、防篡改



尽 管 散 列 代 码 不 能 确 保 ViewState 字 段 中 实 际 数 据 的 安 全 ,但 它 能 够 显 著 降 低 有 人 通 过 ViewState 骗 过 应 用 程 序 的 可 能 性 ,即 防 止 回 传 应 用 程 序 通 常



禁止用户输入的值。



可 以 通 过 设 置 EnableViewStateMAC 属 性 来 指 示 ASP.NET 向 ViewState 字 段 中 追 加 一 个 散 列 代 码 :







可 以 在 页 面 级 别 上 设 置 EnableViewStateMAC,也 可 以 在 应 用 程 序 级 别 上 设 置 。在 回 传 时 ,ASP.NET 将 为 ViewState 数 据 生 成 一 个 散 列 代 码 ,并 将 其



与 存 储 在 回 传 值 中 的 散 列 代 码 进 行 比 较 。 如 果 两 处 的 散 列 代 码 不 匹 配 , 该 ViewState 数 据 将 被 丢 弃 , 同 时 控 件 将 恢 复 为 原 来 的 设 置 。



默认情况下, 此 也

ASP.NET 使 用 SHA1 算 法 来 生 成 ViewState 散 列 代 码 。 外 , 可 以 通 过 在 machine.config 文 件 中 设 置 来 选 择 MD5 算



法,如下所示:







十、加密



可 以 使 用 加 密 来 保 护 ViewState 字 段 中 的 实 际 数 据 值 。 首 先 , 必 须 如 上 所 述 设 置 EnableViewStatMAC="true"。 然 后 , 将 machineKey



validation 类 型 设 置 为 3DES。 这 将 指 示 ASP.NET 使 用 Triple DES 对 称 加 密 算 法 来 加 密 ViewState 值 。







十 一 、 Web 领 域 中 的 ViewState 安 全 性



默 认 情 况 下 ,ASP.NET 将 创 建 一 个 随 机 的 验 证 密 钥 ,并 存 储 在 每 个 服 务 器 的 本 地 安 全 授 权 (LSA) 中 。要 验 证 在 另 一 台 服 务 器 上 创 建 的 ViewState 字



段 ,两 台 服 务 器 的 validationKey 必 须 设 置 为 相 同 的 值 。如 果 要 通 过 上 述 方 式 之 一 ,对 运 行 于 Web 领 域 配 置 中 的 应 用 程 序 进 行 ViewState 安 全 设 置 ,则 需 要



为所有服务器提供一个唯一的、共享的验证密钥。



验 证 密 钥 是 一 个 包 含 20 到 64 位 密 码 增 强 字 节 的 随 机 字 符 串 ,用 40 到 128 个 十 六 进 制 字 符 表 示 。密 钥 越 长 越 安 全 ,因 此 建 议 使 用 128 个 字 符 的



密钥(如果计算机支持)。



十二、总结





ASP.NET ViewState 是 一 种 新 的 状 态 服 务 , 供 开 发 人 员 基 于 每 个 用 户 来 跟 踪 UI 状 态 。 它

ViewState 没 有 什 么 神 秘 之 处 , 只 是 利 用 了 一 个 老 的 Web 编



程 技 巧 :在 一 个 隐 藏 的 窗 体 字 段 中 来 回 传 递 状 态 ,并 将 它 直 接 应 用 于 页 面 处 理 框 架 中 。但 效 果 却 非 常 好 - 在 基 于 Web 的 窗 体 中 只 需 编 写 并 维 护 很 少 的 代 码 。



用 户 可 能 并 不 总 是 需 要 它 , 但 我 想 您 在 需 要 它 的 时 候 会 发 现 , ViewState 是 提 供 给 页 面 开 发 人 员 的 诸 多 ASP.NET 新 功 能 中 非 常 令 人 满 意 的 一 种 功 能 。









139. 泛型



140. 扩展方法



141. 多线程

任何程序在执行时,至少有一个主线程,下面这段小程序可以给读者一个直观的印象:



//SystemThread.cs

119



using System;



using System.Threading;



namespace ThreadTest



{



class RunIt



{



[STAThread]



static void Main(string[] args)



{



Thread.CurrentThread.Name="System Thread";//给 当 前 线 程 起 名 为 "System Thread"



Console.WriteLine(Thread.CurrentThread.Name+"'Status:"+Thread.CurrentThread.ThreadState);



Console.ReadLine();



}



}



}



编译执行后你看到了什么?是的,程序将产生如下输出:



System Thread's Status:Running







在 这 里 , 我 们 通 过 Thread 类 的 静 态 属 性 CurrentThread 获 取 了 当 前 执 行 的 线 程 , 对 其 Name 属 性 赋 值 “ System Thread” , 最 后 还 输 出 了 它 的 当 前 状 态



( ThreadState) 。 所 谓 静 态 属 性 , 就 是 这 个 类 所 有 对 象 所 公 有 的 属 性 , 不 管 你 创 建 了 多 少 个 这 个 类 的 实 例 , 但 是 类 的 静 态 属 性 在 内 存 中 只 有 一 个 。 很 容 易 理



解 CurrentThread 为 什 么 是 静 态 的 — — 虽 然 有 多 个 线 程 同 时 存 在 , 但 是 在 某 一 个 时 刻 , CPU 只 能 执 行 其 中 一 个 。



就 像 上 面 程 序 所 演 示 的 , 我 们 通 过 Thread 类 来 创 建 和 控 制 线 程 。 注 意 到 程 序 的 头 部 , 我 们 使 用 了 如 下 命 名 空 间 :



using System;



using System.Threading;



在 .net framework class 所 其

library 中 , 有 与 多 线 程 机 制 应 用 相 关 的 类 都 是 放 在 System.Threading 命 名 空 间 中 的 。 中 提 供 Thread 类 用 于 创 建 线 程 ,



ThreadPool 类 用 于 管 理 线 程 池 等 等 ,此 外 还 提 供 解 决 了 线 程 执 行 安 排 ,死 锁 ,线 程 间 通 讯 等 实 际 问 题 的 机 制 。如 果 你 想 在 你 的 应 用 程 序 中 使 用 多 线 程 ,就 必 须



包 含 这 个 类 。 Thread 类 有 几 个 至 关 重 要 的 方 法 , 描 述 如 下 :







Start():启 动 线 程



Sleep(int):静 态 方 法 , 暂 停 当 前 线 程 指 定 的 毫 秒 数



Abort():通 常 使 用 该 方 法 来 终 止 一 个 线 程



Suspend(): 该 方 法 并 不 终 止 未 完 成 的 线 程 , 它 仅 仅 挂 起 线 程 , 以 后 还 可 恢 复 。



Resume():恢 复 被 Suspend()方 法 挂 起 的 线 程 的 执 行



下 面 我 们 就 动 手 来 创 建 一 个 线 程 , 使 用 Thread 类 创 建 线 程 时 , 只 需 提 供 线 程 入 口 即 可 。 线 程 入 口 使 程 序 知 道 该 让 这 个 线 程 干 什 么 事 , 在 C#中 , 线 程 入 口 是 通



过 ThreadStart 代 理( delegate)来 提 供 的 ,你 可 以 把 ThreadStart 理 解 为 一 个 函 数 指 针 ,指 向 线 程 要 执 行 的 函 数 ,当 调 用 Thread.Start()方 法 后 ,线 程 就 开



始 执 行 ThreadStart 所 代 表 或 者 说 指 向 的 函 数 。







在 多 线 程 的 程 序 中 ,经 常 会 出 现 两 种 情 况 。一 种 情 况 下 ,应 用 程 序 中 的 线 程 把 大 部 分 的 时 间 花 费 在 等 待 状 态 ,等 待 某 个 事 件 发 生 ,然 后 才 能 给 予 响 应 ;而 另 外



一 种 情 况 则 是 线 程 平 常 都 处 于 休 眠 状 态 ,只 是 周 期 性 地 被 唤 醒 。在 .net framework 里 边 ,我 们 使 用 ThreadPool 来 对 付 第 一 种 情 况 ,使 用 Timer 来 对 付 第 二 种



情况。







ThreadPool 类 提 供 一 个 由 系 统 维 护 的 线 程 池 — — 可 以 看 作 一 个 线 程 的 容 器 , 该 容 器 需 要 Windows 2000 以 上 版 本 的 系 统 支 持 , 因 为 其 中 某 些 方 法 调 用 了



只 有 高 版 本 的 Windows 才 有 的 API 函 数 。 你 可 以 使 用 ThreadPool.QueueUserWorkItem()方 法 将 线 程 安 放 在 线 程 池 里 , 该 方 法 的 原 型 如 下 :







//将 一 个 线 程 放 进 线 程 池 , 该 线 程 的 Start()方 法 将 调 用 WaitCallback 代 理 对 象 代 表 的 函 数



public static bool QueueUserWorkItem(WaitCallback);



//重 载 的 方 法 如 下 , 参 数 object 将 传 递 给 WaitCallback 所 代 表 的 方 法



public static bool QueueUserWorkItem(WaitCallback, object);

120







要 注 意 的 是 ,ThreadPool 类 也 是 一 个 静 态 类 ,你 不 能 也 不 必 要 生 成 它 的 对 象 ,而 且 一 旦 使 用 该 方 法 在 线 程 池 中 添 加 了 一 个 项 目 ,那 么 该 项 目 将 是 没 有 办 法



取 消 的 。在 这 里 你 无 需 自 己 建 立 线 程 ,只 需 把 你 要 做 的 工 作 写 成 函 数 ,然 后 作 为 参 数 传 递 给 ThreadPool.QueueUserWorkItem()方 法 就 行 了 ,传 递 的 方 法 就 是 依



靠 WaitCallback 代 理 对 象 , 而 线 程 的 建 立 、 管 理 、 运 行 等 等 工 作 都 是 由 系 统 自 动 完 成 的 , 你 无 须 考 虑 那 些 复 杂 的 细 节 问 题 , 线 程 池 的 优 点 也 就 在 这 里 体 现 出



来了,就好像你是公司老板——只需要安排工作,而不必亲自动手。



下 面 的 例 程 演 示 了 ThreadPool 的 用 法 。首 先 程 序 创 建 了 一 个 ManualResetEvent 对 象 ,该 对 象 就 像 一 个 信 号 灯 ,可 以 利 用 它 的 信 号 来 通 知 其 它 线 程 ,本 例 中 当



线程池中所有线程工作都完成以后, 从 它

ManualResetEvent 的 对 象 将 被 设 置 为 有 信 号 , 而 通 知 主 线 程 继 续 运 行 。 有 几 个 重 要 的 方 法 :Reset(),Set(),WaitOne()。



用 ( , 该

初 始 化 该 对 象 时 , 户 可 以 指 定 其 默 认 的 状 态 有 信 号 /无 信 号 ) 在 初 始 化 以 后 , 对 象 将 保 持 原 来 的 状 态 不 变 直 到 它 的 Reset()或 者 Set()方 法 被 调 用 ,Reset()



方 法 将 其 设 置 为 无 信 号 状 态 , Set()方 法 将 其 设 置 为 有 信 号 状 态 。 WaitOne()方 法 使 当 前 线 程 挂 起 直 到 ManualResetEvent 对 象 处 于 有 信 号 状 态 , 此 时 该 线 程 将



被激活。然后,程序将向线程池中添加工作项,这些以函数形式提供的工作项被系统用来初始化自动建立的线程。当所有的线程都运行完了以后,



ManualResetEvent.Set()方 法 被 调 用 ,因 为 调 用 了 ManualResetEvent.WaitOne()方 法 而 处 在 等 待 状 态 的 主 线 程 将 接 收 到 这 个 信 号 ,于 是 它 接 着 往 下 执 行 ,完 成



后边的工作。







142. 讲讲你对 Visual Studio 2005.NET 中的 CLR 编程的理解,它与 MFC 编程有什么区别(列

举 2 点即可)?

1、MFC 是 基 于 Windows 的 ,直 接 编 译 成 本 机 二 进 制 代 码 ,可 直 接 在 Windows 操 作 系 统 中 执 行 ,它 是 依 赖 于 平 台 系 统 调 用 的( 也 就 是 MFC 本 身 对 API 的 包



装)



2、 .net 编 程 ( 就 是 你 说 的 CLR) 是 基 于 .net framework 的 , 理 论 上 说 它 是 跨 平 台 的 ( 虽 然 目 前 只 有 Windows 支 持 它 , 而 且 需 要 单 独 加 装 组 件 ) , 编 译 后



的只是中间代码,不可直接执行。









143. HTTP module/HTTP handler

ASP.NET 对请求处理的过程:

这 它 ( 之 将

当 请 求 一 个 *.aspx 文 件 的 时 候 , 个 请 求 会 被 inetinfo.exe 进 程 截 获 , 判 断 文 件 的 后 缀 aspx) 后 , 这 个 请 求 转 交 给 ASPNET_ISAPI.dll,ASPNET_ISAPI.dll



会 通 过 http 管 道 ( HttpPipeLine) 将 请 求 发 送 给 ASPNET_WP.exe 进 程 , 在 ASPNET_WP.exe 进 程 中 通 过 HttpRuntime 来 处 理 这 个 请 求 , 处 理 完 毕 将 结 果 返 回 客



户端。



inetinfo.exe 进 程 : 是 www 服 务 的 进 程 , IIS 服 务 和 ASPNET_ISAPI.DLL 都 寄 存 在 此 进 程 中 。



ASPNET_ISAPI.DLL: 是 处 理 .aspx 文 件 的 win32 组 件 。 其 实 IIS 服 务 器 是 只 能 识 别 .html 文 件 的 , 当 IIS 服 务 器 发 现 被 请 求 的 文 件 是 .aspx 文 件 时 ,



IIS 服 务 器 将 其 交 给 aspnet_isapi.dll 来 处 理 。



aspnet_wp.exe 进 程 : ASP.NET 框 架 进 程 , 提 供 .net 运 行 的 托 管 环 境 , .net 的 CLR(公 共 语 言 运 行 时 )就 是 寄 存 在 此 进 程 中 。



ASP.NET Framework 处 理 一 个 Http Request 的 流 程 :



HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->ASPNET_WP.exe-->HttpRuntime-->HttpApplicationFactory-->HttpApplication-->HttpModule-



->HttpHandlerFactory-->HttpHandler-->HttpHandler.ProcessRequest()









ASP.NET 请 求 处 理 过 程 是 基 于 管 道 模 型 的 , 个 管 道 模 型 是 由 多 个 HttpModule 和 HttpHandler 组 成 ,ASP.NET 把 http 请 求 依 次 传 递 给 管 道 中 各 个 HttpModule,



最 终 被 HttpHandler 处 理 , 处 理 完 成 后 , 再 次 经 过 管 道 中 的 HTTP 模 块 , 把 结 果 返 回 给 客 户 端 。 我 们 可 以 在 每 个 HttpModule 中 都 可 以 干 预 请 求 的 处 理 过 程 。









注 意 : 在 http 请 求 的 处 理 过 程 中 , 只 能 调 用 一 个 HttpHandler, 但 可 以 调 用 多 个 HttpModule。



当 请 求 到 达 HttpModule 的 时 候 ,系 统 还 没 有 对 这 个 请 求 真 正 处 理 ,但 是 我 们 可 以 在 这 个 请 求 传 递 到 处 理 中 心( HttpHandler)之 前 附 加 一 些 其 它 信 息 ,或 者 截



获 的 这 个 请 求 并 作 一 些 额 外 的 工 作 , 也 或 者 终 止 请 求 等 。 在 HttpHandler 处 理 完 请 求 之 后 , 我 们 可 以 再 在 相 应 的 HttpModule 中 把 请 求 处 理 的 结 果 进 行 再 次 加



工返回客户端。



HttpModule

HTTP 模 块 是 实 现 了 System.Web.IhttpModule 接 口 的 类 。



IHttpModule 接 口 的 声 明 :



public interface IHttpModule



{

121



void Init (HttpApplication context);



void Dispose ();



}



Init 方 法 : 系 统 初 始 化 的 时 候 自 动 调 用 , 这 个 方 法 允 许 HTTP 模 块 向 HttpApplication 对 象 中 的 事 件 注 册 自 己 的 事 件 处 理 程 序 。



Dispose 方 法 : 这 个 方 法 给 予 HTTP 模 块 在 对 象 被 **收 集 之 前 执 行 清 理 的 机 会 。 此 方 法 一 般 无 需 编 写 代 码 。







HTTP 模 块 可 以 向 System.Web.HttpApplication 对 象 注 册 下 面 一 系 列 事 件 :



AcquireRequestState 当 ASP.NET 运 行 时 准 备 好 接 收 当 前 HTTP 请 求 的 对 话 状 态 的 时 候 引 发 这 个 事 件 。



AuthenticateRequest 当 ASP.NET 运 行 时 准 备 验 证 用 户 身 份 的 时 候 引 发 这 个 事 件 。



AuthorizeRequest 当 ASP.NET 运 行 时 准 备 授 权 用 户 访 问 资 源 的 时 候 引 发 这 个 事 件 。



BeginRequest 当 ASP.NET 运 行 时 接 收 到 新 的 HTTP 请 求 的 时 候 引 发 这 个 事 件 。



Disposed 当 ASP.NET 完 成 HTTP 请 求 的 处 理 过 程 时 引 发 这 个 事 件 。



EndRequest 把 响 应 内 容 发 送 到 客 户 端 之 前 引 发 这 个 事 件 。



Error 在 处 理 HTTP 请 求 的 过 程 中 出 现 未 处 理 异 常 的 时 候 引 发 这 个 事 件 。



PostRequestHandlerExecute 在 HTTP 处 理 程 序 结 束 执 行 的 时 候 引 发 这 个 事 件 。



PreRequestHandlerExecute 在 ASP.NET 开 始 执 行 HTTP 请 求 的 处 理 程 序 之 前 引 发 这 个 事 件 。 在 这 个 事 件 之 后 , ASP.NET 把 该 请 求 转 发 给 适 当



的 HTTP 处 理 程 序 。



PreSendRequestContent 在 ASP.NET 把 响 应 内 容 发 送 到 客 户 端 之 前 引 发 这 个 事 件 。 这 个 事 件 允 许 我 们 在 内 容 到 达 客 户 端 之 前 改 变 响 应 内 容 。



我们可以使用这个事件给页面输出添加用于所有页面的内容。例如通用菜单、头信息或脚信息。



PreSendRequestHeaders 在 ASP.NET 把 HTTP 响 应 头 信 息 发 送 给 客 户 端 之 前 引 发 这 个 事 件 。在 头 信 息 到 达 客 户 端 之 前 ,这 个 事 件 允 许 我 们 改变



它 的 内 容 。 我 们 可 以 使 用 这 个 事 件 在 头 信 息 中 添 加 cookie 和 自 定 义 数 据 。



ReleaseRequestState 当 ASP.NET 结 束 所 搜 有 的 请 求 处 理 程 序 执 行 的 时 候 引 发 这 个 事 件 。



ResolveRequestCache 我 们 引 发 这 个 事 件 来 决 定 是 否 可 以 使 用 从 输 出 缓 冲 返 回 的 内 容 来 结 束 请 求 。 这 依 赖 于 Web 应 用 程 序 的 输 出 缓 冲 时 怎 样



设置的。



UpdateRequestCache 当 ASP.NET 完 成 了 当 前 的 HTTP 请 求 的 处 理 , 并 且 输 出 内 容 已 经 准 备 好 添 加 给 输 出 缓 冲 的 时 候 , 引 发 这 个 事 件 。 这 依 赖



于 Web 应 用 程 序 的 输 出 缓 冲 是 如 何 设 置 的 。



上面这么多的事件,我们看起来可能会有些眼晕,但没关系,下面一步一步地看。



HttpModule 生 命 周 期 示 意 图









下面是事件的触发顺序:









BeginRequest 和 PreRequestHandlerExecute 之 间 的 事 件 是 在 服 务 器 执 行 HttpHandler 处 理 之 前 触 发 。



PostRequestHandlerExecute 和 PreSendRequestContent 之 间 的 事 件 是 在 服 务 器 执 行 Handler 处 理 之 后 触 发 。







下 面 我 们 看 一 下 如 何 使 用 HttpModule 来 实 现 我 们 日 常 的 应 用 :



HttpModule 通 过 在 某 些 事 件 中 注 册 , 把 自 己 插 入 ASP.NET 请 求 处 理 管 道 。 当 这 些 事 件 发 生 的 时 候 , ASP.NET 调 用 对 相 应 的 HTTP 模 块 , 这 样 该



模块就能处理请求了。



1、 向 每 个 页 面 动 态 添 加 一 些 备 注 或 说 明 性 的 文 字 :



有 的 网 站 每 一 个 页 面 都 会 弹 出 一 个 广 告 或 在 每 个 页 面 都 以 注 释 形 式 ( ) 加 入 网 站 的 版 权 信 息 。 如 果 在 每 个 页 面 教 编 写 这 样 的



JS 代 码 的 话 , 对 于 大 一 点 的 网 站 , 这 种 JS 代 码 的 编 写 与 维 护 可 是 一 个 很 繁 琐 枯 燥 的 工 作 。



有 了 HttpModule 我 们 就 可 以 很 简 单 地 解 决 这 个 问 题 了 。 HttpModule 是 客 户 端 发 出 请 求 到 客 户 端 接 收 到 服 务 器 响 应 之 间 的 一 段 必 经 之 路 。

122



我 们 完 全 可 以 在 服 务 器 处 理 完 请 求 之 后 ,并 在 向 客 户 端 发 送 响 应 文 本 之 前 这 段 时 机 ,把 这 段 注 释 文 字 添 加 到 页 面 文 本 之 后 。这 样 ,每 一 个 页 面 请 求 都 会 被 附 加



上这段注释文字。



这 段 代 码 究 竟 该 在 哪 个 事 件 里 实 现 呢 ? PostRequestHandlerExecute 和 PreSendRequestContent 之 间 的 任 何 一 个 事 件 都 可 以 , 但 我 比



较 喜 欢 在 EndRequest 事 件 里 编 写 代 码 。



第 一 步 : 创 建 一 个 类 库 ClassLibrary831。



第 二 步 : 编 写 一 个 类 实 现 IHttpModule 接 口



class TestModule:IHttpModule



{



public void Dispose()



{



}



public void Init(HttpApplication context)



{



}



}



第 三 步 : 在 Init 事 件 中 注 册 EndRequest 事 件 , 并 实 现 事 件 处 理 方 法



class TestModule:IHttpModule



{



public void Dispose(){}



public void Init(HttpApplication context)



{



context.EndRequest += new EventHandler(context_ EndRequest);



}



void context_EndRequest(object sender, EventArgs e)



{



HttpApplication ha = (HttpApplication)sender;



ha.Response.Write("");



}



}



第 四 步 : 在 Web.Conofig 中 注 册 一 下 这 个 HttpModule 模 块















name: 模 块 名 称 , 一 般 是 类 名



type: 有 两 部 分 组 成 , 前 半 部 分 是 命 名 空 间 和 类 名 组 成 的 全 名 , 后 半 部 分 是 程 序 集 名 称 , 如 果 类 是 直 接 放 在 App_Code 文 件 夹 中 , 那 程 序



名 称 是 App_Code。



运 会

这 样 在 Web 站 点 是 添 加 该 类 库 的 引 用 后 , 行 每 个 页 面 , 发 现 其 源 文 件 中 都 会 加 入 “ ” 这 句 话 。 同 样 的 方 法 你 也 可 以 在 其 中 加 入 JS 代 码 。



2、 身 份 检 查



大 家 在 作 登 录 时 , 登 录 成 功 后 , 一 般 要 把 用 户 名 放 在 Session 中 保 存 , 在 其 它 每 一 个 页 面 的 Page_Load 事 件 中 都 检 查 Session 中 是 否



存在用户名,如果不存在就说明用户未登录,就不让其访问其中的内容。



在 比 较 大 的 程 序 中 , 这 种 做 法 实 在 是 太 笨 拙 , 因 为 你 几 乎 要 在 每 一 个 页 面 中 都 加 入 检 测 Session 的 代 码 , 导 致 难 以 开 发 和 维 护 。 下 面



我 们 看 看 如 何 使 用 HttpModule 来 减 少 我 们 的 工 作 量



由 于 在 这 里 我 们 要 用 到 Session 中 的 内 容 , 我 们 只 能 在 AcquireRequestState 和 PreRequestHandlerExecute 事 件 中 编 写 代 码 , 因 为 在



HttpModule 中 只 有 这 两 事 件 中 可 以 访 问 Session。 这 里 我 们 选 择 PreRequestHandlerExecute 事 件 编 写 代 码 。



第 一 步 : 创 建 一 个 类 库 ClassLibrary831。



第 二 步 : 编 写 一 个 类 实 现 IHttpModule 接 口



class TestModule:IHttpModule



{

123



public void Dispose()



{



}



public void Init(HttpApplication context)



{



}



}



第 三 步 : 在 Init 事 件 中 注 册 PreRequestHandlerExecute 事 件 , 并 实 现 事 件 处 理 方 法



class AuthenticModule:IHttpModule



{



public void Dispose(){}



public void Init(HttpApplication context)



{



context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);



}



void context_PreRequestHandlerExecute(object sender, EventArgs e)



{



HttpApplication ha = (HttpApplication)sender;



string path = ha.Context.Request.Url.ToString();



int n = path.ToLower().IndexOf("Login.aspx");



if (n == -1) //是 否 是 登 录 页 面 , 不 是 登 录 页 面 的 话 则 进 入 {}



{



if (ha.Context.Session["user"] == null) // 是 否 Session 中 有 用 户 名 , 若 是 空 的 话 , 转 向 登 录 页 。



{



ha.Context.Response.Redirect("Login.aspx?source=" + path);



}



}



}



}



第 四 步 : 在 Login.aspx 页 面 的 “ 登 录 ” 按 钮 中 加 入 下 面 代 码



protected void Button1_Click(object sender, EventArgs e)



{



if(true) //判 断 用 户 名 密 码 是 否 正 确



{



if (Request.QueryString["source"] != null)



{



string s = Request.QueryString["source"].ToLower().ToString(); //取 出 从 哪 个 页 面 转 来 的



Session["user"] = txtUID.Text;



Response.Redirect(s); //转 到 用 户 想 去 的 页 面



}



else



{



Response.Redirect("main.aspx"); //默 认 转 向 main.aspx



}



}



}



第 五 步 : 在 Web.Conofig 中 注 册 一 下 这 个 HttpModule 模 块









124







3、 多 模 块 的 操 作



如 果 定 义 了 多 个 HttpModule, 在 web.config 文 件 中 引 入 自 定 义 HttpModule 的 顺 序 就 决 定 了 多 个 自 定 义 HttpModule 在 处 理 一 个 HTTP



请求的接管顺序。









HttpHandler

HttpHandler 是 HTTP 请 求 的 处 理 中 心 ,真 正 地 对 客 户 端 请 求 的 服 务 器 页 面 做 出 编 译 和 执 行 ,并 将 处 理 过 后 的 信 息 附 加 在 HTTP 请 求 信 息 流 中 再 次 返 回



到 HttpModule 中 。



HttpHandler 与 HttpModule 不 同 , 一 旦 定 义 了 自 己 的 HttpHandler 类 , 那 么 它 对 系 统 的 HttpHandler 的 关 系 将 是 “ 覆 盖 ” 关 系 。



IHttpHandler 接 口 声 明



public interface IHttpHandler



{



bool IsReusable { get; }



public void ProcessRequest(HttpContext context); // 请 求 处 理 函 数



}







示例:把硬盘上的图片以流的方式写在页面上



class TestHandler : IHttpHandler



{



public void ProcessRequest(HttpContext context)



{



FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);



byte[] b = new byte[fs.Length];



fs.Read(b, 0, (int)fs.Length);



fs.Close();



context.Response.OutputStream.Write(b, 0, b.Length);



}



public bool IsReusable



{



get



{



return true;



}



}



}



Web.Config 配 置 文 件















指 *-

Verb 属 性 : 定 了 处 理 程 序 支 持 的 HTTP 动 作 。 支 持 所 有 的 HTTP 动 作 ;“ GET” - 支 持 Get 操 作 ;“ POST” - 支 持 Post 操 作 ;“ GET, POST”



-支持两种操作。



Path 属 性 : 指 定 了 需 要 调 用 处 理 程 序 的 路 径 和 文 件 名 ( 可 以 包 含 通 配 符 ) 。 “ *” 、 “ *.aspx” 、 “ showImage.aspx” 、 “ test1.aspx,test2.aspx”



Type 属 性 : 用 名 字 空 间 、 类 名 称 和 程 序 集 名 称 的 组 合 形 式 指 定 处 理 程 序 或 处 理 程 序 工 厂 的 实 际 类 型 。 ASP.NET 运 行 时 首 先 搜 索 bin 目 录 中 的 DLL, 接 着 在



GAC 中 搜 索 。



这 样 程 序 运 行 的 效 果 是 该 网 站 的 任 何 一 个 页 面 都 会 显 示 worm.jpg 图 片 。 如 何 只 让 一 个 页 面 ( default21.aspx) 执 行 HttpHandler 中 的

125



ProcessRequest 方 法 呢 ? 最 简 单 的 办 法 是 在 Web.Config 文 件 中 把 path 配 置 信 息 设 为 default21.aspx。



根据这个例子大家可以考虑一下如何编写“验证码”了。



IHttpHandler 工厂

IHttpHandlerFactory 的 作 用 是 对 IHttpHandler 进 行 管 理 。 工 厂 的 作 用 请 见



http://hi.baidu.com/grayworm/blog/item/4a832160f8c9de46eaf8f8c1.html "



IHttpHandlerFactory 接 口 的 声 明 :



public interface IHttpHandlerFactory



{



IHttpHandler GetHandler (HttpContext context,string requestType,string url,string pathTranslated);



void ReleaseHandler (IHttpHandler handler);



}



GetHandler 返 回 实 现 IHttpHandler 接 口 的 类 的 实 例 , ReleaseHandler 使 工 厂 可 以 重 用 现 有 的 处 理 程 序 实 例 。



示 例 : 两 个 用 IHttpHandlerFactory 来 实 现 对 不 同 HttpHandler 的 调 用 。



有 两 个 HttpHandler: 将 图 片 显 示 在 页 面 上 的 HttpHandler 和 生 成 验 证 码 的 Handler



//将 图 片 显 示 在 页 面 上 的 Handler



class TestHandler : IHttpHandler



{



public void ProcessRequest(HttpContext context)



{



FileStream fs = new FileStream(context.Server.MapPath("worm.jpg"), FileMode.Open);



byte[] b = new byte[fs.Length];



fs.Read(b, 0, (int)fs.Length);



fs.Close();



context.Response.OutputStream.Write(b, 0, b.Length);



}



public bool IsReusable



{



get



{



return true;



}



}



}



//生 成 验 证 码 的 Handler



class CodeHandler:IHttpHandler



{



public bool IsReusable



{



get



{



return true;



}



}



public void ProcessRequest(HttpContext context)



{



Image b = new Bitmap(50,20);



Graphics g = Graphics.FromImage(b);



SolidBrush sb = new SolidBrush(Color.White);



Font f = new Font("宋 体 ", 12);

126



string str = "";



Random r = new Random();



for (int i = 0; i











这 样 TestHandlerFactory 就 会 根 据 请 求 的 不 同 页 面 执 行 不 同 的 HttpHandler 处 理 程 序 了 。



HttpHandler 使用会话

如 果 要 在 处 理 程 序 中 使 用 Session, 那 必 须 把 该 HttpHandler 实 现 IRequiresSessionState 接 口 , ,IRequiresSessionState 接 口 是 个 空 接 口 , 它 没



有抽象方法,只是一个标记。此处就不作例子验证了。







144. GAC 的理解及其作用.

GAC 全 称 是 Global Assembly 他 例

Cache, 的 作 用 是 可 以 存 放 一 些 有 很 多 程 序 都 要 用 到 的 公 共 Assembly, 如 System.Data、System.Windows.Forms 等 等 。

127



这 样 , 很 多 程 序 就 可 以 从 GAC 里 面 取 得 Assembly, 而 不 需 要 再 把 所 有 要 用 到 的 Assembly 都 拷 贝 到 应 用 程 序 的 执 行 目 录 下 面 。 举 例 而 言 , 如 果 没 有 GAC, 那 么





势 必 每 个 WinForm 程 序 的 目 录 下 就 都 要 从 C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705 下 面 拷 贝 一 份 System.Windows.Forms.dll, 样 显 然 不 如 都 从 GAC



里 面 取 用 方 便 , 也 有 利 于 Assembly 的 升 级 和 版 本 控 制 。







除 了 系 统 默 认 放 置 在 GAC 中 的 Assembly 如 System.Windows.Forms 以 外 , 我 们 也 可 以 添 加 自 己 的 Assembly:



1) 创 建 一 个 strong-name 的 Assembly, 例 如 ToolbarComponent.dll



2) 运 行 gacutil -i ToolbarComponent.dll, 把 这 个 Assembly 添 加 到 GAC



3) 在 程 序 中 动 态 装 载 :



System.Reflection.Assembly ass=Assembly.Load("ToolbarComponent, Version=1.0.934.20434, Culture=neutral, PublicKeyToken=65f



45658c8d4927f");



MessageBox.Show("Is the assembly loaded from GAC? "+ass.GlobalAssemblyCache);



在 上 面 的 程 序 中 ,ToolbarComponent 就 是 从 GAC 装 载 而 不 是 从 程 序 的 运 行 目 录 下 的 dll 文 件 中 装 载 ,程 序 目 录 下 不 需 要 放 置 ToolbarComponent.dll 程 序 也



能 正 常 运 行 。 另 外 , Assembly.Load()中 的 参 数 可 以 通 过 "gacutil -l"查 到 。







另 外 , 上 面 提 到 了 GAC 中 的 Assembly 必 须 是 strong-name 的 。 创 建 strong-name 的 Assembly 的 步 骤 大 致 如 下 :



a) 在 命 令 行 运 行 “ sn -k keyPair.snk” 创 建 一 个 密 钥 文 件 。 这 里 的 sn.exe 也 是 .NET 附 带 的 一 个 工 具 。



b) 在 VS.NET 里 面 修 改 “ AssemblyInfo.cs” 文 件 :



[assembly: AssemblyDelaySign(false)]



[assembly: AssemblyKeyFile("..\\..\\keyPair.snk")]



c) 编 译 项 目 , 就 能 得 到 一 个 strong-name 的 Assembly。



GAC 中 的 所 有 的 Assembly 都 会 存 放 在 系 统 目 录 "%winroot%\assembly 下 面 。 放 在 系 统 目 录 下 的 好 处 之 一 是 可 以 让 系 统 管 理 员 通 过 用 户 权 限 来 控 制 Assembly 的



访问。







145. firefox 与 IE 对 javascript 和 CSS 的区别

1. document.formName.item("itemName") 问 题



说 明 :IE 下 ,可 以 使 用 document.formName.item("itemName") 或 document.formName.elements["elementName"];



Firefox 下 ,只 能 使 用 document.formName.elements["elementName"].



解 决 方 法 :统 一 使 用 document.formName.elements["elementName"].



2.集 合 类 对 象 问 题



说 明 :IE 下 ,可 以 使 用 ()或 []获 取 集 合 类 对 象 ;Firefox 下 ,只 能 使 用 []获 取 集 合 类 对 象 .



解 决 方 法 :统 一 使 用 []获 取 集 合 类 对 象 .



3.自 定 义 属 性 问 题



说 明 :IE 下 ,可 以 使 用 获 取 常 规 属 性 的 方 法 来 获 取 自 定 义 属 性 ,也 可 以 使 用 getAttribute()获 取 自 定 义 属 性 ;Firefox 下 ,只 能 使 用 getAttribute()获 取 自 定 义 属



性.



解 决 方 法 :统 一 通 过 getAttribute()获 取 自 定 义 属 性 .



4.eval("idName")问 题



说 明 :IE 下 ,,可 以 使 用 eval("idName")或 getElementById("idName")来 取 得 id 为 idName 的 HTML 对 象 ;Firefox 下 只 能 使 用 getElementById("idName")来 取 得



id 为 idName 的 HTML 对 象 .



解 决 方 法 :统 一 用 getElementById("idName")来 取 得 id 为 idName 的 HTML 对 象 .



5.变 量 名 与 某 HTML 对 象 ID 相 同 的 问 题



说 明 :IE 下 ,HTML 对 象 的 ID 可 以 作 为 document 的 下 属 对 象 变 量 名 直 接 使 用 ;Firefox 下 则 不 能 .Firefox 下 ,可 以 使 用 与 HTML 对 象 ID 相 同 的 变 量 名 ;IE 下 则 不 能 。



解 决 方 法 :使 用 document.getElementById("idName") 代 替 document.idName.最 好 不 要 取 HTML 对 象 ID 相 同 的 变 量 名 ,以 减 少 错 误 ;在 声 明 变 量 时 ,一 律 加 上 var,



以避免歧义.



6.const 问 题



说 明 :Firefox 下 ,可 以 使 用 const 关 键 字 或 var 关 键 字 来 定 义 常 量 ;IE 下 ,只 能 使 用 var 关 键 字 来 定 义 常 量 .



解 决 方 法 :统 一 使 用 var 关 键 字 来 定 义 常 量 .



7.input.type 属 性 问 题



说 明 :IE 下 input.type 属 性 为 只 读 ;但 是 Firefox 下 input.type 属 性 为 读 写 .



8.window.event 问 题

128



说 明 :window.event 只 能 在 IE 下 运 行 ,而 不 能 在 Firefox 下 运 行 ,这 是 因 为 Firefox 的 event 只 能 在 事 件 发 生 的 现 场 使 用 . Firefox 必 须 从 源 处 加 入 event 作 参



数 传 递 。 Ie 忽 略 该 参 数 , 用 window.event 来 读 取 该 event。



解决方法:



IE&Firefox:



Submitted(event)"/> …







function Submitted(evt) {



evt=evt?evt:(window.event?window.event:null);



}







9.event.x 与 event.y 问 题



说 明 :IE 下 ,even 对 象 有 x,y 属 性 ,但 是 没 有 pageX,pageY 属 性 ;Firefox 下 ,even 对 象 有 pageX,pageY 属 性 ,但 是 没 有 x,y 属 性 .



解 决 方 法 :使 用 mX(mX = event.x ? event.x : event.pageX;) 来 代 替 IE 下 的 event.x 或 者 Firefox 下 的 event.pageX.



10.event.srcElement 问 题



说 明 :IE 下 ,event 对 象 有 srcElement 属 性 ,但 是 没 有 target 属 性 ;Firefox 下 ,even 对 象 有 target 属 性 ,但 是 没 有 srcElement 属 性 .



解 决 方 法 :使 用 obj(obj = event.srcElement ? event.srcElement : event.target ;)来 代 替 IE 下 的 event.srcElement 或 者 Firefox 下 的 event.target. 请



同 时 注 意 event 的 兼 容 性 问 题 。



11.window.location.href 问 题



说 明 :IE 或 者 Firefox2.0.x 下 ,可 以 使 用 window.location 或 window.location.href;Firefox1.5.x 下 ,只 能 使 用 window.location.



解 决 方 法 :使 用 window.location 来 代 替 window.location.href.



12.模 态 和 非 模 态 窗 口 问 题



说 明 :IE 下 ,可 以 通 过 showModalDialog 和 showModelessDialog 打 开 模 态 和 非 模 态 窗 口 ;Firefox 下 则 不 能 .



解 决 方 法 :直 接 使 用 window.open(pageURL,name,parameters) 方 式 打 开 新 窗 口 。



如 果 需 要 将 子 窗 口 中 的 参 数 传 递 回 父 窗 口 ,可 以 在 子 窗 口 中 使 用 window.opener 来 访 问 父 窗 口 . 例 如 : var parWin = window.opener;



parWin.document.getElementById("Aqing").value = "A qing";



13.frame 问 题



以 下 面 的 frame 为 例 :







(1)访 问 frame 对 象 :



IE:使 用 window.frameId 或 者 window.frameName 来 访 问 这 个 frame 对 象 . frameId 和 frameName 可 以 同 名 。



Firefox:只 能 使 用 window.frameName 来 访 问 这 个 frame 对 象 .



另 外 , 在 IE 和 Firefox 中 都 可 以 使 用 window.document.getElementById("frameId")来 访 问 这 个 frame 对 象 .



(2)切 换 frame 内 容 :



在 IE 和 Firefox 中 都 可 以 使 用 window.document.getElementById("testFrame").src = "xxx.html" 或 window.frameName.location = "xxx.html" 来 切 换 frame



的内容.



如 果 需 要 将 frame 中 的 参 数 传 回 父 窗 口 (注 意 不 是 opener,而 是 parent frame), 可 以 在 frme 中 使 用 parent 来 访 问 父 窗 口 。 例 如 :



parent.document.form1.filename.value="Aqing";



14.body 问 题



Firefox 的 body 在 body 标 签 没 有 被 浏 览 器 完 全 读 入 之 前 就 存 在 ; 而 IE 的 body 则 必 须 在 body 标 签 被 浏 览 器 完 全 读 入 之 后 才 存 在 .



15. 事 件 委 托 方 法



IE: document.body.onload = inject; //Function inject() 在 这 之 前 已 被 实 现



Firefox: document.body.onload = inject();



16. firefox 与 IE 的 父 元 素 (parentElement)的 区 别



IE: obj.parentElement



firefox: obj.parentNode



解 决 方 法 : 因 为 firefox 与 IE 都 支 持 DOM,因 此 使 用 obj.parentNode 是 不 错 选 择 .



17.cursor:hand VS cursor:pointer



firefox 不 支 持 hand, 但 ie 支 持 pointer



解 决 方 法 : 统 一 使 用 pointer

129



18.innerText 在 IE 中 能 正 常 工 作 , 但 是 innerText 在 FireFox 中 却 不 行 . 需 用 textContent。



解决方法:



if(navigator.appName.indexOf("Explorer") > -1){



document.getElementById('element').innerText = "my text";



} else{



document.getElementById('element').textContent = "my text";



}



19. FireFox 中 设 置 HTML 标 签 的 style 时 , 所 有 位 置 性 和 字 体 尺 寸 的 值 必 须 后 跟 px。 这 个 ie 也 是 支 持 的 。



在 使 使

20. ie,firefox 以 及 其 它 浏 览 器 对 于 table 标 签 的 操 作 都 各 不 相 同 , ie 中 不 允 许 对 table 和 tr 的 innerHTML 赋 值 , 用 js 增 加 一 个 tr 时 , 用 appendChild



方法也不管用。



解决方法:



//向 table 追 加 一 个 空 行 :



var row = otable.insertRow(-1);



var cell = document.createElement("td");



cell.innerHTML = " ";



cell.className = "XXXX";



row.appendChild(cell);



21. padding 问 题



padding 5px 4px 3px 1px FireFox 无 法 解 释 简 写 ,



必 须 改 成 padding-top:5px; padding-right:4px; padding-bottom:3px; padding-left:1px;



22. 消 除 ul、 ol 等 列 表 的 缩 进 时



样 式 应 写 成 :list-style:none;margin:0px;padding:0px;



其 中 margin 属 性 对 IE 有 效 , padding 属 性 对 FireFox 有 效



23. CSS 透 明



IE: filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=60) 。



FF: opacity:0.6。



24. CSS 圆 角



IE: 不 支 持 圆 角 。



FF: -moz-border-radius:4px, 或 者 -moz-border-radius-topleft:4px;-moz-border-



radius-topright:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius- bottomright:4px;。



25. CSS 双 线 凹 凸 边 框



IE: border:2px outset;。



FF: -moz-border-top-colors: #d4d0c8 white;-moz-border-left-colors: #d4d0c8 white;-moz-border-right-colors:#404040



#808080;-moz-border-bottom-colors:#404040 #808080;



26. 对 select 的 options 集 合 操 作



枚 举 元 素 除 了 []外 , SelectName.options.item()也 是 可 以 的 , 另 外 SelectName.options.length, SelectName.options.add/remove 都 可 以 在 两 种 浏 览 器 上 使



用 。 注 意 在 add 后 赋 值 元 素 , 否 则 会 失 败 ( 本 人 试 验 如 此 ) 。



27. XMLHTTP 的 区 别



//mf



if (window.XMLHttpRequest) //mf



{



xmlhttp=new XMLHttpRequest()



xmlhttp.



xmlhttp.open("GET",url,true)



xmlhttp.send(null)



}



//ie



else if (window.ActiveXObject) // code for IE



{

130



xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")



if (xmlhttp)



{



xmlhttp.



xmlhttp.open("GET",url,true)



xmlhttp.send()



}



}



}



28. innerHTML 的 区 别



Firefox 不 支 持 innerHTML, 解 决 办 法 可 以 如 下



rng = document.createRange();



el = document.getElementById(elementid);



rng.setStartBefore(el);



htmlFrag = rng.createContextualFragment(content);



while (el.hasChildNodes()) //清 除 原 有 内 容 , 加 入 新 内 容



el.removeChild(el.lastChild);



el.appendChild(htmlFrag);



29. img 的 src 刷 新 问 题



在 IE 下 可 以 用 可 以 刷 新 图 片 , 但 在 FireFox 下 不 行 。



主 要 是 缓 存 问 题 , 在 地 址 后 面 加 个 随 机 数 就 解 决 了 。 编 辑 onclick 事 件 代 码 如 下 : "this.src=this.src+'?'+Math.random()"







146. jQuery AJAX

jQuery 确 实 是 一 个 挺 好 的 轻 量 级 的 JS 框 架 , 能 帮 助 我 们 快 速 的 开 发 JS 应 用 , 并 在 一 定 程 度 上 改 变 了 我 们 写 JavaScript 代 码 的 习 惯 。



废 话 少 说 ,直 接 进 入 正 题 ,我 们 先 来 看 一 些 简 单 的 方 法 ,这 些 方 法 都 是 对 jQuery.ajax()进 行 封 装 以 方 便 我 们 使 用 的 方 法 ,当 然 ,如 果 要 处 理 复 杂 的 逻 辑 ,还



是 需 要 用 到 jQuery.ajax()的 (这 个 后 面 会 说 到 ).







1. load ( url, [data], [callback] ) : 载 入 远 程 HTML 文 件 代 码 并 插 入 至 DOM 中 。





url (String) : 请 求 的 HTML 页 的 URL 地 址 。



data (Map) : (可 选 参 数 ) 发 送 至 服 务 器 的 key/value 数 据 。



callback (Callback) : (可 选 参 数 ) 请 求 完 成 时 (不 需 要 是 success 的 )的 回 调 函 数 。



这 个 方 法 默 认 使 用 GET 方 式 来 传 递 的 ,如 果 [data]参 数 有 传 递 数 据 进 去 ,就 会 自 动 转 换 为 POST 方 式 的 。jQuery 1.2 中 ,可 以 指 定 选 择 符 ,来 筛 选 载 入 的



HTML 文 档 , DOM 中 将 仅 插 入 筛 选 出 的 HTML 代 码 。 语 法 形 如 "url #some > selector" 。



这 个 方 法 可 以 很 方 便 的 动 态 加 载 一 些 HTML 文 件 , 例 如 表 单 。



示例代码:



$(".ajax.load").load("http://www.cnblogs.com/QLeelulu/archive/2008/03/30/1130270.html .post",



function (responseText, textStatus, XMLHttpRequest){



this;//在 这 里 this 指 向 的 是 当 前 的 DOM 对 象 , 即 $(".ajax.load")[0]



//alert(responseText);//请 求 返 回 的 内 容



//alert(textStatus);//请 求 状 态 : success, error



//alert(XMLHttpRequest);//XMLHttpRequest 对 象



});



这里将显示结果。



注 : 不 知 道 为 什 么 URL 写 绝 对 路 径 在 FF 下 会 出 错 , 知 道 的 麻 烦 告 诉 下 。 下 面 的 get()和 post()示 例 使 用 的 是 绝 对 路 径 , 所 以 在 FF 下 你 将 会 出 错 并 不 会 看 到



返 回 结 果 。 还 有 get()和 post()示 例 都 是 跨 域 调 用 的 , 发 现 传 上 来 后 没 办 法 获 取 结 果 , 所 以 把 运 行 按 钮 去 掉 了 。









2. jQuery.get ( url, [data], [callback] ): 使 用 GET 方 式 来 进 行 异 步 请 求





参数:

131



url (String) : 发 送 请 求 的 URL 地 址 .



data (Map) : (可 选 ) 要 发 送 给 服 务 器 的 数 据 , 以 Key/value 的 键 值 对 形 式 表 示 , 会 做 为 QueryString 附 加 到 请 求 URL 中 。



callback (Function) : (可 选 ) 载 入 成 功 时 回 调 函 数 (只 有 当 Response 的 返 回 状 态 是 success 才 是 调 用 该 方 法 )。



这 是 一 个 简 单 的 GET 请 求 功 能 以 取 代 复 杂 $.ajax 。 请 求 成 功 时 可 调 用 回 调 函 数 。 如 果 需 要 在 出 错 时 执 行 函 数 , 请 使 用 $.ajax。 示 例 代 码 :



$.get("./Ajax.aspx", {Action:"get",Name:"lulu"}, function (data, textStatus){



//返 回 的 data 可 以 是 xmlDoc, jsonObj, html, text, 等 等 .



this; // 在 这 里 this 指 向 的 是 Ajax 请 求 的 选 项 配 置 信 息 , 请 参 考 下 图



alert(data);



//alert(textStatus);//请 求 状 态 : success, error 等 等 。



当 然 这 里 捕 捉 不 到 error, 因 为 error 的 时 候 根 本 不 会 运 行 该 回 调 函 数



//alert(this);



});



点击发送请求:



jQuery.get()回 调 函 数 里 面 的 this , 指 向 的 是 Ajax 请 求 的 选 项 配 置 信 息 :









3. jQuery.post ( url, [data], [callback], [type] ) : 使 用 POST 方 式 来 进 行 异 步 请 求





参数:



url (String) : 发 送 请 求 的 URL 地 址 .



data (Map) : (可 选 ) 要 发 送 给 服 务 器 的 数 据 , 以 Key/value 的 键 值 对 形 式 表 示 。



callback (Function) : (可 选 ) 载 入 成 功 时 回 调 函 数 (只 有 当 Response 的 返 回 状 态 是 success 才 是 调 用 该 方 法 )。



type (String) : (可 选 )官 方 的 说 明 是 : Type of data to be sent。 其 实 应 该 为 客 户 端 请 求 的 类 型 (JSON,XML,等 等 )



这 是 一 个 简 单 的 POST 请 求 功 能 以 取 代 复 杂 $.ajax 。 请 求 成 功 时 可 调 用 回 调 函 数 。 如 果 需 要 在 出 错 时 执 行 函 数 , 请 使 用 $.ajax。 示 例 代 码 :



Ajax.aspx:



Response.ContentType = "application/json";



Response.Write("{result: '" + Request["Name"] + ",你 好 ! (这 消 息 来 自 服 务 器 )'}");



jQuery 代 码 :



$.post("Ajax.aspx", { Action: "post", Name: "lulu" },



function (data, textStatus){



// data 可 以 是 xmlDoc, jsonObj, html, text, 等 等 .



//this; // 这 个 Ajax 请 求 的 选 项 配 置 信 息 , 请 参 考 jQuery.get()说 到 的 this



alert(data.result);



}, "json");



点击提交:



这 里 设 置 了 请 求 的 格 式 为 "json":

132









如 果 你 设 置 了 请 求 的 格 式 为 "json", 此 时 你 没 有 设 置 Response 回 来 的 ContentType 为 : Response.ContentType = "application/json"; 那 么 你 将 无 法 捕 捉



到返回的数据。



注 意 一 下 , alert(data.result); 由 于 设 置 了 Accept 报 头 为 “ json” , 这 里 返 回 的 data 就 是 一 个 对 象 , 并 不 需 要 用 eval()来 转 换 为 对 象 。









4. jQuery.getScript ( url, [callback] ) : 通 过 GET 方 式 请 求 载 入 并 执 行 一 个 JavaScript 文 件 。





参数



url (String) : 待 载 入 JS 文 件 地 址 。



callback (Function) : (可 选 ) 成 功 载 入 后 回 调 函 数 。



jQuery 1.2 版 本 之 前 , getScript 只 能 调 用 同 域 JS 文 件 。 1.2 中 , 您 可 以 跨 域 调 用 JavaScript 文 件 。 注 意 : Safari 2 或 更 早 的 版 本 不 能 在 全 局 作 用



域 中 同 步 执 行 脚 本 。 如 果 通 过 getScript 加 入 脚 本 , 请 加 入 延 时 函 数 。



这 个 方 法 可 以 用 在 例 如 当 只 有 编 辑 器 focus()的 时 候 才 去 加 载 编 辑 器 需 要 的 JS 文 件 .下 面 看 一 些 示 例 代 码 :



加 载 并 执 行 test.js。



jQuery 代 码 :



$.getScript("test.js");







加 载 并 执 行 AjaxEvent.js , 成 功 后 显 示 信 息 。



jQuery 代 码 :



$.getScript("AjaxEvent.js", function(){



alert("AjaxEvent.js 加 载 完 成 并 执 行 完 成 .你 再 点 击 上 面 的 Get 或 Post 按 钮 看 看 有 什 么 不 同 ? ");



});



加 载 完 后 请 重 新 点 击 一 下 上 面 的 Load 请 求 看 看 有 什 么 不 同 。







jQuery Ajax 事件

Ajax 请 求 会 产 生 若 干 不 同 的 事 件 , 我 们 可 以 订 阅 这 些 事 件 并 在 其 中 处 理 我 们 的 逻 辑 。 在 jQuery 这 里 有 两 种 Ajax 事 件 : 局 部 事 件 和 全 局 事 件 。



局 部 事 件 就 是 在 每 次 的 Ajax 请 求 时 在 方 法 内 定 义 的 , 例 如 :



$.ajax({



beforeSend: function(){



// Handle the beforeSend event



},



complete: function(){



// Handle the complete event



}



// ...



});



全 局 事 件 是 每 次 的 Ajax 请 求 都 会 触 发 的 , 它 会 向 DOM 中 的 所 有 元 素 广 播 , 在 上 面 getScript() 示 例 中 加 载 的 脚 本 就 是 全 局 Ajax 事 件 。 全 局 事 件 可 以 如 下



定义:



$("#loading").bind("ajaxSend", function(){



$(this).show();



}).bind("ajaxComplete", function(){



$(this).hide();



});

133



或者:



$("#loading").ajaxStart(function(){



$(this).show();



});



我 们 可 以 在 特 定 的 请 求 将 全 局 事 件 禁 用 , 只 要 设 置 下 global 选 项 就 可 以 了 :



$.ajax({



url: "test.html",



global: false,// 禁 用 全 局 Ajax 事 件 .



// ...



});



下 面 是 jQuery 官 方 给 出 的 完 整 的 Ajax 事 件 列 表 :



 ajaxStart (Global Event)



This event is broadcast if an Ajax request is started and no other Ajax requests are currently running.



beforeSend (Local Event)



This event, which is triggered before an Ajax request is started, allows you to modify the XMLHttpRequest object (setting add itional headers,



if need be.)



ajaxSend (Global Event)



This global event is also triggered before the request is run.



success (Local Event)



This event is only called if the request was successful (no errors from the server, no errors with the data).



ajaxSuccess (Global Event)



This event is also only called if the request was successful.



error (Local Event)



This event is only called if an error occurred with the request (you can never have both an error and a success callback with a request).



ajaxError (Global Event)



This global event behaves the same as the local error event.



complete (Local Event)



This event is called regardless of if the request was successful, or not. You will always receive a complete callback, even for synchronous



requests.



ajaxComplete (Global Event)



This event behaves the same as the complete event and will be triggered every time an Ajax request finishes.



 ajaxStop (Global Event)



This global event is triggered if there are no more Ajax requests being processed.



具 体 的 全 局 事 件 请 参 考 API 文 档 。



好 了 , 下 面 开 始 说 jQuery 里 面 功 能 最 强 的 Ajax 请 求 方 法 $.ajax();









jQuery.ajax ( options ) : 通 过 HTTP 请 求 加 载 远 程 数 据





这 个 是 jQuery 的 底 层 AJAX 实 现 。 简 单 易 用 的 高 层 实 现 见 $.get, $.post 等 。



$.ajax() 返 回 其 创 建 的 XMLHttpRequest 对 象 。 大 多 数 情 况 下 你 无 需 直 接 操 作 该 对 象 , 但 特 殊 情 况 下 可 用 于 手 动 终 止 请 求 。



注 意 : 如 果 你 指 定 了 dataType 选 项 , 确 保 服 务 器 返 回 正 确 的 MIME 信 息 ,

请 (如 xml 返 回 "text/xml")。 误 的 MIME 类 型 可 能 导 致 不 可 预 知 的 错 误 。





见 Specifying the Data Type for AJAX Requests 。



当 设 置 datatype 类 型 为 'script' 的 时 候 , 所 有 的 远 程 (不 在 同 一 个 域 中 )POST 请 求 都 回 转 换 为 GET 方 式 。



$.ajax() 只 有 一 个 参 数 : 参 数 key/value 对 象 , 包 含 各 配 置 及 回 调 函 数 信 息 。 详 细 参 数 选 项 见 下 。



jQuery 1.2 中 , 您 可 以 跨 域 加 载 JSON 数 据 , 使 用 时 需 将 数 据 类 型 设 置 为 JSONP 。 使 用 JSONP 形 式 调 用 函 数 时 , 如 "myurl?callback=?" jQuery 将



自 动 替 换 ? 为 正 确 的 函 数 名 , 以 执 行 回 调 函 数 。 数 据 类 型 设 置 为 "jsonp" 时 , jQuery 将 自 动 调 用 回 调 函 数 。 (这 个 我 不 是 很 懂 )



参数列表:





参数名 类型 描述

134





url String (默 认 : 当 前 页 地 址 ) 发 送 请 求 的 地 址 。





type String (默 认 : "GET") 请 求 方 式 ("POST" 或 "GET"), 默 认 为 "GET"。注 意 :其 它 HTTP 请 求 方



法 , 如 PUT 和 DELETE 也 可 以 使 用 , 但 仅 部 分 浏 览 器 支 持 。





timeout Number 设置请求超时时间(毫秒)。此设置将覆盖全局设置。





async Boolean (默 认 : true) 默 认 设 置 下 , 所 有 请 求 均 为 异 步 请 求 。 如 果 需 要 发 送 同 步 请 求 , 请 将 此 选 项



设 置 为 false。 意 , 步 请 求 将 锁 住 浏 览 器 , 户 其 它 操 作 必 须 等 待 请 求 完 成 才 可 以 执 行 。

注 同 用





beforeSend Function 如

发 送 请 求 前 可 修 改 XMLHttpRequest 对 象 的 函 数 , 添 加 自 定 义 HTTP 头 。XMLHttpRequest



对象是唯一的参数。



function (XMLHttpRequest) {



this; // the options for this ajax request



}





cache Boolean (默 认 : true) jQuery 1.2 新 功 能 , 设 置 为 false 将 不 会 从 浏 览 器 缓 存 中 加 载 请 求 信 息 。





complete Function 请 求 完 成 后 回 调 函 数 (请 求 成 功 或 失 败 时 均 调 用 )。参 数 : XMLHttpRequest 对 象 ,成 功



信息字符串。



function (XMLHttpRequest, textStatus) {



this; // the options for this ajax request



}





contentType String (默 认 : "application/x-www-form-urlencoded") 发 送 信 息 至 服 务 器 时 内 容 编 码 类 型 。 默



认值适合大多数应用场合。





data Object, 发 送 到 服 务 器 的 数 据 。将 自 动 转 换 为 请 求 字 符 串 格 式 。GET 请 求 中 将 附 加 在 URL 后 。查 看



String processData 选 项 说 明 以 禁 止 此 自 动 转 换 。必 须 为 Key/Value 格 式 。如 果 为 数 组 ,jQuery



将 自 动 为 不 同 值 对 应 同 一 个 名 称 。 如 {foo:["bar1", "bar2"]} 转 换 为



'&foo=bar1&foo=bar2'。





dataType String 预 期 服 务 器 返 回 的 数 据 类 型 。 如 果 不 指 定 , jQuery 将 自 动 根 据 HTTP 包 MIME 信 息 返 回



responseXML 或 responseText, 并 作 为 回 调 函 数 参 数 传 递 , 可 用 值 :



"xml": 返 回 XML 文 档 , 可 用 jQuery 处 理 。



"html": 返 回 纯 文 本 HTML 信 息 ; 包 含 script 元 素 。



"script": 返 回 纯 文 本 JavaScript 代 码 。 不 会 自 动 缓 存 结 果 。



"json": 返 回 JSON 数 据 。



"jsonp": JSONP 格 式 。 使 用 JSONP 形 式 调 用 函 数 时 , 如 "myurl?callback=?" jQuery 将 自 动 替



换 ? 为正确的函数名,以执行回调函数。





error Function (默 认 : 自 动 判 断 (xml 或 html)) 请 求 失 败 时 将 调 用 此 方 法 。 这 个 方 法 有 三 个 参 数 :



XMLHttpRequest 对 象 , 错 误 信 息 , ( 可 能 ) 捕 获 的 错 误 对 象 。



function (XMLHttpRequest, textStatus, errorThrown) {



// 通 常 情 况 下 textStatus 和 errorThown 只 有 其 中 一 个 有 值



this; // the options for this ajax request



}





global Boolean (默 认 : true) 是 否 触 发 全 局 AJAX 事 件 。 设 置 为 false 将 不 会 触 发 全 局 AJAX 事 件 , 如



ajaxStart 或 ajaxStop 。 可 用 于 控 制 不 同 的 Ajax 事 件





ifModified Boolean (默 认 : false) 仅 在 服 务 器 数 据 改 变 时 获 取 新 数 据 。使 用 HTTP 包 Last-Modified 头 信 息



判断。





processData Boolean (默 认 : true) 默 认 情 况 下 ,发 送 的 数 据 将 被 转 换 为 对 象 (技 术 上 讲 并 非 字 符 串 ) 以 配 合 默 认



内 容 类 型 "application/x-www-form-urlencoded"。 果 要 发 送 DOM 树 信 息 或 其 它 不



135





希 望 转 换 的 信 息 , 请 设 置 为 false。





success Function 请求成功后回调函数。这个方法有两个参数:服务器返回数据,返回状态



function (data, textStatus) {



// data could be xmlDoc, jsonObj, html, text, etc...



this; // the options for this ajax request



}





这 里 有 几 个 Ajax 事 件 参 数 :beforeSend ,success ,complete ,error 。 我 们 可 以 定 义 这 些 事 件 来 很 好 的 处 理 我 们 的 每 一 次 的 Ajax 请 求 。注 意 一 下 ,



这 些 Ajax 事 件 里 面 的 this 都 是 指 向 Ajax 请 求 的 选 项 信 息 的 (请 参 考 说 get() 方 法 时 的 this 的 图 片 )。



请 认 真 阅 读 上 面 的 参 数 列 表 , 如 果 你 要 用 jQuery 来 进 行 Ajax 开 发 , 那 么 这 些 参 数 你 都 必 需 熟 知 的 。



示例代码,获取博客园首页的文章题目 :



$.ajax({



type: "get",



url: "http://www.cnblogs.com/rss",



beforeSend: function(XMLHttpRequest){



//ShowLoading();



},



success: function(data, textStatus){



$(".ajax.ajaxResult").html("");



$("item",data).each(function(i, domEle){



$(".ajax.ajaxResult").append(""+$(domEle).children("title").text()+"");



});



},



complete: function(XMLHttpRequest, textStatus){



//HideLoading();



},



error: function(){



//请 求 出 错 处 理



}



});







147. 深入浅出 JSON







JSON 定 义



JSON(JavaScript Object Notation) 是 一 种 轻 量 级 的 数 据 交 换 格 式 ,易 于 阅 读 和 编 写 ,同 时 也 易 于 机 器 解 析 和 生 成 。它 基 于 ECMA262 语 言 规 范( 1999-12



中 但 (

第 三 版 ) JavaScript 编 程 语 言 的 一 个 子 集 。 JSON 采 用 与 编 程 语 言 无 关 的 文 本 格 式 , 是 也 使 用 了 类 C 语 言 包 括 C, C++, C#, Java, JavaScript, Perl,



Python 等 ) 的 习 惯 , 这 些 特 性 使 JSON 成 为 理 想 的 数 据 交 换 格 式 。



JSON 的 结 构 基 于 下 面 两 点



1. "名 称 /值 "对 的 集 合 不 同 语 言 中 , 它 被 理 解 为 对 象 (object), 记 录 (record), 结 构 (struct), 字 典 (dictionary), 哈 希 表 (hash table), 键 列 表 (keyed list)







2. 值 的 有 序 列 表 多 数 语 言 中 被 理 解 为 数 组 (array)



JSON 使 用 :



JSON 以 一 种 特 定 的 字 符 串 形 式 来 表 示 JavaScript 对 象 。 如 果 将 具 有 这 样 一 种 形 式 的 字 符 串 赋 给 任 意 一 个 JavaScript 变 量 , 那 么 该 变 量 会 变 成 一 个 对 象 引



用,而这个对象就是字符串所构建出来的,好像有点拗口,我们还是用实例来说 明。



这 里 假 设 我 们 需 要 创 建 一 个 User 对 象 , 并 具 有 以 下 属 性



 用 户 ID



 用户名



 用 户 Email



您 可 以 使 用 以 下 JSON 形 式 来 表 示 User 对 象 :

136



{"UserID":11, "Name":"Truly", "Email":"zhuleipro ◎ hotmail.com"};



然 后 如 果 把 这 一 字 符 串 赋 予 一 个 JavaScript 变 量 , 那 么 就 可 以 直 接 使 用 对 象 的 任 一 属 性 了 。



完整代码:







var User = {"UserID":11, "Name":"Truly", "Email":"zhuleipro◎ hotmail.com"};



alert(User.Name);







实 际 使 用 时 可 能 更 复 杂 一 点 , 比 如 我 们 为 Name 定 义 更 详 细 的 结 构 , 使 它 具 有 FirstName 和 LastName:



{"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro ◎ hotmail.com"}



完整代码:







var User = {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro ◎ hotmail.com"};



alert(User.Name.FirstName);







现在我们增加一个新的需求,我们某个页面需要一个用户列表,而不仅仅是一个单一的用户信息,那么这里就需要创建一个用户列表数组。



下 面 代 码 演 示 了 使 用 JSON 形 式 定 义 这 个 用 户 列 表 :



[



{"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro ◎ hotmail.com"},



{"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter"}, "Email":"xxx ◎ xxx.com"},



{"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu"}, "Email":"xxx2 ◎ xxx2.com"}



]







完整代码:







var UserList = [



{"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu"}, "Email":"zhuleipro ◎ hotmail.com"},



{"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter"}, "Email":"xxx ◎ xxx.com"},



{"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu"}, "Email ":"xxx2◎ xxx2.com"}



];



alert(UserList[0].Name.FirstName);







事 实 上 除 了 使 用 "."引 用 属 性 外 , 我 们 还 可 以 使 用 下 面 语 句 :



alert(UserList[0]["Name"]["FirstName"]); 或 者 alert(UserList[0].Name["FirstName"]);







现 在 读 者 应 该 对 JSON 的 使 用 有 点 认 识 了 , 归 纳 为 以 下 几 点 :



 对 象 是 属 性 、 值 对 的 集 合 。 一 个 对 象 的 开 始 于 “ {” , 结 束 于 “ }” 。 每 一 个 属 性 名 和 值 间 用 “ :” 提 示 , 属 性 间 用 “ ,” 分 隔 。



 数 组 是 有 顺 序 的 值 的 集 合 。 一 个 数 组 开 始 于 "[", 结 束 于 "]", 值 之 间 用 ","分 隔 。



 值 可 以 是 引 号 里 的 字 符 串 、 数 字 、 true、 false、 null, 也 可 以 是 对 象 或 数 组 。 这 些 结 构 都 能 嵌 套 。



 字 符 串 和 数 字 的 定 义 和 C 或 Java 基 本 一 致 。







本 文 通 过 一 个 实 例 演 示 , 初 步 了 解 了 JSON 的 强 大 用 途 。 可 以 归 结 如 下 :



 JSON 提 供 了 一 种 优 秀 的 面 向 对 象 的 方 法 , 以 便 将 元 数 据 缓 存 到 客 户 机 上 。



 JSON 帮 助 分 离 了 验 证 数 据 和 逻 辑 。



 JSON 帮 助 为 Web 应 用 程 序 提 供 了 Ajax 的 本 质 。







148. 在 UI 上使用 BackgroundWorker

凡 是 WinForm 的 应 用 程 序 , 如 果 他 执 行 了 一 个 的 非 常 冗 长 的 处 理 操 作 ( 比 如 文 件 查 询 ) , 它 在 执 行 时 会 锁 定 用 户 界 面 , 虽 然 主 活 动 窗 口 一 直 在 运 行 , 但 用



户无法与程序交互,无法移动窗体或改变窗体大小,所以用户感觉很不爽。如何做才能使得这个程序有响应。答案就是在后台线程中执行这个操作。



在这里已经有了多种方法来做这个事情:

137



(一 )委 托 异 步 调 用



将 具 体 耗 时 的 操 作 作 为 一 个 委 托 , 并 用 BeginInvoke 来 异 步 执 行 这 个 委 托 ( Invoke 是 同 步 调 用 ) , 并 且 可 以 为 这 个 操 作 传 入 参 数 并 且 通 过 EndInvoke 方



法获得返回返回值。



(二 )使 用 ThreadPool



新 建 .net FrameWork 中 自 带 的 WaitCallback 委 托 , 然 后 放 到 线 程 池 中 运 行 ThreadPool.QueueUserWorkItem( callback ); 根 据 WaitCallback 委 托



的 定 义 , 可 以 传 入 一 个 object 类 型 的 参 数 。



但是不能精确的控制线程池中的线程。



(三 )使 用 Thread



和 ThreadPool 相 比 ,使 用 Thread 的 开 销 会 比 较 大 。但 是 它 有 它 的 优 势 ,使 用 Thread 类 可 以 显 式 管 理 线 程 。只 要 有 可 能 ,就 应 该 使 用 ThreadPool



类 来 创 建 线 程 。 然 而 , 在 一 些 情 况 下 , 您 还 是 需 要 创 建 并 管 理 您 自 己 的 线 程 , 而 不 是 使 用 ThreadPool 类 。 在 .net 2.0 中 , 提 供 了 一 个 新 的 委 托



ParameterizedThreadStart 支 持 启 动 一 个 线 程 并 传 入 参 数 , 这 是 对 原 来 的 ThreadStart 委 托 的 改 进 。



说 了 这 么 多 还 没 有 说 到 今 天 的 主 角 BackgroundWorker, 他 也 是 一 个 在 2.0 中 新 增 的 类 ,可 以 用 于 启 动 后 台 线 程 ,并 在 后 台 计 算 结 束 后 调 用 主 线 程 的 方 法 .



可 以 看 出 同 样 的 功 能 使 用 委 托 的 异 步 调 用 也 可 以 实 现 ,只 是 使 用 BackgroundWorker 的 话 会 更 加 的 简 便 快 捷 ,可 以 节 省 开 发 时 间 ,并 把 你 从 创 建 自 己 的 委 托



以 及 对 它 们 的 调 用 中 解 救 出 来 。 真 是 这 样 的 吗 看 看 下 面 这 个 例 子 。 其 实 我 也 是 从 101Samples 中 看 到 的 例 子 。



先 看 看 BackgroundWorker 中 的 主 要 概 念 。



第一:主要的事件及参数。



DoWork— — 当 执 行 BackgroundWorker.RunWorkerAsync 方 法 时 会 触 发 该 事 件 , 并 且 传 递 DoWorkEventArgs 参 数 ;



ProgressChanged— — 操 作 处 理 中 获 得 的 处 理 状 态 变 化 , 通 过 BackgroundWorker.ReportProgress(int) 方 法 触 发 该 事 件 , 并 且 传 递



ProgressChangedEventArgs,其 中 包 含 了 处 理 的 百 分 比 ;









RunWorkerCompleted— — 异 步 操 作 完 成 后 会 触 发 该 事 件 , 然 如 果 需 要 在 操 作 过 程 中 结 束 可 以 执 行 BackgroundWorker.CancelAsync 方 法 要 求 异 步 调 用 中 止 ,



跳 同

并 且 在 异 步 委 托 操 作 中 检 测 BackgroundWorker.CancellationPending 属 性 如 果 为 true 的 话 , 出 异 步 调 用 , 时 将 DoWorkEventArgs.Cancel 属 性 设 为 true,



这 样 当 退 出 异 步 调 用 的 时 候 , 可 以 让 处 理 RunWorkerCompleted 事 件 的 函 数 知 道 是 正 常 退 出 还 是 中 途 退 出 。



第二:主要的方法。



BackgroundWorker.RunWorkerAsync— —



“ 起 动 ” 异 步 调 用 的 方 法 有 两 次 重 载 RunWorkerAsync(),RunWorkerAsync(object argument) , 第 二 个 重 载 提 供 了 一 个 参 数 , 可 以 供 异 步 调



用 使 用 。( 如 果 有 多 个 参 数 要 传 递 怎 么 办 ,使 用 一 个 类 来 传 递 他 们 吧 )。调 用 该 方 法 后 会 触 发 DoWork 事 件 ,并 且 为 处 理 DoWork 事 件 的 函 数 DoWorkEventArg 事



件 参 数 , 其 中 包 含 了 RunWorkerAsync 传 递 的 参 数 。 在 相 应 DoWork 的 处 理 函 数 中 就 可 以 做 具 体 的 复 杂 操 作 。



BackgroundWorker.ReportProgress — —



有 时 候 需 要 在 一 个 冗 长 的 操 作 中 向 用 户 不 断 反 馈 进 度 , 这 样 的 话 就 可 以 调 用 的 ReportProgress(int percent),在 调 用 ReportProgress 方



法 时 ,触 发 ProgressChanged 事 件 。提 供 一 个 在 0 到 100 之 间 的 整 数 ,它 表 示 后 台 活 动 已 完 成 的 百 分 比 。你 也 可 能 提 供 任 何 对 象 作 为 第 二 个 参 数 ,允 许 你 给



事 件 处 理 程 序 传 递 状 态 信 息 。 作 为 传 递 到 此 过 程 的 ProgressChangedEventArgs 参 数 属 性 , 百 分 比 和 你 自 己 的 对 象 ( 如 果 提 供 的 话 ) 均 要 被 传 递 到



ProgressChanged 事 件 处 理 程 序 。 这 些 属 性 被 分 别 命 名 为 ProgressPercentage 和 UserState, 并 且 你 的 事 件 处 理 程 序 可 以 以 任 何 需 要 的 方 式 使 用 它 们 。 (注



意 : 只 有 在 BackgroundWorker.WorkerReportsProgress 属 性 被 设 置 为 true 该 方 法 才 可 用 )。



BackgroundWorker.CancelAsync— —



但 需 要 退 出 异 步 调 用 的 时 候 , 就 调 用 的 这 个 方 法 。 但 是 样 还 不 够 , 因 为 它 仅 仅 是 将 BackgroudWorker.CancellationPending 属 性 设 置 为 true。 你 需 要 在



具 体 的 异 步 调 用 处 理 的 时 候 , 不 断 检 查 BackgroudWorker.CancellationPending 是 否 为 true, 如 果 是 真 的 话 就 退 出 。 (注 意 : 只 有 在



BackgroundWorker.WorkerSupportsCancellation 属 性 被 设 置 为 true 该 方 法 才 可 用 )。



贴 出 一 段 101Samples 里 面 的 代 码 , 看 一 下 就 明 白 了 :



public partial class MainForm : Form



{



private System.ComponentModel.BackgroundWorker backgroundCalculator;



public MainForm()



{



InitializeComponent();



backgroundCalculator = new BackgroundWorker();



backgroundCalculator.WorkerReportsProgress = true;



backgroundCalculator.WorkerSupportsCancellation = true;



backgroundCalculator.DoWork +=

138



new DoWorkEventHandler(backgroundCalculator_DoWork);



backgroundCalculator.ProgressChanged +=



new ProgressChangedEventHandler(backgroundCalculator_ProgressChanged);



backgroundCalculator.RunWorkerCompleted +=



new RunWorkerCompletedEventHandler(backgroundCalculator_RunWorkerCompleted);



updateStatus(String.Empty);



}



private int getNextPrimeAsync(int start, BackgroundWorker worker, DoWorkEventArgs e)



{



int percentComplete = 0;



start++;



while (!isPrime(start))



{



// Check for cancellation



if (worker.CancellationPending)



{



e.Cancel = true;



break;



}



else



{



start++;



percentComplete++;



worker.ReportProgress(percentComplete % 100);



}



}



return start;



}



void backgroundCalculator_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)



{



if (e.Cancelled)



{



updateStatus("Cancelled.");



}



else if (e.Error != null)



{



reportError(e.Error);



}



else



{



reportPrime((int)e.Result);



}



calcProgress.Value = 0;



}



void backgroundCalculator_ProgressChanged(object sender, ProgressChangedEventArgs e)



{



updateProgress(e.ProgressPercentage);



}



void backgroundCalculator_DoWork(object sender, DoWorkEventArgs e)

139



{



int start = (int) e.Argument;



e.Result = getNextPrimeAsync(start, (BackgroundWorker)sender, e);



}



private void nextPrimeAsyncButton_Click(object sender, EventArgs e)



{



updateStatus("Calculating...");



int start;



Int32.TryParse(textBoxPrime.Text, out start);



if (start == 0)



{



reportError("The number must be a valid integer");



}



else



{



// Kick off the background worker process



backgroundCalculator.RunWorkerAsync(int.Parse(textBoxPrime.Text));



}



}



private void cancelButton_Click(object sender, EventArgs e)



{



if (backgroundCalculator.IsBusy)



{



updateStatus("Cancelling...");



backgroundCalculator.CancelAsync();



}



}



// Update the Status label



private void updateStatus(string status)



{



calcStatus.Text = status;



}



// Indicate progress using progress bar



private void updateProgress(int percentComplete)



{



calcProgress.Value = percentComplete;



}



}







BackgroundWorker 创 建 自 己 的 委 托 并 调 用 这 个 窗 体 的 Invoke 方 法 来 运 行 它 , BackgroundWorker 组 件 以 一 种 优 雅 的 方 式 来 处 理 这 个 线 程 转 换 。



BackgroundWorker 组 件 允 许 你 从 后 台 线 程 中 调 用 它 的 ReportProgress 方 法 ,该 方 法 触 发 其 ProgressChanged 事 件 处 理 例 程 返 回 到 窗 体 的 线 程 中 。你 不 必 使



用 delegate/Invoke 方 法 自 己 处 理 这 个 线 程 转 换 , 而 是 调 用 ReportProgress, 其 余 的 事 情 交 给 组 件 来 做 。









149. 项目描述

Siebel Report



Novartis’ ETMS system is undergoing moving to Siebel System. These 24 Siebel reports retrieve data directly from Siebel database. The l inks



of 24 Siebel reports will be embedded in web pages of Siebel system. The project includes 24 reports and will use 3 tiers solu tion. Every report

140



will map to a page and an entity class. All entity classes will combine to be the business layer. Database layer includes all reporting stored



procedures and common stored procedures.



Followings techniques and tools are used in the project. Use ASP.NET 2.0 + Reporting Service (RDLC) to render

every report.

 ASP.NET 2.0

 Visual Studio 2005 with SP1

 Reporting Service (RDLC)

 SQLPlus

Waterfall software development life cycle model is followed during the process of Siebel Report development.

This development methodology contains the following stages:

 Requirements Gathering and Analysis

 System Design

 Coding and Unit Test

 System Test

 UAT (will be performed by Client)

Requirement Traceability Matrix (RTM) Document is used to make sure that specifications described in the FRS document are all covered completely.



This document is updated during each phase to help to verify and analyze that all the specifications are covered.



Object-Oriented design is employed in the system design. Requirements are converted from business model to individual components and finally



into class definition in the low level design.



He is responsible for the report design and sales module analysis and implemented.







CMS



Huaxia D&B is Shanghai Huaxia Dun & Bradstreet Business Information Consulting Co., Limited (HDBC), founded

in Dec 2006. It is the joint venture of D&B, the world leader in business information for more than 165 years,

and Huaxia Credit, a well recognized information provider in China.

Correspondents Management System (CMS) is a platform HDBC wants to implement to manage the activities of “Place

tickets to agents”, “Delivery from agents” and “Billing the tickets to agents”.

HDBC wants to build the system into a web application by using .NET technique and SQL Server 2005.

We gathered some high level requirements through meetings with the IT and Business team of HDBC. We then do

an proposal for an IT solution to the CMS.

It contains 6 main function modules:

 Place tickets

 Deliver tickets

 Billing of ticket

 Integrated with OPAL system

 Common data management

 System Reports

I am responsible for calculation module design and coding, report design.





DIT-C Tool



DIT-C Tool is an application which helps the clients of Huaxia D&B to transfer the standard DIT’s

XML data to specific customer-formatted XML file. Huaxia D&B wants to build it into a Desktop application

using .NET technique.

The running environment of this tool should be Windows XP/2000 and .Net framework 3.5 or above

should be pre-installed.



I am responsible for transfer tracking and logging module.









CareScore

141



Care score is an internal employee appraisal system for Huaxia D&B. This system is originally developed

under Access and written by VBA. This system allows the QAA to do the appraisal for BA or DEO based

on the Duns he or she gets. The BA/DEO leader and Manager should be able to review the appraisal result

reports for the BA/DEO. Currently the client wants to update the system to ASP.NET and SQL server 2000.

Care Score have Admin module, Report Module and Do Appraisal module. Admin module provides facility

to manage the user information and report type. The Do Appraisal module provides the facility to create

new appraisal for BA/DEO and edit existing data. Report module provides the ability to view and generate

BA summary report, BA Compare report, and DEO summary report and QAA performance report.

I am responsible for evaluation page implementing and designing report.



PRDBR



PRDB Rewrite project is a reengineering project. Nestle once used an Access based application called

Project Request Database (PRDB) to serve as a single repository for tracking and accelerate the approval

process of Projects and Enhancements. This existing application is re-engineered with .Net framework

2.0. It provide user friendly interface in order to fulfill all the functions and to ease the effort

for maintenance. This is a web application which uses SQL Server 2005 as the back end.

He is responsible for calculation module, report implementing and mail module.







150. ERP

ERP 是 英 文 Enterprise Resourse Planning 的 缩 写 , 中 文 意 思 是 企 业 资 源 规 划 。 它 是 一 个 以 管 理 会 计 为 核 心 的 信 息 系 统 , 识 别 和 规 划 企 业 资 源 , 从 而 获 取 客 户



订 单 , 完 成 加 工 和 交 付 , 最 后 得 到 客 户 付 款 。 ERP 将 企 业 内 部 所 有 资 源 整 合 在 一 起 , 对 采 购 、 生 产 、 成 本 、 库 存 、 分 销 、 运 输 、 财 务 、 人 力 资 源 进 行 规 划 , 从



而达到最佳资源组合,取得最佳效益。







151. 设计模式

创建型模式:



工厂方法定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类。



抽象工厂提供一个创建一系列或相关依赖对象的接口,而无需指定他们具体的类。



建造者将一个复杂对象的构建与它的的表示分离,使得同样的构建过程可以创建不同的表示。 将一个复杂对象的构建与它的表示分离,用同样的构建过程



创建不同的产品。



原型是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新对象。建立相应数目的原型并克隆它们通常比每次用合适的状态手工实例化该类更



方便一些。



单例保证一个类仅有一个实例,并提供一个访问它的全局访问点。 对一些类来说,一个实例是很重要的。一个全局变量可以使得一个对象被访问,但它不



能 防 止 客 户 实 例 化 多 个 对 象 。单 例 模 式 让 类 自 身 负 责 保 存 它 唯 一 实 例 。这 个 类 可 以 保 证 没 有 其 他 实 例 可 以 被 创 建 ,并 且 提 供 了 一 个 访 问 该 实 例 的 方 法 。对 唯 一



的实例可以严格地控制客户怎样以及何时访问它。



创建型模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的是 由抽象类所定义的接口。这样,创建型模式在创建了什么、



谁 创 建 它 、它 是 怎 么 被 创 建 的 ,以 及 何 时 创 建 这 些 方 面 提 供 了 很 大 的 灵 活 性 。创 建 型 模 式 抽 象 了 实 例 化 的 过 程 。他 们 帮 助 一 个 系 统 独 立 于 如 何 创 建 、组 合 和 表



示它的那些对象。创建型模式都会将关于该系统使用哪些具体的类的信息封装起来。



内聚性描述的是一个例程内部组成部分之间相互联系的紧密程度。而耦合性描述的是一个例程与其他例程之间联系的紧密程度。软件开发的目标应该是创



建这样的例程:内部完整,也就是高内聚,而与其他例程之间的联系则是小巧、直接、可见、灵活的,这就是松耦合。







结构型模式



适 配 器 (Adapter)将 一 个 类 的 接 口 转 换 成 客 户 希 望 的 另 一 个 接 口 。 适 配 器 模 式 使 得 原 本 由 于 接 口 不 兼 容 而 不 能 一 起 工 作 的 哪 些 类 可 以 一 起 工 作 。



桥 接 (Bridge)将 抽 象 部 分 与 它 的 实 现 部 分 分 离 , 使 它 们 都 可 以 独 立 变 化 。



组 合 (Composite)将 对 象 组 合 成 树 形 结 构 以 表 示 ‘ 部 分 -整 体 ’ 的 层 次 结 构 , 组 合 模 式 使 得 用 户 对 单 个 对 象 的 使 用 具 有 一 致 性 。



装 饰 (Decorator)动 态 地 给 一 个 对 象 添 加 一 些 额 外 的 职 责 。 就 增 加 功 能 来 说 , 装 饰 模 式 相 比 生 成 子 类 更 加 灵 活 。



外 观 (Facade)为 子 系 统 中 的 一 组 接 口 提 供 一 个 一 致 的 界 面 , 外 观 模 式 定 义 了 一 个 高 层 接 口 , 这 个 接 口 使 得 这 一 子 系 统 更 容 易 使 用 。



享 元 (Flyweight)为 运 用 共 享 技 术 有 效 地 支 持 大 量 细 粒 度 的 对 象 。



代 理 (Proxy)为 其 他 对 象 提 供 一 种 代 理 以 控 制 对 这 个 对 象 的 访 问 。







代理与外观的主要区别在于,代理对象代表一个单一对象而外观对象代表一个子系统;代理的客户对象无法直接直接访问目标对象,由代理提供对单独的



目标对象的访问控制,而外观的客户对象可以直接访问子系统中的各个对象, 但通常由外观对象提供对子系统各元件功能的简化的共同层次的调用接口。

142



代 理 是 一 种 原 来 对 象 的 代 表 ,其 他 需 要 与 这 个 对 象 打 交 道 的 操 作 都 是 和 这 个 代 表 交 涉 。而 适 配 器 则 不 需 要 虚 构 出 一 个 代 表 者 ,只 需 要 为 应 付 特 定 使 用 目 的 ,



将原来的类进行一些组合。









行为型模式



观 察 者 (Observer)定 义 对 象 间 的 一 种 一 对 多 的 依 赖 关 系 , 当 一 个 对 象 的 状 态 发 生 改 变 时 , 所 有 依 赖 于 它 的 对 象 都 得 到 通 知 并 被 自 动 更 新 。



模 板 方 法 (TemplateMethod)定 义 一 个 操 作 的 算 法 骨 架 , 而 将 一 些 步 骤 延 迟 到 子 类 中 , 模 板 方 法 使 得 子 类 可 以 不 改 变 一 个 算 法 结 构 即 可 以 重 定 义 该 算 法 的 某



些特定步骤。



命 令 (Command)将 一 个 请 求 封 装 为 一 个 对 象 , 从 而 使 你 可 用 不 同 的 请 求 对 客 户 进 行 参 数 化 ; 可 以 对 请 求 排 队 或 记 录 请 求 日 志 , 以 及 支 持 可 撤 销 的 操 作 。



状 态 (State)允 许 一 个 对 象 在 其 内 部 状 态 改 变 时 改 变 它 的 行 为 , 让 其 看 起 来 似 乎 修 改 了 它 的 类 。



职 责 链 (Chain of Responsiblilty)使 多 个 对 象 都 有 机 会 处 理 请 求 , 从 而 避 免 请 求 的 发 送 者 和 接 受 者 之 间 的 耦 合 关 系 。 将 这 些 对 象 连 成 一 条 链 , 并 沿 着 这 条



链传递该请求,直到有一个对象处理它为止。



解 析 器 (Interpreter)给 定 一 个 语 言 , 定 义 它 的 文 法 的 一 种 表 示 , 并 定 义 一 个 解 释 器 , 这 个 解 释 器 使 用 该 表 示 来 解 释 语 言 的 句 子 。



中 介 者 (Mediator)用 一 个 中 介 对 象 来 封 装 一 系 列 的 对 象 交 互 。 中 介 者 使 各 对 象 不 需 要 显 式 地 相 互 引 用 , 从 而 使 耦 合 松 散 , 而 且 可 以 独 立 地 改 变 它 们 之 间 的



交互。



访 问 者 (Visitor)表 示 一 个 作 用 于 某 对 象 结 构 中 的 各 个 元 素 的 操 作 。 它 使 你 可 以 在 不 改 变 各 元 素 的 类 的 前 提 下 定 义 作 用 于 这 些 元 素 的 新 操 作 。



策 略 (Strategy)定 义 一 系 列 的 算 法 , 把 它 们 一 个 个 封 装 起 来 , 并 且 使 他 们 可 以 相 互 替 换 , 使 算 法 可 以 独 立 于 使 用 它 的 客 户 而 变 化 。









152. 数据库索引

一、引言



对 数 据 库 索 引 的 关 注 从 未 淡 出 我 的 们 的 讨 论 ,那 么 数 据 库 索 引 是 什 么 样 的 ? 聚 集 索 引 与 非 聚 集 索 引 有 什 么 不 同 ? 希 望 本 文 对 各 位 同 仁 有 一 定 的 帮 助 。有 不 少 存



疑 的 地 方 , 诚 心 希 望 各 位 不 吝 赐 教 指 正 , 共 同 进 步 。 [最 近 首 页 之 争 沸 沸 扬 扬 , 也 不 知 道 这 个 放 在 这 合 适 么 , 苦 劳 ? 功 劳 ? … … ]







二 、 B-Tree



其 例

我 们 常 见 的 数 据 库 系 统 , 索 引 使 用 的 数 据 结 构 多 是 B-Tree 或 者 B+Tree。 如 ,MsSql 使 用 的 是 B+Tree, 所

Oracle 及 Sysbase 使 用 的 是 B-Tree。 以 在 最 开 始 ,



简 单 地 介 绍 一 下 B-Tree。



B-Tree 不 同 于 Binary Tree( 二 叉 树 , 最 多 有 两 个 子 树 ) , 一 棵 M 阶 的 B-Tree 满 足 以 下 条 件 :



1) 每 个 结 点 至 多 有 M 个 孩 子 ;



2) 除 根 结 点 和 叶 结 点 外 , 其 它 每 个 结 点 至 少 有 M/2 个 孩 子 ;



3) 根 结 点 至 少 有 两 个 孩 子 ( 除 非 该 树 仅 包 含 一 个 结 点 ) ;



4) 所 有 叶 结 点 在 同 一 层 , 叶 结 点 不 包 含 任 何 关 键 字 信 息 ;



5) 有 K 个 关 键 字 的 非 叶 结 点 恰 好 包 含 K+1 个 孩 子 ;



另 外 , 对 于 一 个 结 点 , 其 内 部 的 关 键 字 是 从 小 到 大 排 序 的 。 以 下 是 B-Tree( M=4) 的 样 例 :









对 于 每 个 结 点 , 主 要 包 含 一 个 关 键 字 数 组 Key[], 一 个 指 针 数 组 ( 指 向 儿 子 ) Son[]。 在 B-Tree 内 , 查 找 的 流 程 是 : 使 用 顺 序 查 找 ( 数 组 长 度 较 短 时 ) 或 折 半



查 找 方 法 查 找 Key[]数 组 ,若 找 到 关 键 字 K,则 返 回 该 结 点 的 地 址 及 K 在 Key[]中 的 位 置 ;否 则 ,可 确 定 K 在 某 个 Key[i]和 Key[i+1]之 间 ,则 从 Son[i]所 指 的



子结点继续查找,直到在某结点中查找成功;或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。



接 着 , 我 们 使 用 以 下 图 片 演 示 如 何 生 成 B-Tree( M=4, 依 次 插 入 1~6) :



从 图 可 见 ,当 我 们 插 入 关 键 字 4 时 ,由 于 原 结 点 已 经 满 了 ,故 进 行 分 裂 ,基 本 按 一 半 的 原 则 进 行 分 裂 ,然 后 取 出 中 间 的 关 键 字 2,升 级( 这 里 是 成 为 根 结 点 )。



其它的依类推,就是这样一个大概的过程。

143









三、数据库索引



1. 什 么 是 索 引



在数据库中,索引的含义与日常意义上的“索引”一词并无多大区别(想想小时候查字典),它是用于提高数据库表数据访问速度的数据库对象。



A) 索 引 可 以 避 免 全 表 扫 描 。 多 数 查 询 可 以 仅 扫 描 少 量 索 引 页 及 数 据 页 , 而 不 是 遍 历 所 有 数 据 页 。



B) 对 于 非 聚 集 索 引 , 有 些 查 询 甚 至 可 以 不 访 问 数 据 页 。



C) 聚 集 索 引 可 以 避 免 数 据 插 入 操 作 集 中 于 表 的 最 后 一 个 数 据 页 。



D) 一 些 情 况 下 , 索 引 还 可 用 于 避 免 排 序 操 作 。



当然,众所周知,虽然索引可以提高查询速度,但是它们也会导致数据库系统更新数据的性能下降,因为大部分数据更新需要同时更新索引。







2.索 引 的 存 储



一 条 索 引 记 录 中 包 含 的 基 本 信 息 包 括 : 键 值 ( 即 你 定 义 索 引 时 指 定 的 所 有 字 段 的 值 ) +逻 辑 指 针 ( 指 向 数 据 页 或 者 另 一 索 引 页 ) 。









当 你 为 一 张 空 表 创 建 索 引 时 ,数 据 库 系 统 将 为 你 分 配 一 个 索 引 页 ,该 索 引 页 在 你 插 入 数 据 前 一 直 是 空 的 。此 页 此 时 既 是 根 结 点 ,也 是 叶 结 点 。每 当 你 往 表 中 插



入一行数据,数据库系统即向此根结点中插入一行索 引记录。当根结点满时,数据库系统大抵按以下步骤进行分裂:



A) 创 建 两 个 儿 子 结 点



B) 将 原 根 结 点 中 的 数 据 近 似 地 拆 成 两 半 , 分 别 写 入 新 的 两 个 儿 子 结 点



C) 根 结 点 中 加 上 指 向 两 个 儿 子 结 点 的 指 针



通 常 状 况 下 ,由 于 索 引 记 录 仅 包 含 索 引 字 段 值( 以 及 4-9 字 节 的 指 针 ),索 引 实 体 比 真 实 的 数 据 行 要 小 许 多 ,索 引 页 相 较 数 据 页 来 说 要 密 集 许 多 。一 个 索 引 页



可 以 存 储 数 量 更 多 的 索 引 记 录 , 这 意 味 着 在 索 引 中 查 找 时 在 I/O 上 占 很 大 的 优 势 , 理 解 这 一 点 有 助 于 从 本 质 上 了 解 使 用 索 引 的 优 势 。







3. 索 引 的 类 型



A) 聚 集 索 引 , 表 数 据 按 照 索 引 的 顺 序 来 存 储 的 。 对 于 聚 集 索 引 , 叶 子 结 点 即 存 储 了 真 实 的 数 据 行 , 不 再 有 另 外 单 独 的 数 据 页 。



B) 非 聚 集 索 引 , 表 数 据 存 储 顺 序 与 索 引 顺 序 无 关 。 对 于 非 聚 集 索 引 , 叶 结 点 包 含 索 引 字 段 值 及 指 向 数 据 页 数 据 行 的 逻 辑 指 针 , 该 层 紧 邻 数 据 页 , 其 行 数 量 与



数据表行数据量一致。



在 一 张 表 上 只 能 创 建 一 个 聚 集 索 引 ,因 为 真 实 数 据 的 物 理 顺 序 只 可 能 是 一 种 。如 果 一 张 表 没 有 聚 集 索 引 ,那 么 它 被 称 为 “ 堆 集 ”( Heap)。这 样 的 表 中 的 数 据



行没有特定的顺序,所有的新行将被添加的表的末尾位置。







4. 聚 集 索 引



在聚集索引中,叶结点也即数据结点,所有数据行的存储顺序与索引的存储顺序一致。

144









1) 聚 集 索 引 与 查 询 操 作



如 上 图 ,我 们 在 名 字 字 段 上 建 立 聚 集 索 引 ,当 需 要 在 根 据 此 字 段 查 找 特 定 的 记 录 时 ,数 据 库 系 统 会 根 据 特 定 的 系 统 表 查 找 的 此 索 引 的 根 ,然 后 根 据 指 针 查 找 下



一 个 , 直 到 找 到 。 例 如 我 们 要 查 询 “ Green” , 由 于 它 介 于 [Bennet,Karsen], 据 此 我 们 找 到 了 索 引 页 1007, 在 该 页 中 “ Green” 介 于 [Greane, Hunter]间 , 据



此 我 们 找 到 叶 结 点 1133( 也 即 数 据 结 点 ) , 并 最 终 在 此 页 中 找 以 了 目 标 数 据 行 。



此 次 查 询 的 IO 包 括 3 个 索 引 页 的 查 询 ( 其 中 最 后 一 次 实 际 上 是 在 数 据 页 中 查 询 ) 。 这 里 的 查 找 可 能 是 从 磁 盘 读 取 (Physical Read)或 是 从 缓 存 中 读 取 (Logical



Read), 如 果 此 表 访 问 频 率 较 高 , 那 么 索 引 树 中 较 高 层 的 索 引 很 可 能 在 缓 存 中 被 找 到 。 所 以 真 正 的 IO 可 能 小 于 上 面 的 情 况 。







2) 聚 集 索 引 与 插 入 操 作



最简单的情况下,插入操作根据索引找到对应的数据页,然后通过挪动已有的记录为新数据腾出空间,最后插入数据。



如 果 数 据 页 已 满 ,则 需 要 拆 分 数 据 页( 页 拆 分 是 一 种 耗 费 资 源 的 操 作 ,一 般 数 据 库 系 统 中 会 有 相 应 的 机 制 要 尽 量 减 少 页 拆 分 的 次 数 ,通 常 是 通 过 为 每 页 预 留 空



间来实现):



A) 在 该 使 用 的 数 据 段 ( extent) 上 分 配 新 的 数 据 页 , 如 果 数 据 段 已 满 , 则 需 要 分 配 新 段 。



B) 调 整 索 引 指 针 , 这 需 要 将 相 应 的 索 引 页 读 入 内 存 并 加 锁 。



C) 大 约 有 一 半 的 数 据 行 被 归 入 新 的 数 据 页 中 。



D) 如 果 表 还 有 非 聚 集 索 引 , 则 需 要 更 新 这 些 索 引 指 向 新 的 数 据 页 。



特殊情况:



A) 如 果 新 插 入 的 一 条 记 录 包 含 很 大 的 数 据 , 可 能 会 分 配 两 个 新 数 据 页 , 其 中 之 一 用 来 存 储 新 记 录 , 另 一 存 储 从 原 页 中 拆 分 出 来 的 数 据 。



B) 通 常 数 据 库 系 统 中 会 将 重 复 的 数 据 记 录 存 储 于 相 同 的 页 中 。



C) 类 似 于 自 增 列 为 聚 集 索 引 的 , 数 据 库 系 统 可 能 并 不 拆 分 数 据 页 , 页 只 是 简 单 的 新 添 数 据 页 。







3) 聚 集 索 引 与 删 除 操 作



删除行将导致其下方的数据行向上移动以填充删除记录造成的空白。



如果删除的行是该数据页中的最后一行,那么该数据页将被回收,相应的索引页中的记录将被删除。如果回收的数据页 位 于 跟 该 表的 其 它 数 据 页相 同 的 段上 ,



那么它可能在随后的时间内被利用。如果该数据页是该段的唯一一个数据页,则该段也被回收。



对于数据的删除操作,可能导致索引页中仅有一条记录,这时,该记录可能会被移至邻近的索引页中,原索引页将被回收,即所谓的“索引合并”。







5. 非 聚 集 索 引



非聚集索引与聚集索引相比:



A) 叶 子 结 点 并 非 数 据 结 点



B) 叶 子 结 点 为 每 一 真 正 的 数 据 行 存 储 一 个 “ 键 -指 针 ” 对



C) 叶 子 结 点 中 还 存 储 了 一 个 指 针 偏 移 量 , 根 据 页 指 针 及 指 针 偏 移 量 可 以 定 位 到 具 体 的 数 据 行 。



D) 类 似 的 , 在 除 叶 结 点 外 的 其 它 索 引 结 点 , 存 储 的 也 是 类 似 的 内 容 , 只 不 过 它 是 指 向 下 一 级 的 索 引 页 的 。



聚 集 索 引 是 一 种 稀 疏 索 引 ,数 据 页 上 一 级 的 索 引 页 存 储 的 是 页 指 针 ,而 不 是 行 指 针 。而 对 于 非 聚 集 索 引 ,则 是 密 集 索 引 ,在 数 据 页 的 上 一 级 索 引 页 它 为 每 一 个



数据行存储一条索引记录。



对于根与中间级的索引记录,它的结构包括:



A) 索 引 字 段 值



B) RowId( 即 对 应 数 据 页 的 页 指 针 +指 针 偏 移 量 ) 。 在 高 层 的 索 引 页 中 包 含 RowId 是 为 了 当 索 引 允 许 重 复 值 时 , 当 更 改 数 据 时 精 确 定 位 数 据 行 。

145



C) 下 一 级 索 引 页 的 指 针



对于叶子层的索引对象,它的结构包括:



A) 索 引 字 段 值



B) RowId









1) 非 聚 集 索 引 与 查 询 操 作



针 对 上 图 ,如 果 我 们 同 样 查 找“ Green”,那 么 一 次 查 询 操 作 将 包 含 以 下 IO:3 个 索 引 页 的 读 取 +1 个 数 据 页 的 读 取 。同 样 ,由 于 缓 存 的 关 系 ,真 实 的 IO 实 际 可



能要小于上面列出的。







2) 非 聚 集 索 引 与 插 入 操 作



如 果 一 张 表 包 含 一 个 非 聚 集 索 引 但 没 有 聚 集 索 引 ,则 新 的 数 据 将 被 插 入 到 最 末 一 个 数 据 页 中 ,然 后 非 聚 集 索 引 将 被 更 新 。如 果 也 包 含 聚 集 索 引 ,该 聚 集 索 引 将



被用于查找新行将要处于什么位置,随后,聚集索引、以及非聚集索引将被更新。







3) 非 聚 集 索 引 与 删 除 操 作



如 果 在 删 除 命 令 的 Where 子 句 中 包 含 的 列 上 ,建 有 非 聚 集 索 引 ,那 么 该 非 聚 集 索 引 将 被 用 于 查 找 数 据 行 的 位 置 ,数 据 删 除 之 后 ,位 于 索 引 叶 子 上 的 对 应 记 录 也



将被删除。如果该表上有其它非聚集索引,则它们叶子结点上的相应数据也要删除。



如果删除的数据是该数所页中的唯一一条,则该页也被回收,同时需要更新各个索引树上的指针。



由于没有自动的合并功能,如果应用程序中有频繁的随机删除操作,最后可能导致表包含多个数据页,但每个页中只有少量数据。







6. 索 引 覆 盖



索引覆盖是这样一种索引策略:当某一查询中包含的所需字段皆包含于一个索引中,此时索引将大大提高查询性 能。



包 含 多 个 字 段 的 索 引 , 称 为 复 合 索 引 。 索 引 最 多 可 以 包 含 31 个 字 段 , 索 引 记 录 最 大 长 度 为 600B。 如 果 你 在 若 干 个 字 段 上 创 建 了 一 个 复 合 的 非 聚 集 索 引 , 且 你



的 查 询 中 所 需 Select 字 段 及 Where,Order By,Group By,Having 子 句 中 所 涉 及 的 字 段 都 包 含 在 索 引 中 , 则 只 搜 索 索 引 页 即 可 满 足 查 询 , 而 不 需 要 访 问 数 据 页 。



由于非聚集索引的叶结点包含所有数据行中的索引列值,使用这些结点即可返回真正的数据,这种情况称之为 “索引覆盖”。



在索引覆盖的情况下,包含两种索引扫描:



A) 匹 配 索 引 扫 描



B) 非 匹 配 索 引 扫 描







1) 匹 配 索 引 扫 描



此 类 索 引 扫 描 可 以 让 我 们 省 去 访 问 数 据 页 的 步 骤 ,当 查 询 仅 返 回 一 行 数 据 时 ,性 能 提 高 是 有 限 的 ,但 在 范 围 查 询 的 情 况 下 ,性 能 提 高 将 随 结 果 集 数 量 的 增 长 而



增长。



针 对 此 类 扫 描 , 索 引 必 须 包 含 查 询 中 涉 及 的 的 所 有 字 段 , 另 外 , 还 需 要 满 足 : Where 子 句 中 包 含 索 引 中 的 “ 引 导 列 ” ( Leading Column) , 例 如 一 个 复 合 索 引



包 含 A,B,C,D 四 列 , 则 A 为 “ 引 导 列 ” 。 如 果 Where 子 句 中 所 包 含 列 是 BCD 或 者 BD 等 情 况 , 则 只 能 使 用 非 匹 配 索 引 扫 描 。







2) 非 配 置 索 引 扫 描



正 如 上 述 ,如 果 Where 子 句 中 不 包 含 索 引 的 导 引 列 ,那 么 将 使 用 非 配 置 索 引 扫 描 。这 最 终 导 致 扫 描 索 引 树 上 的 所 有 叶 子 结 点 ,当 然 ,它 的 性 能 通 常 仍 强 于 扫 描



所有的数据页。

146





153. Oracle 游标

一.概念:



游标:用来查询数据库,获取记录集合(结果集)的指针,可以让开发者一次访问一行结果集,在每条结果集上作操作。



二.分类:



1. 静 态 游 标 :



分为显式游标和隐式游标。



2. REF 游 标 :



是一种引用类型,类似于指针。



三.详细内容:



1. 显 式 游 标 :



CURSOR 游 标 名 (参 数 ) [返 回 值 类 型 ] IS



Select 语 句



生命周期:



a.打 开 游 标 (OPEN)



解析,绑定。。。不会从数据库检索数据



b.从 游 标 中 获 取 记 录 (FETCH INTO)



执行查询,返回结果集。通常定义局域变量作为从游标获取数据的缓冲区。



c.关 闭 游 标 (CLOSE)



完成游标处理,用户不能从游标中获取行。还可以重新打开。



选项:参数和返回类型



set serveroutput on



declare



cursor emp_cur ( p_deptid in number) is



select * from employees where department_id = p_deptid;



l_emp employees%rowtype;



begin



dbms_output.put_line('Getting employees from department 30');



open emp_cur(30);



loop



fetch emp_cur into l_emp;



exit when emp_cur%notfound;



dbms_output.put_line('Employee id '|| l_emp.employee_id || ' is ');



dbms_output.put_line(l_emp.first_name || ' ' || l_emp.last_name);



end loop;



close emp_cur;



dbms_output.put_line('Getting employees from department 90');



open emp_cur(90);



loop



fetch emp_cur into l_emp;



exit when emp_cur%notfound;



dbms_output.put_line('Employee id '|| l_emp.employee_id || ' is ');



dbms_output.put_line(l_emp.first_name || ' ' || l_emp.last_name);



end loop;



close emp_cur;



end;



/







2. 隐 式 游 标 :



不用明确建立游标变量,分两种:

147



a.在 PL/SQL 中 使 用 DML 语 言 , 使 用 ORACLE 提 供 的 名 为 SQL 的 隐 示 游 标



b.CURSOR FOR LOOP, 用 于 for loop 语 句



a.举 例 :



declare



begin



update departments set department_name=department_name;



--where 1=2;







dbms_output.put_line('update '|| sql%rowcount ||' records');



end;



/







b.举 例 :



declare



begin



for my_dept_rec in ( select department_name, department_id from departments)



loop



dbms_output.put_line(my_dept_rec.department_id || ' : ' || my_dept_rec.department_name);



end loop;



end;



/







c.举 例 :



单 独 select



declare



l_empno emp.EMPLOYEE_ID%type;



-- l_ename emp.ename%type;



begin



select EMPLOYEE_ID



into l_empno



from emp;



--where rownum =1;



dbms_output.put_line(l_empno);



end;



/



使 用 INTO 获 取 值 , 只 能 返 回 一 行 。







游标属性:



%FOUND: 变 量 最 后 从 游 标 中 获 取 记 录 的 时 候 , 在 结 果 集 中 找 到 了 记 录 。



%NOTFOUND: 变 量 最 后 从 游 标 中 获 取 记 录 的 时 候 , 在 结 果 集 中 没 有 找 到 记 录 。



%ROWCOUNT: 当 前 时 刻 已 经 从 游 标 中 获 取 的 记 录 数 量 。



%ISOPEN: 是 否 打 开 。







Declare



Cursor emps is



Select * from employees where rownumschema->table 后 ,如 果 在 现 有 的 分 区 表 上 建 立 没 有 显 式 声 明 的 聚 集 索 引 时 ,分



区表会自动变为非分区表。这一点很让我纳闷。如果你觉得我的非分区索引无法对起子分区,



你 可 以 提 醒 我 一 下 呀 ! 没 有 任 何 的 提 醒 , 直 接 就 变 成 了 非 分 区 表 。 不 知 道 这 算 不 算 一 个 bug。 大 家 也 可 以 试 试 。

153



分 区 表 效 率 问 题 肯 定 是 大 家 关 心 的 问 题 。在 我 的 试 验 中 ,如 果 按 照 分 区 字 段 进 行 的 查 询( 过 滤 )效 率 会 高 于 未 分 区 表 的 相 同 语 句 。但 是 如 果 按 照 非 分 区 字 段 进



行 查 询 , 效 率 会 低 于 未 分 区 表 的 相 同 语 句 。 但 是 随 着 数 据 量 的 增 大 , 这 种 成 本 差 距 会 逐 渐 减 小 , 趋 于 相 等 。 ( 500 万 数 量 级 只 相 差 10%左 右 )







6、 CLR 类 型







微 软 对 CLR 作 了 大 篇 幅 的 宣 传 , 这 是 因 为 数 据 库 产 品 终 于 融 入 .net 体 系 中 。 最 开 始 我 们 也 是 狂 喜 , 感 觉 对 象 数 据 库 的 一 些 概 念 可 以 实 现 了 。 但 是 作 了 些 试 验 ,



发 现 使 用 CLR 的 存 储 过 程 或 函 数 在 达 到 一 定 的 阀 值 的 时 候 ,系 统 性 能 会 呈 指 数 级 下 滑 !这 是 非 常 危 险 的 !只 使 用 几 个 可 能 没 有 问 题 ,当 一 旦 大 规 模 使 用 会 造 成



严重的系统性能问题!







其 实 可 以 做 一 下 类 比 , Oracle 等 数 据 库 产 品 老 早 就 支 持 了 java 编 程 , 而 且 提 供 了 java 池 参 数 作 为 用 户 配 置 接 口 。 但 是 现 在 有 哪 些 系 统 大 批 使 用 了 java 存 储



过 程 ? ! 连 Oracle 自 己 的 应 用 都 不 用 为 什 么 ? ! 还 不 是 性 能 有 问 题 ! 否 则 面 向 对 象 的 数 据 库 早 就 实 现 了 !







建 议 使 用 CLR 的 地 方 一 般 是 和 应 用 的 复 杂 程 度 或 操 作 系 统 环 境 有 很 高 的 耦 合 度 的 场 景 。如 你 想 构 建 复 杂 的 算 法 ,并 且 用 到 了 大 量 的 指 针 和 高 级 数 据 模 型 。或 者



是 要 和 操 作 系 统 进 行 Socket 通 讯 的 场 景 。 否 则 建 议 慎 重 !







7、 索 引 视 图







索 引 视 图 2k 就 有 。但 是 2005 对 其 效 率 作 了 一 些 改 进 但 是 schema.viewname 的 作 用 域 真 是 太 限 制 了 它 的 应 用 面 。还 有 一 大 堆 的 环 境 参 数 和 种 种 限 制 都 让 人 对 它



有点却步。







8、 语 句 和 事 务 快 照







语 句 级 快 照 和 事 务 级 快 照 终 于 为 SQL Server 的 并 发 性 能 带 来 了 突 破 。个 人 感 觉 语 句 级 快 照 大 家 应 该 应 用 。事 务 级 快 照 ,如 果 是 高 并 发 系 统 还 要 慎 用 。如 果 一



个用户总是被提示修改不成功要求重试时,会杀人的!







9、 数 据 库 快 照







原 理 很 简 单 , 对 要 求 长 时 间 计 算 某 一 时 间 点 的 报 表 生 成 和 防 用 户 操 作 错 误 很 有 帮 助 。 但 是 比 起 Oracle10g 的 闪 回 技 术 还 是 细 粒 度 不 够 。 可 惜 !







10、 Mirror



Mirror 可 以 算 是 SQL Server 的 Data guard 了 。 但 是 能 不 能 被 大 伙 用 起 来 就 不 知 道 了 。







二、开发方面







1、 Ranking 函 数 集



其 中 最 有 名 的 应 该 是 row_number 了 。这 个 终 于 解 决 了 用 临 时 表 生 成 序 列 号 的 历 史 ,而 且 SQL Server2005 的 row_number 比 Oracle 的 更 先 进 。因 为 它 把 Order



by 集 成 到 了 一 起 , 不 用 像 Oracle 那 样 还 要 用 子 查 询 进 行 封 装 。 但 是 大 家 注 意 一 点 。 如 下 面 的 例 子 :







select ROW_NUMBER() OVER (order by aa)



from tbl



order by bb







会 先 执 行 aa 的 排 序 , 然 后 再 进 行 bb 的 排 序 。







可 能 有 的 朋 友 会 抱 怨 集 成 的 order by, 其 实 如 果 使 用 ranking 函 数 ,Order by 是 少 不 了 的 。 如 果 担 心 Order by 会 影 响 效 率 , 可 以 为 order by 的 字 段 建



立 聚 集 索 引 , 查 询 计 划 会 忽 略 order by 操作(因为本来就是排序的嘛)。







2、 top



可 以 动 态 传 入 参 数 , 省 却 了 动 态 SQL 的 拼 写 。

154







3、 Apply



对递归类的树遍历很有帮助。







4、 CTE



个人感觉这个真是太棒了!阅读清晰,非常有时代感。







5、 try/catch



代 替 了 原 来 VB 式 的 错 误 判 断 。 比 Oracle 高 级 不 少 。







6、 pivot/unpivot



个 人 感 觉 没 有 case 直 观 。 而 且 默 认 的 第 三 字 段 ( 还 可 能 更 多 ) 作 为 group by 字 段 很 容 易 造 成 新 手 的 错 误 。









三 、 DBA 管 理 方 面







1、 数 据 库 级 触 发 器



记 得 在 最 开 始 使 用 2k 的 时 候 就 要 用 到 这 个 功 能 , 可 惜 2k 没 有 , 现 在 有 了 作 解 决 方 案 的 朋 友 会 很 高 兴 吧 。







2、 多 加 的 系 统 视 图 和 实 时 系 统 信 息







这 些 东 西 对 DBA 挑 优 非 常 有 帮 助 , 但 是 感 觉 粒 度 还 是 不 太 细 。







3、 优 化 器 的 改 进



一 直 以 来 个 人 感 觉 SQL Server 的 优 化 器 要 比 Oracle 的 聪 明 。 SQL2005 的 更 是 比 2k 聪 明 了 不 少 。 ( 有 次 作 试 验 发 现 有 的 语 句 在 200 万 级 时 还 比 50 万 级 的 相



同 语 句 要 快 show_text 的 一 些 提 示 没 有 找 到 解 释 。 一 直 在 奇 怪 。 )



论坛例子:



http://community.csdn.net/Expert/topic/4543/4543718.xml?temp=.405987







4、 profiler 的 新 事 件 观 察



这 一 点 很 好 的 加 强 了 profiler 的 功 能 。 但 是 提 到 profiler 提 醒 大 家 注 意 一 点 。 windows2003 要 安 装 sp1 补 丁 才 能 启 动 profiler。 否 则 点 击 没 有 反 应 。







5、 sqlcmd







习 惯 敲 命 令 行 的 朋 友 可 能 会 爽 一 些 。 但 是 功 能 有 限 。 适 合 机 器 跑 不 动 SQL Server Management Studio 的 朋 友 使 用 。







四、遗憾







1、 登 陆 的 控 制



始 终 遗 憾 SQL Server 的 登 陆 无 法 分 配 CPU/内 存 占 用 等 指 标 数 。 如 果 你 的 SQL Server 给 别 人 分 配 了 一 个 只 可 以 读 几 个 表 的 权 限 , 而 这 个 家 伙 疯 狂 的 死 循 环



进 行 连 接 查 询 , 会 给 你 的 系 统 带 来 很 大 的 负 担 。 而 SQL Server 如 果 能 像 Oracle 一 样 可 以 为 登 陆 分 配 如 : 5%的 cpu, 10%的 内 存 。 就 可 以 解 决 这 个 漏 洞 。







2、 数 据 库 物 理 框 架 没 有 变 动



undo 和 redo 都 放 在 数 据 库 得 transaction 中 , 个 人 感 觉 是 个 败 笔 。 如 果 说 我 们 在 设 计 数 据 库 的 时 候 考 虑 分 多 个 数 据 库 , 可 能 能 在 一 定 程 度 上 避 免 I/O 效 率 问



题 。 但 是 同 样 会 为 索 引 视 图 等 应 用 带 来 麻 烦 。 看 看 行 级 和 事 务 级 的 快 照 数 据 放 在 tempdb 中 , 就 能 感 觉 到 目 前 架 构 的 尴 尬 。







3、 还 是 没 有 逻 辑 备 份



备份方面可能还是一个老大难的问题。不能单独备份几个表总是感觉不爽。灵活备份的问题不知 道什么时候才能解决。

155



4、 SSIS(DTS)太 复 杂 了







SQL Server 的 异 构 移 植 功 能 个 人 感 觉 最 好 了 。 ( 如 果 对 比 过 SQL Server 的 链 接 服 务 器 和 Oracle 的 透 明 网 关 的 朋 友 会 发 现 SQL Server 的



sp_addlinkedserver(openquery)异 构 数 据 库 系 列 比 Oracle 真 是 强 太 多 了 。 )



以 前 的 DTS 轻 盈 简 单 。但 是 现 在 的 SSIS 虽 然 功 能 强 大 了 很 多 ,但 是 总 是 让 人 感 觉 太 麻 烦 。看 看 论 坛 中 询 问 SSIS 的 贴 子 就 知 道 。做 的 功 能 太 强 大 了 ,往 往 会 有



很多用户不会用了。







157. 用一条 SQL 语句 查询出每门课都大于 80 分的学生姓名



name kecheng fenshu



张三 语文 81



张三 数学 75



李四 语文 76



李四 数学 90



王五 语文 81



王五 数学 100



王五 英语 90







A: select distinct name from table where name not in (select distinct name from table where fenshub.Debit101ccur







************************************************************************************









160. 精妙 SQL 语句

说 明 : 复 制 表 (只 复 制 结 构 ,源 表 名 : a 新 表 名 : b)



SQL: select * into b from a where 11

156







说 明 : 拷 贝 表 (拷 贝 数 据 ,源 表 名 : a 目 标 表 名 : b)







SQL: insert into b(a, b, c) select d,e,f from a;







说明:显示文章、提交人和最后回复时间







SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) addda te from table where table.title=a.title) b







说 明 : 外 连 接 查 询 (表 名 1: a 表 名 2: b)







SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c







说明:日程安排提前五分钟提醒







SQL: select * from 日 程 安 排 where datediff('minute',f 开 始 时 间 ,getdate())>5







说明:两张关联表,删除主表中已经在副表中没有 的信息







SQL:







delete from info where not exists ( select * from infobz where info.infid=infobz.infid )







原表:



courseid coursename score



-------------------------------------



1 java 70



2 oracle 90



3 xml 40



4 jsp 30



5 servlet 80







161.

Student(S#,Sname,Sage,Ssex) 学 生 表



Course(C#,Cname,T#) 课 程 表



SC(S#,C#,score) 成 绩 表



Teacher(T#,Tname) 教 师 表







问题:



1、 查 询 “ 001” 课 程 比 “ 002” 课 程 成 绩 高 的 所 有 学 生 的 学 号 ;



select a.S# from (select s#,score from SC where C#='001') a,(select s#,score



from SC where C#='002') b



where a.score>b.score and a.s#=b.s#;



2、 查 询 平 均 成 绩 大 于 60 分 的 同 学 的 学 号 和 平 均 成 绩 ;



select S#,avg(score)



from sc



group by S# having avg(score) >60;



3、 查 询 所 有 同 学 的 学 号 、 姓 名 、 选 课 数 、 总 成 绩 ;



select Student.S#,Student.Sname,count(SC.C#),sum(score)

157



from Student left Outer join SC on Student.S#=SC.S#



group by Student.S#,Sname



4、 查 询 姓 “ 李 ” 的 老 师 的 个 数 ;



select count(distinct(Tname))



from Teacher



where Tname like '李 %';



5、 查 询 没 学 过 “ 叶 平 ” 老 师 课 的 同 学 的 学 号 、 姓 名 ;



select Student.S#,Student.Sname



from Student



where S# not in (select distinct( SC.S#) from SC,Course,Teacher where SC.C#=Course.C# and Teacher.T#=Course.T# and Teacher.T name='叶



平 ');



6、 查 询 学 过 “ 001” 并 且 也 学 过 编 号 “ 002” 课 程 的 同 学 的 学 号 、 姓 名 ;



select Student.S#,Student.Sname from Student,SC where Student.S#=SC.S# and SC.C#='001'and exists( Select * from SC as SC_2 where SC_2.S#=SC.S#



and SC_2.C#='002');



7、 查 询 学 过 “ 叶 平 ” 老 师 所 教 的 所 有 课 的 同 学 的 学 号 、 姓 名 ;



select S#,Sname



from Student



where S# in (select S# from SC ,Course ,Teacher where SC.C#=Course.C# and Teacher.T#=Course.T# and T eacher.Tname='叶 平 ' group by S# having



count(SC.C#)=(select count(C#) from Course,Teacher where Teacher.T#=Course.T# and Tname=' 叶 平 '));



8、 查 询 课 程 编 号 “ 002” 的 成 绩 比 课 程 编 号 “ 001” 课 程 低 的 所 有 同 学 的 学 号 、 姓 名 ;



Select S#,Sname from (select Student.S#,Student.Sname,score ,(select score f rom SC SC_2 where SC_2.S#=Student.S# and SC_2.C#='002') score2



from Student,SC where Student.S#=SC.S# and C#='001') S_2 where score2 60);



10、 查 询 没 有 学 全 所 有 课 的 同 学 的 学 号 、 姓 名 ;



select Student.S#,Student.Sname



from Student,SC



where Student.S#=SC.S# group by Student.S#,Student.Sname having count(C#) =60 THEN 1 ELSE 0 END)/COUNT(*) AS 及 格 百 分 数



FROM SC T,Course



where t.C#=course.C#



GROUP BY t.C#



ORDER BY 100 * SUM(CASE WHEN isnull(score,0)>=60 THEN 1 ELSE 0 END)/COUNT(*) DESC



20、 查 询 如 下 课 程 平 均 成 绩 和 及 格 率 的 百 分 数 (用 "1 行 "显 示 ): 企 业 管 理 ( 001) , 马 克 思 ( 002) , OO&UML ( 003) , 数 据 库 ( 004)



SELECT SUM(CASE WHEN C# ='001' THEN score ELSE 0 END)/SUM(CASE C# WHEN '001' THEN 1 ELSE 0 END) AS 企 业 管 理 平 均 分



,100 * SUM(CASE WHEN C# = '001' AND score >= 60 THEN 1 ELSE 0 END)/SUM(CASE WHEN C# = '001' THEN 1 ELSE 0 END) AS 企 业 管 理 及 格 百



分数



,SUM(CASE WHEN C# = '002' THEN score ELSE 0 END)/SUM(CASE C # WHEN '002' THEN 1 ELSE 0 END) AS 马 克 思 平 均 分



,100 * SUM(CASE WHEN C# = '002' AND score >= 60 THEN 1 ELSE 0 END)/SUM(CASE WHEN C# = '002' THEN 1 ELSE 0 END) AS 马 克 思 及 格 百 分







,SUM(CASE WHEN C# = '003' THEN score ELSE 0 END)/SUM(CASE C# WHEN '003' T HEN 1 ELSE 0 END) AS UML 平 均 分



,100 * SUM(CASE WHEN C# = '003' AND score >= 60 THEN 1 ELSE 0 END)/SUM(CASE WHEN C# = '003' THEN 1 ELSE 0 END) AS UML 及 格 百 分 数



,SUM(CASE WHEN C# = '004' THEN score ELSE 0 END)/SUM(CASE C# WHEN '004' THEN 1 ELSE 0 E ND) AS 数 据 库 平 均 分



,100 * SUM(CASE WHEN C# = '004' AND score >= 60 THEN 1 ELSE 0 END)/SUM(CASE WHEN C# = '004' THEN 1 ELSE 0 END) AS 数 据 库 及 格 百 分







FROM SC

159



21、 查 询 不 同 老 师 所 教 不 同 课 程 平 均 分 从 高 到 低 显 示



SELECT max(Z.T#) AS 教 师 ID,MAX(Z.Tname) AS 教 师 姓 名 ,C.C# AS 课 程 I D ,MAX(C.Cname) AS 课 程 名 称 ,AVG(Score) AS 平 均 成 绩



FROM SC AS T,Course AS C ,Teacher AS Z



where T.C#=C.C# and C.T#=Z.T#



GROUP BY C.C#



ORDER BY AVG(Score) DESC



22、 查 询 如 下 课 程 成 绩 第 3 名 到 第 6 名 的 学 生 成 绩 单 : 企 业 管 理 ( 001) , 马 克 思 ( 002) , UML ( 003) , 数 据 库 ( 004)



[学 生 ID],[学 生 姓 名 ],企 业 管 理 ,马 克 思 ,UML,数 据 库 ,平 均 成 绩



SELECT DISTINCT top 3



SC.S# As 学 生 学 号 ,



Student.Sname AS 学 生 姓 名 ,



T1.score AS 企 业 管 理 ,



T2.score AS 马 克 思 ,



T3.score AS UML,



T4.score AS 数 据 库 ,



ISNULL(T1.score,0) + ISNULL(T2.score,0) + ISNULL(T3.score,0) + ISNULL(T4.score,0) as 总 分



FROM Student,SC LEFT JOIN SC AS T1



ON SC.S# = T1.S# AND T1.C# = '001'



LEFT JOIN SC AS T2



ON SC.S# = T2.S# AND T2.C# = '002'



LEFT JOIN SC AS T3



ON SC.S# = T3.S# AND T3.C# = '003'



LEFT JOIN SC AS T4



ON SC.S# = T4.S# AND T4.C# = '004'



WHERE student.S#=SC.S# and



ISNULL(T1.score,0) + ISNULL(T2.score,0) + ISNULL(T3.score,0) + ISNULL(T4.score,0)



NOT IN



(SELECT



DISTINCT



TOP 15 WITH TIES



ISNULL(T1.score,0) + ISNULL(T2.score,0) + ISNULL(T3.score,0) + ISNULL(T4.s core,0)



FROM sc



LEFT JOIN sc AS T1



ON sc.S# = T1.S# AND T1.C# = 'k1'



LEFT JOIN sc AS T2



ON sc.S# = T2.S# AND T2.C# = 'k2'



LEFT JOIN sc AS T3



ON sc.S# = T3.S# AND T3.C# = 'k3'



LEFT JOIN sc AS T4



ON sc.S# = T4.S# AND T4.C# = 'k4'



ORDER BY ISNULL(T1.score,0) + ISNULL(T2.score,0) + ISNULL(T3.score,0) + ISNULL(T4.score,0) DESC);







23、 统 计 列 印 各 科 成 绩 ,各 分 数 段 人 数 :课 程 ID,课 程 名 称 ,[100-85],[85-70],[70-60],[ T2.平 均 成 绩 ) as 名 次 ,



S# as 学 生 学 号 ,平 均 成 绩



FROM (SELECT S#,AVG(score) 平 均 成 绩



FROM SC



GROUP BY S#



) AS T2



ORDER BY 平 均 成 绩 desc;







25、 查 询 各 科 成 绩 前 三 名 的 记 录 :(不 考 虑 成 绩 并 列 情 况 )



SELECT t1.S# as 学 生 ID,t1.C# as 课 程 ID,Score as 分 数



FROM SC t1



WHERE score IN (SELECT TOP 3 sc ore



FROM SC



WHERE t1.C#= C#



ORDER BY score DESC



)



ORDER BY t1.C#;



26、 查 询 每 门 课 程 被 选 修 的 学 生 数



select c#,count(S#) from sc group by C#;



27、 查 询 出 只 选 修 了 一 门 课 程 的 全 部 学 生 的 学 号 和 姓 名



select SC.S#,Student.Sname,count(C#) AS 选 课 数



from SC ,Student



where SC.S#=Student.S# group by SC.S# ,Student.Sname having count(C#)=1;



28、 查 询 男 生 、 女 生 人 数



Select count(Ssex) as 男 生 人 数 from Student group by Ssex having Ssex=' 男 ';



Select count(Ssex) as 女 生 人 数 from Student group by Ssex having Ssex=' 女 ';



29、 查 询 姓 “ 张 ” 的 学 生 名 单



SELECT Sname FROM Student WHERE Sname like ' 张 %';



30、 查 询 同 名 同 性 学 生 名 单 , 并 统 计 同 名 人 数



select Sname,count(*) from Student group by Sname having count(*)>1;;



31、 1981 年 出 生 的 学 生 名 单 (注 : Student 表 中 Sage 列 的 类 型 是 datetime)



select Sname, CONVERT(char (11),DATEPART(year,Sage)) as age



from student



where CONVERT(char(11),DATEPART(year,Sage))='1981';



32、 查 询 每 门 课 程 的 平 均 成 绩 , 结 果 按 平 均 成 绩 升 序 排 列 , 平 均 成 绩 相 同 时 , 按 课 程 号 降 序 排 列



Select C#,Avg(score) from SC group by C# order by Avg(score),C# DESC ;



33、 查 询 平 均 成 绩 大 于 85 的 所 有 学 生 的 学 号 、 姓 名 和 平 均 成 绩



select Sname,SC.S# ,avg(score)



from Student,SC



where Student.S#=SC.S# group by SC.S#,Sname having avg(score)>85;

161



34、 查 询 课 程 名 称 为 “ 数 据 库 ” , 且 分 数 低 于 60 的 学 生 姓 名 和 分 数



Select Sname,isnull(score,0)



from Student,SC,Course



where SC.S#=Student.S# and SC.C#=Course.C# and Course.Cname=' 数 据 库 'and score =70 AND SC.S#=student.S#;



37、 查 询 不 及 格 的 课 程 , 并 按 课 程 号 从 大 到 小 排 列



select c# from sc where scor e 80 and C#='003';



39、 求 选 了 课 程 的 学 生 人 数



select count(*) from sc;



40、 查 询 选 修 “ 叶 平 ” 老 师 所 授 课 程 的 学 生 中 , 成 绩 最 高 的 学 生 姓 名 及 其 成 绩



select Student.Sname,score



from Student,SC,Course C,Teacher



where Student.S#=SC.S# and SC.C#=C.C# and C.T#=Teacher.T# and Teacher.Tname=' 叶 平 ' and SC.score=(select max(score)from SC where C#=C.C# );



41、 查 询 各 个 课 程 及 相 应 的 选 修 人 数



select count(*) from sc group by C#;



42、 查 询 不 同 课 程 成 绩 相 同 的 学 生 的 学 号 、 课 程 号 、 学 生 成 绩



select distinct A.S#,B.score from SC A ,SC B where A.Score=B.Score and A.C# B.C# ;



43、 查 询 每 门 功 成 绩 最 好 的 前 两 名



SELECT t1.S# as 学 生 ID,t1.C# as 课 程 ID,Score as 分 数



FROM SC t1



WHERE score IN (SELECT TOP 2 score



FROM SC



WHERE t1.C#= C#



ORDER BY score DESC



)



ORDER BY t1.C#;



44、 统 计 每 门 课 程 的 学 生 选 修 人 数 ( 超 过 10 人 的 课 程 才 统 计 ) 。 要 求 输 出 课 程 号 和 选 修 人 数 , 查 询 结 果 按 人 数 降 序 排 列 , 查 询 结 果 按 人 数 降 序 排 列 , 若 人 数



相同,按课程号升序排列



select C# as 课 程 号 ,count(*) as 人 数



from sc



group by C#



order by count(*) desc,c#



45、 检 索 至 少 选 修 两 门 课 程 的 学 生 学 号



select S#



from sc



group by s#



having count(*) > = 2



46、 查 询 全 部 学 生 都 选 修 的 课 程 的 课 程 号 和 课 程 名



select C#,Cname



from Course

162



where C# in (select c# from sc group by c#)



47、 查 询 没 学 过 “ 叶 平 ” 老 师 讲 授 的 任 一 门 课 程 的 学 生 姓 名



select Sname from Student where S# not in (select S# from Course,Teacher,SC where Course.T#=Teacher.T# and SC.C#=course.C# an d Tname='



叶 平 ');



48、 查 询 两 门 以 上 不 及 格 课 程 的 同 学 的 学 号 及 其 平 均 成 绩



select S#,avg(isnull(score,0)) from SC where S# in (select S# from SC where score 2)group by S#;



49、 检 索 “ 004” 课 程 分 数 小 于 60, 按 分 数 降 序 排 列 的 同 学 学 号



select S# from SC where C#='004'and score 5







3. 查 询 借 阅 了 "水 浒 "一 书 的 读 者 , 输 出 姓 名 及 班 级



--实 现 代 码 :



SELECT * FROM CARD c



WHERE EXISTS(



SELECT * FROM BORROW a,BOOKS b



WHERE a.BNO=b.BNO



AND b.BNAME=N'水 浒 '



AND a.CNO=c.CNO)







4. 查 询 过 期 未 还 图 书 , 输 出 借 阅 者 ( 卡 号 ) 、 书 号 及 还 书 日 期



--实 现 代 码 :



SELECT * FROM BORROW



WHERE RDATE0



INSERT BORROW_SAVE SELECT i.*



FROM INSERTED i,BOOKS b



WHERE i.BNO=b.BNO



AND b.BNAME=N'数 据 库 技 术 及 应 用 '







12. 建 立 一 个 视 图 , 显 示 "力 01"班 学 生 的 借 书 信 息 ( 只 要 求 显 示 姓 名 和 书 名 )



--实 现 代 码 :



CREATE VIEW V_VIEW



AS



SELECT a.NAME,b.BNAME



FROM BORROW ab,CARD a,BOOKS b



WHERE ab.CNO=a.CNO



AND ab.BNO=b.BNO



AND a.CLASS=N'力 01'







13. 查 询 当 前 同 时 借 有 "计 算 方 法 "和 "组 合 数 学 "两 本 书 的 读 者 , 输 出 其 借 书 卡 号 , 并 按 卡 号 升 序 排 序 输 出



--实 现 代 码 :



SELECT a.CNO



FROM BORROW a,BOOKS b



WHERE a.BNO=b.BNO



AND b.BNAME IN(N'计 算 方 法 ',N'组 合 数 学 ')



GROUP BY a.CNO



HAVING COUNT(*)=2



ORDER BY a.CNO DESC







14. 假 定 在 建 BOOKS 表 时 没 有 定 义 主 码 , 写 出 为 BOOKS 表 追 加 定 义 主 码 的 语 句



--实 现 代 码 :



ALTER TABLE BOOKS ADD PRIMARY KEY(BNO)







15.1 将 NAME 最 大 列 宽 增 加 到 10 个 字 符 ( 假 定 原 为 6 个 字 符 )

165



--实 现 代 码 :



ALTER TABLE CARD ALTER COLUMN NAME varchar(10)







15.2 为 该 表 增 加 1 列 NAME( 系 名 ) , 可 变 长 , 最 大 20 个 字 符



--实 现 代 码 :



ALTER TABLE CARD ADD 系 名 varchar(20)









问题描述:



为管理岗位业务培训信息,建立 3 个表:



S (S#,SN,SD,SA) S#,SN,SD,SA 分 别 代 表 学 号 、 学 员 姓 名 、 所 属 单 位 、 学 员 年 龄



C (C#,CN ) C#,CN 分别代表课程编号、课程名称



SC ( S#,C#,G ) S#,C#,G 分别代表学号、所选修的课程编号、学习成绩







要求实现如下 5 个处理:



1. 使 用 标 准 SQL 嵌 套 语 句 查 询 选 修 课 程 名 称 为 ’ 税 收 基 础 ’ 的 学 员 学 号 和 姓 名



2. 使 用 标 准 SQL 嵌 套 语 句 查 询 选 修 课 程 编 号 为 ’ C2’ 的 学 员 姓 名 和 所 属 单 位



3. 使 用 标 准 SQL 嵌 套 语 句 查 询 不 选 修 课 程 编 号 为 ’ C5’ 的 学 员 姓 名 和 所 属 单 位



4. 使 用 标 准 SQL 嵌 套 语 句 查 询 选 修 全 部 课 程 的 学 员 姓 名 和 所 属 单 位



5. 查 询 选 修 了 课 程 的 学 员 人 数



6. 查 询 选 修 课 程 超 过 5 门 的 学 员 学 号 和 所 属 单 位







1. 使 用 标 准 SQL 嵌 套 语 句 查 询 选 修 课 程 名 称 为 ’ 税 收 基 础 ’ 的 学 员 学 号 和 姓 名



--实 现 代 码 :



SELECT SN,SD FROM S



WHERE [S#] IN(



SELECT [S#] FROM C,SC



WHERE C.[C#]=SC.[C#]



AND CN=N'税 收 基 础 ')









2. 使 用 标 准 SQL 嵌 套 语 句 查 询 选 修 课 程 编 号 为 ’ C2’ 的 学 员 姓 名 和 所 属 单 位



--实 现 代 码 :



SELECT S.SN,S.SD FROM S,SC



WHERE S.[S#]=SC.[S#]



AND SC.[C#]='C2'







3. 使 用 标 准 SQL 嵌 套 语 句 查 询 不 选 修 课 程 编 号 为 ’ C5’ 的 学 员 姓 名 和 所 属 单 位



--实 现 代 码 :



SELECT SN,SD FROM S



WHERE [S#] NOT IN(



SELECT [S#] FROM SC



WHERE [C#]='C5')







4. 使 用 标 准 SQL 嵌 套 语 句 查 询 选 修 全 部 课 程 的 学 员 姓 名 和 所 属 单 位



--实 现 代 码 :



SELECT SN,SD FROM S



WHERE [S#] IN(

166



SELECT [S#] FROM SC



RIGHT JOIN C ON SC.[C#]=C.[C#]



GROUP BY [S#]



HAVING COUNT(*)=COUNT(DISTINCT [S#]))







5. 查 询 选 修 了 课 程 的 学 员 人 数



--实 现 代 码 :



SELECT 学 员 人 数 =COUNT(DISTINCT [S#]) FROM SC







6. 查 询 选 修 课 程 超 过 5 门 的 学 员 学 号 和 所 属 单 位



--实 现 代 码 :



SELECT SN,SD FROM S



WHERE [S#] IN(



SELECT [S#] FROM SC



GROUP BY [S#]



HAVING COUNT(DISTINCT [C#])>5)









162. Covariance and Contravariance 委托中的协变和逆变

将方法签名与委托类型匹配时,协变和逆变可以提供一定程度的灵活性。协变允许方法具有的派生返回类型比委托中定义的更多。逆变允许方法具有的派生



参数类型比委托类型中的更少。



示 例 1( 协 变 )



说明



本示例演示如何将委托与具有返回类型的方法一起使用,这些返回类型派生自委托签名中的返回类型。由 SecondHandler 返回的数据类型是 Dogs

类型,它是由委托中定义的 Mammals 类型派生的。







class Mammals



{



}







class Dogs : Mammals



{



}







class Program



{



// Define the delegate.



public delegate Mammals HandlerMethod();







public static Mammals FirstHandler()



{



return null;



}







public static Dogs SecondHandler()



{



return null;



}

167







static void Main()



{



HandlerMethod handler1 = FirstHandler;







// Covariance allows this delegate.



HandlerMethod handler2 = SecondHandler;



}



}



示 例 2( 逆 变 )



说明



本 示 例 演 示 如 何 将 委 托 与 具 有 某 个 类 型 的 参 数 的 方 法 一 起 使 用 ,这 些 参 数 是 委 托 签 名 参 数 类 型 的 基 类 型 。通 过 逆 变 ,以 前 必 须 使 用 若 干 个 不 同 处 理 程 序 的 地 方



现 在 只 要 使 用 一 个 事 件 处 理 程 序 即 可 。如 ,现 在 可 以 创 建 一 个 接 收 EventArgs 输 入 参 数 的 事 件 处 理 程 序 ,然 后 ,可 以 将 该 处 理 程 序 与 发 送 MouseEventArgs 类



型 ( 作 为 参 数 ) 的 Button.MouseClick 事 件 一 起 使 用 , 也 可 以 将 该 处 理 程 序 与 发 送 KeyEventArgs 参 数 的 TextBox.KeyDown 事 件 一 起 使 用 。



代码



C#







复制代码

System.DateTime lastActivity;



public Form1()



{



InitializeComponent();







lastActivity = new System.DateTime();



this.textBox1.KeyDown += this.MultiHandler; //works with KeyEventArgs



this.button1.MouseClick += this.MultiHandler; //works with MouseEventArgs







}







// Event hander for any event with a n EventArgs or



// derived class in the second parameter



private void MultiHandler(object sender, System.EventArgs e)



{



lastActivity = System.DateTime.Now;



}



Related docs
Other docs by huanghengdong
EOL Share the Care PowerPoint
Views: 1  |  Downloads: 0
Quotes
Views: 0  |  Downloads: 0
IntentionsDatabase-Live
Views: 0  |  Downloads: 0
Team CYC
Views: 5  |  Downloads: 0
CommitteeCharter_1_
Views: 2  |  Downloads: 0
Mak ke a one o off donat tion
Views: 0  |  Downloads: 0
GBCMA_AR_2007_Page36_RCT_Increase_cover
Views: 0  |  Downloads: 0
By registering with docstoc.com you agree to our
privacy policy

You are almost ready to download!

You are almost ready to download!