新西兰签证网站开发,开发一个电商平台app要多少钱,百度收录最快网站,秦皇岛网站制作费用《修炼之道:.NET 开发要点精讲》目录《修炼之道:.NET 开发要点精讲》第 1 章 另辟蹊径#xff1a;解读.NET1.7 本章思考 位置 465第 2 章 高屋建瓴#xff1a;梳理编程约定2.2 方法与线程的关系 位置 5192.7 线程安全 位置 5952.8 调用与回调 位置 6612.… 《修炼之道:.NET 开发要点精讲》目录《修炼之道:.NET 开发要点精讲》第 1 章 另辟蹊径解读.NET1.7 本章思考 位置 465第 2 章 高屋建瓴梳理编程约定2.2 方法与线程的关系 位置 5192.7 线程安全 位置 5952.8 调用与回调 位置 6612.9 托管资源与非托管资源 位置 6662.13 协议 位置 751第 3 章 编程之基础数据类型3.1 引用类型与值类型 位置 8443.3 赋值与复制 位置 1118第 4 章 物以类聚对象也有生命4.1 堆和栈 位置 12354.2 堆中对象的出生与死亡 位置 12724.3 管理非托管资源 位置 13814.4 正确使用 IDisposable 接口 位置 15474.6 本章思考 位置 1587第 5 章 重中之重委托与事件5.1 什么是.NET 中的委托 位置 16255.2 事件与委托的关系 位置 18605.3 使用事件编程 位置 19405.4 弱委托 位置 20775.5 本章回顾 位置 2151第 6 章 线程的升级异步编程模型6.1 异步编程的必要性 位置 21716.2 委托的异步调用 位置 22026.3 非委托的异步调用 位置 23586.6 本章思考 位置 2446第 7 章 可复用代码组件的来龙去脉7.1 .NET 中的组件 位置 24577.2 容器 – 组件 – 服务模型 位置 24997.3 设计时与运行时 位置 26727.4 控件 位置 2776第 8 章 经典重视桌面 GUI 框架揭秘8.2 Win32 应用程序的结构 位置 28418.4 Windows Forms 框架 位置 31488.5 Winform 程序的结构 位置 3180第 9 章 沟通无碍网络编程9.1 两种 Socket 通信方式 位置 35189.3 UDP 通信的实现 位置 38409.4 异步编程在网络编程中的应用 位置 39919.6 本章思考 位置 4062第 10 章 动力之源代码中的 “泵”10.2 常见的 “泵” 结构 位置 4150第 11 章 规绳矩墨模式与原则11.1 软件的设计模式 位置 430811.5 本章思考 位置 4605第 12 章 难免的尴尬代码依赖12.1 从面向对象开始 位置 473812.2 不可避免的代码依赖 位置 481512.3 降低代码依赖 位置 493612.4 框架的 “代码依赖” 位置 499812.6 本章思考 位置 5022作者周见智博图轩第 1 章 另辟蹊径解读.NET1.7 本章思考 位置 4651. 简述. NET 平台 中 CTS、 CLS 以及 CLR 的 含义 与 作用。A CTS 指 公共 类型 系统 是. NET 平台 中 各种 语言 必须 遵守 的 类型 规范 CLS 指 公共 语言 规范 是. NET 平台 中 各种 语言 必须 遵守 的 语言 规范 CLR 指 公共 语言 运行时 它是 一个 虚拟 机. NET 平台 中 所有 的 托管 代码 均需 要在 CLR 中 运行 可将 其 视为 另外 一个 操作系统。 第 2 章 高屋建瓴梳理编程约定2.2 方法与线程的关系 位置 519只要 我们 确定 了 两个 方法 只会 运行 在 同一个 线程 中 那么 这 两个 方法 就不 可能 同时 执行 跟 方法 所处 的 位置 无关。只可 能 一前一后 执行 此时 我们 不需要 考虑 方法 中 访问 的 公共 资源 的 线程 是否 安全。 2.7 线程安全 位置 595如果 操作 一个 对象 比如 调用 它的 方法 或者 给 属性 赋值 为 非 原子 操作 即可 能 操作 还没 完成 就 暂停 了 这个时候 如果 有 另外 一个 线程 开始 运行 同时 也 操作 这个 对象 访问 了 同样 的 方法 或 属性 那么 这时 就可能 会 出现 一个 问题前一 个 操作 还未 结束 后 一个 操作 就 开始 了 前后 两个 操作 一起 出现 混乱。当 多个 线程 同时 访问 一个 对象 时 如果 每次 执行 都得 到 不一样 的 结果 甚至 出现 异常 那么 这个 对象 便是 “非 线程 安全”。造成 一个 对象 非 线程 安全 的 因素 有 很多 除了 前面 提到 的 由于 非 原子 操作 执行 到 一半 就 中断 以外 还有 一种 情况 是由 多个 CPU 造成 的 即 就算 操作 没有 中断 由于 多个 CPU 可以 真正 实现 多 线程 同时 运行 所以 就有 可能 出现 “ 对 同一 对象 同时 操作 出现 混乱” 的 情况如图 2- 4 所示。 2.7 线程安全 位置 627在 Winform 编程 中 我们 之所以 经常 会 遇见 “不在 创建 控 件 的 线程 中 访问 该 控 件” 的 异常 原因 就 是对 UI 控 件 的 操作 几乎 都不 是 线程 安全 的 部分 是。一般 UI 控 件 只能 由 UI 线程 操作 其余 的 所有 操作 均需 要 投递 到 UI 线程 之中 执行 否则 就会 像 前面 讲过 的 那样 程序 出现 异常 或不 稳定。可以 使用 Control. InvokeRequired 属性 去 判断 当前 线程 是否 是 创建 控 件 的 线程 UI 线程 如果 是 则 该 属性 返回 false 可以 直接 操作 UI 控 件 否则 返回 true 不能 直接 操作 UI 控 件。注Control 含有 若干个 线程 安全 的 方法 和 属性 常见 的 主要 有 Control. InvokeRequired 属性、 Control. Invoke 方法、 Control. BeginInvoke 方法 Control. Invoke 的 异步 版本、 Control. EndInvoke 方法 以及 Control. CreateGraphics 方法。这些 属性 和 方法 都可以 在 非 UI 线程 中 使用 并且 跨线 程 访问 这些 方法 和 属性 时不 会 引起 程序 异常。 2.8 调用与回调 位置 661.NET 平台 开发 中的 回 调 主要 是 通过 委托 来 实现 的。委托 是一 种 代理 专门 负责 调用 方法。 2.9 托管资源与非托管资源 位置 666.NET 中 对象 使 用到 的 非 托管 资源 主要 有 I/ O 流、 数据库 连接、 Socket 连接、 窗口 句柄 等 各种 直接 与 操作系统 相关 的 资源。 2.13 协议 位置 751图 2- 11 网络 七 层 协议 第 3 章 编程之基础数据类型3.1 引用类型与值类型 位置 844通常 值 类型 又 分为 以下 两个 部分。1 简单 值 类型包括 类似 int、 bool、 long 等. NET 内置 类型。它们 本质上 也是 一种 结构 体。2 复合 值 类型使用 Struct 关键字 定义 的 结构 体 比如 System. Drawing. Point 等。复合 值 类型 可以 由 简单 值 类型 和 引用 类型 组成。 3.3 赋值与复制 位置 1118引用 类型 的 赋值、 浅 复制、 深 复制 的 区别 如图 3- 15 所示。 值 类型 的 赋值、 浅 复制、 深 复制 的 区别 如图 3- 16 所示。 对象 深 复制 的 过程 如图 3- 17 所示。 3.3 赋值与复制 位置 1155NET 中 可以 使用 “序列 化 和 反 序列 化” 的 技术 实现 对象 的 深 复制 只要 一个 类型 及其 所有 成员 的 类型 都 标示 为 “ 可 序列 化” 那么 就可以 先 序列 化 该类 型 对象 到 字 节流 然后 再将 字节 流 反序 列 化成 源 对象 的 副本。这样一来 源 对象 与 副本 之间 没有 任何 关联 从而 达到 深 复制 的 效果。 第 4 章 物以类聚对象也有生命4.1 堆和栈 位置 1235栈 主要 用来 记录 程序 的 运行 过程 它有 严格 的 存储 和 访问 顺序而 堆 主要 存储 程序 运行 期间 产生 的 一些 数据 几乎没有 顺序 的 概念。 4.1 堆和栈 位置 1269堆 跟 栈 的 本质 都是 一段 内存块。 4.2 堆中对象的出生与死亡 位置 1272栈 中的 对象 由 系统 负责 自动 存入 和 移 除 正常 情况下 跟我 们 程序 开发 的 关联 并不 大。 4.2 堆中对象的出生与死亡 位置 1354谨慎 使用 对象 的 析构方法。析 构 方法 由 CLR 调用 不受 程序控制 而且 容易 造成 对象 重生。析 构 方法 除了 用作 管理 非 托管 资源 外 几乎 不能 用作 其他 用途。 4.3 管理非托管资源 位置 1381GC. SuppressFinalize 方法 请求 CLR 不要 再 调用 本 对象 的 析 构 方法 原因 很 简单 既然 非 托管 资源 已经 释放 完成 那么 CLR 就 没 必要 再继续 调用 析 构 方法。 注CLR 调用 对象 的 析 构 方法 是一 个 复杂 的 过程 需要 消耗 非常 大的 性能 这也 是 尽量 避免 在 析 构 方法 中 释放 非 托管 资源 的 一个 重要 原因 最好 是 彻底 地 不 调用 析 构 方法。 4.4 正确使用 IDisposable 接口 位置 1547如果 一个 类型 使 用了 非 托管 资源 或者 它 包含 使 用了 非 托管 资源 的 成员 那么 开发者 就应 该 应用 “ Dispose 模式”正确地 实现 间接 或 直接 IDisposable 接口 正确地 重写 Dispose bool disposing 虚 方法。 4.6 本章思考 位置 1587调用 一个 对象 的 Dispose 方法 后 并不 意味着 该 对象 已经 死亡 只有 GC 将对 象 实例 占用 的 内存 回收 后 才 可以说 对象 已死。但是 通常 情况下 在调 用 对象 的 Dispose 方法 后 由于 释 放了 该 对象 的 非 托管 资源 因此 该 对象 几乎 就 处于 “无用” 状态“ 等待 死亡” 是它 正确 的 选择。 第 5 章 重中之重委托与事件5.1 什么是.NET 中的委托 位置 1625像 声明 一个 普通 方法 一样 提供 方法 名称、 参数、 访问 修饰 符 以及 返回 值 然后 在前面 加上 Delegate 关键字 这样 就 定义 了 一个 委托 类型。委托 类型 定义 完成 后 怎样 去 实例 化 一个 委托 对象 呢其实 很 简单 跟 实例 化 其他 类型 对象 一样 我们 可以 通过 new 关键字 创建 委托 对象。 5.1 什么是.NET 中的委托 位置 1655使用 委托 调用 方法 时 我们 可以 直接 使用 “委托对象 参数 列表” 这样 的 格式 它 等效 于 “ 委托对象.Invoke 参数 列表”。给 委托 赋值 的 另外 一种 方式 是委托对象 方法。 怎样 让 一个 委托 同时 调用 两个 或者 两个 以上 的 方法 呢答案 是 直接 使用 加法 赋值 运算符 将 多个 方法 附加 到 委托 对象 上。 5.1 什么是.NET 中的委托 位置 1744委托 内部 的 “链 表” 结构 跟 单向 链 表 的 实现 原理 却不 相同。它 并不是 通过 Next 引用 与 后续 委托 建立 关联 而是 将 所有 委托 存放 在 一个 数组中 如图 5- 6 所示。每一个 委托 类型 都有 一个 公开 的 GetInvocationList 的 方法 可以 返回 已 附加 到 委托 对象 上 的 所有 委托 即 图 5- 6 中 数组 列表 部分。另外 我们 平时 不 区分 委托 对象 和 委托 链 表 提到 委托 对象 它 很有可能 就 表示 一个 委托 链 表 这 跟 单向 链 表 只 包含 一个 节点 时 道理 类似。 5.1 什么是.NET 中的委托 位置 1781委托 跟 String 类型 一样 也是 不可改变 的。换句话说 一旦 委托 对象 创建 完成 后 这个 对象 就不能 再被 更改 那么 我们 前面 讲到 的 将 一个 委托 附加 到 另外 一个 委托 对象 上 形成 一个 委托 链 表 又 该 如何 解释 呢其实 这个 跟 String. ToUpper 过程 类似 我们 对 委托 进行 附加、 移 除 等 操作 都会 产生 一个 全新 的 委托 这些 操作 并不 会 改变 原有 委托 对象。 5.1 什么是.NET 中的委托 位置 1821框架 有两 种 调用 框架 使用者 编写 的 代码 的 方式 一种 便是 面向 抽象 编程 即 框架 中 尽量 不出 现 某个 具体 类型 的 引用 而是 使用 抽象化 的 基 类 引用 或者 接口 引用 代替。只要 框架 使用者 编写 的 类型 派生 自 抽象化 的 基 类 或 实现 了 接口 框架 均可 以 正确地 调用 它们。框架 调用 框架 使用者 代码 的 另外 一种 方式 就是 使用 委托 将 委托 作为 参数 变量 传递 给 框架 框架 通过 委托 调用 方法。 5.2 事件与委托的关系 位置 1860问题. NET 中 提出 了 一种 介于 public 和 private 之间 的 另外 一种 访问 级别在 定义 委托 成员 的 时候 给出 event 关键字 进行 修饰 前面 加了 event 关键字 修饰 的 public 委托 成员 只 能在 类 外部 进行 附加 和 移 除 操作 而 调用 操作 只能 发生 在 类型 内部。 我们 把 类 中 设置 了 event 关键字 的 委托 叫作 “事件”。“ 事件” 本质上 就是 委托 对象。事件 的 出现 限制 了 委托 调用 只能 发生 在 一个 类型 的 内部 如图 5- 12 所示。在 图 5- 12 中 由于 server 中的 委托 使用 了 event 关键字 修饰 因此 委托 只能 在 server 内部 调用 对 外部 也 只能 进行 附加 和 移 除 方法 操作。当 符合 某一 条件 时 server 内部 会 调用 委托 这个 时间 不由 我们 client 控制 而是 由 系统 server 决定。因此 大部分 时候 事件 在 程序 中 起 到了 回 调 作用。 调用 加了 event 关键字 修饰 的 委托 也称 为 “ 激发事件”。其中 调用 方 图 5- 12 中的 server 被称为 “ 事件发布者”被 调用 方 图 5- 12 中的 client 被称为 “ 事件注册者” 或 “ 事件观察者”、“ 事件订阅者” 等 本书 中统 一 称之为 “事件 注册 者”附加 委托 的 过程 被 称之为 “ 注册事件” 或 “ 绑定事件”、“ 监听事件”、“ 订阅事件” 等 本书 中统 一 称之为 “注册 事件”移 除 委托 的 过程 被 称之为 “ 注销事件”。通过 委托 调用 的 方法 被称为 “ 事件处理程序”。 5.3 使用事件编程 位置 1940在调 用 委托 链 时 如果 某一个 委托 对应 的 方法 抛出 了 异常 那么 剩下 的 其他 委托 将 不会 再 调用。这个 很容易 理解 本来 是按 先后 顺序 依次 调用 方法 如果 其中 某一个 抛出 异常 剩下 的 肯定 会被 跳过。为了 解决 这个 问题 单单 是将 激发 事件 的 代码 放在 try/catch 块 中 是 不够 的 我们 还 需要 分 步调 用 每个 委托 将 每 一步 的 调用 代码 均 放在 try/catch 块 中。 5.3 使用事件编程 位置 1967除了 事件 本身 的 命名 事件 所属 委托 类型 的 命名 也 同样 有 标准 格式 一般以 “ 事件 名 EventHandler” 这种 格式 来给 委托 命名 因此 前面 提到 的 NewEmailReceived 事件 对应 的 委托 类型 名称 应该 是 “NewEmailReceivedEventHandler”。激发 事件 时会 传递 一些 参数 这些 参数 一般 继承 自 EventArgs 类型 后者 为. NET 框架 预定 义 类型 以 “ 事件 名 EventArgs” 来 命名 比如 前面 提到 的 NewEmailReceived 事件 在 激发 时 传递 的 参数 类型 名称 应该 是 “NewEmailReceivedEventArgs”。 5.3 使用事件编程 位置 2004之所以 要把 激发 事件 的 代码 放在 一个 单独 的 虚 方法 中 是 为了 让 从 该 类型 EmailManager 派生 出来 的 子类 能够 重写 虚 方法 从而 改变 激发 事件 的 逻辑。 虚 方法 的 命名 方式 一般 为 “ On 事件 名”。另外 该 代码 中的 虚 方法 必须 定义 为 “protected” 因为 派生 类 中 很可能 要 调用 基 类 的 虚 方法。 5.3 使用事件编程 位置 2039图 5- 13 属性 和 事件 的 作用 5.4 弱委托 位置 2077在 事件 编程 中 委托 的 Target 成员 就是 对 事件 注册 者 的 强 引用 如果 事件 注册 者 没有 注销 事件 强 引用 Target 便会 一直 存在 堆 中的 事件 注册 者 内存 就 一直 不 会被 CLR 回收 这对 开发 人员 来讲 几乎 是 很难 发觉 的。 像 “A a new A” 中的 a 被称为 “ 显式强引用 Explicit Strong Reference” 类似 委托 中 包含 的 不明显 的 强 引用 我们 称之为 “ 隐式强引用 Implicit Strong Reference”。弱引用 与 对象 实例 之间 属于 一种 “弱 关联” 关系 跟 强 引用 与 对象 实例 的 关系 不一样 就算 程序 中有 弱 引用 指向 堆 中 对象 实例 CLR 还是 会把 该 对象 实例 当作 回收 目标。程序 中 使用 弱 引用 访问 对象 实例 之前 必须 先 检查 CLR 有没有 回收 该 对象 内存。换句话说 当 堆 中 一个 对象 实例 只有 弱 引用 指向 它 时 CLR 可以 回收 它的 内存。使用 弱 引用 堆 中 对象 能否 被 访问 同时 掌握 在 程序 和 CLR 手中。创建 一个 弱 引用 很 简单 使用 WeakReference 类型 给 它的 构造 方法 传递 一个 强 引用 作为 参数 即可。 5.4 弱委托 位置 2105在 编程 过程中 由于 很难 管理 好强 引用 从而 造成 不必 要的 内存 开销。尤其 前面 讲到 的 “隐式 强 引用” 在 使用 过程中 不易 发觉 它们 的 存在。弱 引用 特别 适合 用于 那些 对 程序 依赖 程度 不高 的 对象 即那 些 对象 生命 期 主要 不是 由 程序控制 的 对象。比如 事件 编程 中 事件 发布者 对 事件 注册 者 的 存在 与否 不是 很 关心 如果 注册 者 在 那就 激发 事件 并 通知 注册 者如果 注册 者 已经 被 CLR 回收 内存 那么 就不 通知 它 这 完全 不会 影响 程序 的 运行。 5.4 弱委托 位置 2109前面 讲到 过 委托 包含 两个 部分一个 Object 类型 的 Target 成员 代表 被 调用 方法 的 所有者 如果 方法 为 静态 方法 则 Target 为 null另一个 是 MethodInfo 类型 的 Method 成员 代表 被 调用 方法。由于 Target 成员 是 一个 强 引用 所以 只要 委托 存在 那么 方法 的 所有者 就会 一直 在 堆 中 存在 而 不 能被 CLR 回收。如果 我们将 委托 中的 Target 强 引用 换成 弱 引 用的 话 那么 不管 委托 存在 与否 都不 会 影响 方法 的 所有者 在 堆 中 内存 的 回收。这样一来 我们 在使 用 委托 调用 方法 之前 需要 先 判断 方法 的 所有者 是否 已经 被 CLR 回收。我们 称 将 Target 成员 换成 弱 引用 之后 的 委托 为 “ 弱委托” 弱 委托 定义 代码 如下//Code 5- 26
class WeakDelegate
{ WeakReference _weakRef; //NO. 1MethodInfo _method; //NO. 2public WeakDelegate(Delegate d) { _weakRef new WeakReference(d.Target);_methodInfo d.Method;} public object Invoke( param object[] args) {object obj _weakRef.Target;if(_weakRef.IsAlive) //NO. 3{ return _method.Invoke(obj, args); //NO. 4} else { return null;} }
}弱 委托 将 委托 与 被 调用 方法 的 所有者 之间 的 关 系由 “强 关联” 转换 成了 “ 弱 关联” 方法 的 所有者 在 堆 中的 生命 期 不再 受 委托 的 控制 如图 5- 16 所示 为 弱 委托 的 结构。本 小节 示例 代码 中的 WeakDelegate 类型 并没有 提供 类似 Delegate. Combine 以及 Delegate. Remove 这样 操作 委托 链 表 的 方法 当然 也 没有 弱 委托 链 表 的 功能。这些 功能 可以 仿照 单向 链 表 的 结构 去 实现 把 每个 弱 委托 都 当作 链 表中 的 一个 节点。其 方法 可 参照 5. 1. 2 小节 中 讲到 的 单向 链 表。 5.5 本章回顾 位置 2151委托 的 3 个 作用第一 它 允许 把 方法 作为 参数 传递 给 其他 的 模块第二 它 允许 我们 同时 调用 多个 具有 相同 签名 的 方法第三 它 允许 我们 异步 调用 任何 方法。这 3 个 作用 奠定 了 委托 在. NET 编程 中的 绝对 重要 地位。 第 6 章 线程的升级异步编程模型6.1 异步编程的必要性 位置 2171通常 情况下 调用 一个 方法 都 符合 这样 一个 规律调用 线程 开始 调用 方法 A 后 在 A 返回 之前 调用 线程 得不到 程序 执行 的 控制 权。也就是说 方法 A 后面 的 代码 是 不可能 执行 的 直到 A 返回 为止 这种 调用 方式 被 称之为 “ 同步调用”相反 如果 调用 在 返回 之前 调用 线程 依旧 保留 控制 权 能够 继续 执行 后面 的 代码 那么 这种 调用 方式 被称为 “ 异步调用”。 同步 调用 也 被 一些 学者 称为 “ 阻塞调用”一些 相对 的 异步 调用 则 被称为 “ 非阻塞调用”。 6.2 委托的异步调用 位置 2202理论上 讲 任何 一个 方法 通过 委托 包装 后 都可以 实现 异步 调用。 .NET 编译器 定义 的 每个 委托 类型 都 自动 生成 了 两个 方法BeginInvoke 和 EndInvoke。这 两个 方法 专门 用来 负责 异步 调用 委托。BeginInvoke 返回 一个 IAsyncResult 接口 类型 它可 以 唯一 区分 一个 异步 调用 过程。BeginInvoke 一 执行 就能 马上 返回 不会 阻塞 调用 线程。 EndInvoke 表示 结束 对 委托 的 异步 调用 但这 并不 意味着 它可 以 中断 异步 调用 过程 如果 异步 调用 还未 结束 EndInvoke 则 只能 等待 直到 异步 调用 过程 结束。另外 如果 委托 带有 返回 值 我们 必须 通过 EndInvoke 获得 这个 返回 结果。 6.2 委托的异步调用 位置 2233委托 异步 调用 开始 后 系统 会在 线程 池 中 找到 一个 空闲 的 线程 去 执行 委托。 6.2 委托的异步调用 位置 2296异步 调用 委托 时 由于 方法 实际 运行 在 其他 线程 中 线程 池 中的 某一 线程 非 当前 调用 线程 因此 当前 线程 捕获 不了 异常 那么 我们 怎样 才能 知道 异步 调用 过程中 到底 是否 会有 异常 呢答案 就在 EndInvoke 方法 上 如果 异步 调用 过程 有 异常 那么 该 异常 就会 在 我们 在 调用 EndInvoke 方法 时 抛出。所以 我们 在 调用 EndInvoke 方法 时 一定 要把 它 放在 try/catch 块 中。 6.3 非委托的异步调用 位置 2358.NET 中 提供 异步 方法 的 类型 有 Stream 或 其 派生 类、 Socket 或 其 派生 类 以及 访问 数据库 的 SqlConnection 类型 等。它们 的 使用 方式 跟 委托 的 BeginInvoke 和 EndInvoke 方法 类似 只是 命名 有所 差别 基本上 都是 “Begin 操作” 和 “ End 操作” 这种 格式。比如 FileStream. BeginRead 表示 开始 一个 异步 读 操作 而 FileStream. EndRead 则 表示 结束 异步 读 操作。 6.6 本章思考 位置 2446异步 编程 与 多 线程 编程 的 效果 类似 都是 为了 能够 并行 执行 代码 达到 同时 处理 任务 的 目的。 异步编程 时 系统 自己 通过 线程池 来 分配 线程 不需要 人工干预 异步 编程 逻辑 复杂 不易 理解 而 多 线程 编程 时 完全 需要 人为 去 控制 相对 较 灵活。 第 7 章 可复用代码组件的来龙去脉7.1 .NET 中的组件 位置 2457在. NET 编程 中 我们 把 实现 直接 或者 间接 System.ComponentModel.IComponent 接口 的 类型 称为 “组件” 7.1 .NET 中的组件 位置 2472组件 和 控 件 不是 相等 的 组件包含控件 控 件 只是 组件 中的 一个 分类。 7.1 .NET 中的组件 位置 2477图 7- 2 Windows Forms 中 控 件 之间 的 关系 所 有的 控 件 均 派生 自 Control 类 Control 类 又 属于 组件 因此 所有 控 件 均 具备 组件 的 特性。不管 组件 还是 控 件 它们 都是 可以 重复 使用 的 代码 集合 都 实现 了 IDisposable 接口 都 需要 遵循 第 4 章 中 讲到 的 Dispose 模式。如果 一个 类型 使 用了 非 托管 资源 它 实现 IDisposable 接口 就可以 了 那 为什么 还要 在. NET 编程 中 又 提出 组件 的 概念 呢这样做 可以 说完 全是 为了 实现 程序 的 “ 可视化开发” 也就是 我们 常说 的 “所见 即 所得”。在 类似 Visual Studio 这样 的 开发 环境 中 一切 “ 组件” 均 可被 可 视 化 设计 换句话说 只要 我们 定义 的 类型 实现 了 IComponent 接口 那么 在开 发 阶段 该 类型 就可以 出现 在窗 体 设计 器 中 我们 就可以 使用 窗体 设计 器 编辑 它的 属性、 给 它 注册 事件。它 还能 被 窗体 设计 器 中 别的 组件 识别。 7.2 容器 – 组件 – 服务模型 位置 2499在. NET 编程 中 把 所有 实现 直接 或 间接 System. ComponentModel.IContainer 接口 的 类型 都 称之为 逻辑 容器 以下 简称 “容器”。容器 是 为 组件 服务 的。.NET 框架 中有 一个 IContainer 接口 的 默认 实现System. ComponentModel.Container 类型 该类 型 默认 实现 了 IContainer 接口 中的 方法 以及 属性。 7.2 容器 – 组件 – 服务模型 位置 2514传统 容器 仅仅 是在 空间 上 简单 地 将 数据 组织 在一起 并不 能为 数据 之间 的 交互 提供 支持。而 本章 讨论 的 逻辑容器 在 某种 意义上 讲 更为 高级。它 能为 组件 逻辑 元素 之间 的 通信 提供 支持 组件 与 组件 之间 不再 是 独立 存在。此外 它 还能 直接 给 组件 提供 某些 服务。物理 容器 和 逻辑 容器 分别 与 元素 之间 的 关系 如图 7- 4 所示。物理 容器 中的 元素 之间 不能 相互 通信 物理 容器 也不 可能 为 其内 部 元素 提供 服务逻辑 容器 中的 组件 之间 可以 通过 逻辑 容器 作为 桥梁 进行 数据 交换同时 逻辑 容器 还能 给 各个 组件 提供 服务。所谓 服务 就是 指 逻辑 容器 能够 给 组件 提供 一些 访问 支持。比如 某个 组件 需要 知道 它的 所属 容器 中共 包含 有 多少 个 组件 那么 它 就 可以向 容器 发出 请求。容器 收到 请求 后 会为 它 返回 一个 获取 组件 总数 的 接口。 在 本章 7. 1. 1 小节 中 我们 提 到过 IComponent 接口 中有 一个 ISite 类型 的 属性。当时 说 它是 起到 一个 “定位” 的 作用。现在 看来 组件 与 容器 之间 的 纽带 就 是它 组件 通过 该 属性 与 它 所属 容器 取得 了 联系。 7.2 容器 – 组件 – 服务模型 位置 2574Component、 Site 以及 Container 3 个 类型 均 包含 有 获取 服务 的 方法 GetService。现在 我们 可以 整理 一下 组件 向 容器 请求 服务 的 流程 如图 7- 6 所示。 注容器 将 组件 添加 进来 时 执行 Container. Add 会 初始化 该 组件 的 Site 属性 让 该 组件 与 容器 产生 关联 只有 当 这一 过程 发生 之后 组件 才能 获取 容器 的 服务。 7.2 容器 – 组件 – 服务模型 位置 2601在 我们 向 窗体 设计 器 中 拖动控件 时 是 会 执行 类似 “new Button” 这样 的 代码 在 内存 中 实例化 一个 组件 实例。 7.2 容器 – 组件 – 服务模型 位置 2655图 7- 10 窗体 设计 器 中的 组件 与 生成 的 源 代码在 图 7- 10 中 图中 左边 显示 我们 拖放 到 设计 器 中的 一个 Button 控 件。在 这个 过程中 窗体 设计 器 除了 会 实例 化 一个 Button 控 件 图中 左边 Form2 中 还会 为我 们 生成 右边 的 代码。 7.3 设计时与运行时 位置 2672任何 组件 都有 两种 状态设计时 和 运行时。判断 组件 的 当前状态 有 以下 两种 方法。1 判断 组件 的 DesignMode 属性。每个 组件 都有 一个 Bool 类型 的 DesignMode 属性 正如 它的 字面 意思 如果 该 属性 为 true 那么 代表 组件 当前 处于 设计 时 状态否则 该 组件 处于 运行时 状态。2 随便 请求 一个 服务 看 返回 来的 服务 接口 是否 为 null。前面 提 到过 当 一个 组件 不属于 任何 一个 容器 时 那么 它 通过 GetService 方法 请求 的 服务 肯定 返回 为 null。 注12 方法 均不 适合 嵌套组件 因为 窗体 设计 器 只会 将 最外 层 组件 的 DesignMode 属性 值 设置 为 true。有 一种 可以 解决 嵌套 组件 中 无法 判断 其 子 组件 状态 的 方法 那就 是 通过 Process 类 来 检查 当前 进程 的 名称。看 它是 否 包含 “devenv” 这个 字符串。如果 有 那么 说明 组件 当前 处于 Visual Studio 开发 环境 中 即 组件 处于 设计 时 if Process. GetCurrentProcess (). ProcessName. Contains”devenv” 为 假 说明 组件 处于 运行时。这种 方法 也有 一个 弊端 很 明显 如果 我们 使用 的 不是 Visual Studio 开发 环境 即 进程 名 不 包含 devenv 或者 我们自己 的 程序 进程 名称 本身 就 已经 包含 了 devenv 那么 该 怎么办 呢 在开 发 一些 需要 授权 的 组件 时 就可以 用到 组件 的 两种 状态。这些 需要 授权 的 组件 收费 对象 一般 是 开发者。因此 在 开发者 使用 这些 组件 开发 系统 的 时候 处于 开发 阶段 就应 该有 授权 入口 而 当 程序 运行 之后 就不 应该 出现 授权 的 界面。 7.4 控件 位置 2776无论是 复合 控 件、 扩展 控 件 还是 自定义 控 件 我们 均可 以 重写 控 件 的 窗口过程WndProc 虚 方法 从 根源 上 接触 到 Windows 消息 这个 做法 并不是 自定义 控 件 的 专利。 第 8 章 经典重视桌面 GUI 框架揭秘8.2 Win32 应用程序的结构 位置 2841程序 是 无法 直接 识别 用户 键盘 或者 鼠标 等 设备 的 输入 信息 这些 输入 必须 先由 操作系统 转换 成 固定 格式 数据 之后 才能 被 程序 使用。在 Windows 编程 中 我们 把 由 操作系统 转换 之后 的 固定 格式 数据 称为 Windows 消息。Windows 消息 是一 种 预 定义 的 数据 结构 比如 C 中的 Struct 它 包含 有 消息 类型、 消息 接收者 以及 消息 参数 等 信息。我们 还把 程序 中 获取 Windows 消息 的 结构 称之为 Windows 消息循环。Windows 消息 循环 在 代码 中就 是一 个 循环 结构 比如 while 循环 它 不停 地 从 操作系统 中 获取 Windows 消息 然后 交给 程序 去 处理。 8.2 Win32 应用程序的结构 位置 2895在 Windows 中 其实 将 消息 分成 了 两类 一类 需要 存入 消息 队列 然后 由 消息 循环 取出 来之 后才 能被 窗口 过程 处理 这类 消息 被 称之为 “ 队列消息” Queued Message。这类 消息 主要 包括 用户 的 鼠标 键盘 输入 消息、 绘制 消息 WM_ PAINT、 退出 消息 WM_ QUIT 以及 时间 消息 WM_ TIMER。另 一类 是 不需要 存入 消息 队列 也不 经过 消息 循环 它们 直接 传递 给 窗口 过程 由 窗口 过程 直接 处理 这类 消息 被 称之为 “ 非队列消息” Nonqueued Message。当 操作系统 想要 告诉 窗口 发生了 某 件事 时 它 会 给 窗口 发送 一个 非 队列 消息 比如 当 我们 使用 SetWindowPos API 移动 窗口 后 系统 自动 会 发送 一个 WM_ WINDOWPOSCHANGED 消息 给 该 窗口 的 窗口 过程 告诉 它 位置 发生 变化 了。 8.4 Windows Forms 框架 位置 3148在 Windows Forms 框架 中 以 Control 为 基 类 其他 所有 与 窗体 显示 有关 的 控 件 几乎 都 派生 自 它Control 类 中的 WndProc 虚 方法 就是 我们 在 Win32 开发 模式 中 所 熟悉 的 窗口 过程。另外 前面 也 讲到 过 窗体 和 控 件 本质上 是一 个 东西 只是 它们 有着 不同 的 属性 所以 我们 可以 看到 窗体 类 Form 间接 派生 自 Control 类。 Winform 程序 中 包含 3 个 部分消息 队列、 UI 线程 以及 控 件。 8.5 Winform 程序的结构 位置 3180在 每个 Winform 程序 的 Program. cs 文件 中 都有 一个 Main 方法 该 Main 方法 就是 程序 的 入口 方法。每个 程序 启动 时 都会 以 Main 方法 为 入口 创建 一个 线程 这个 线程 就是 UI 线程。可能 你会 问 UI 线程 怎么 没有 消息 循环 呢那是 因为 Main 方法 中 总是 会 出现 一个 类似 Application. Run 的 方法 而 消息 循环 就 隐 藏在 了 该 方法 内部 具体 参见 下一 小节 内容。一个 程序 理论上 可以 有 多个 UI 线程 且 每个 线程 都有 自己的 消息 队列 由 操作系统 维护、 消息 循环、 窗体 等 元素 如图 8- 13 所示。 由于 UI 线程 之间 的 数据 交换 比较 复杂 因此 在 实际 开发 中 在 没有 特殊 需求 的 情况下 一个 程序 一般 只 包含 有一个 UI 线程。 8.5 Winform 程序的结构 位置 3265对 UI 线程 的 认识① 一个 程序 可以 包含 有 多个 UI 线程 我们 完全可以 通过 System. Threading. Thread 类 新创 建 一个 普通 线程 然后 在 该 线程 中 调用 Application. Run 方法 来 开启 消息 循环② 一个 UI 线程 结束 该 线程 中的 消息 循环 个数 为 “0” 后 将会 激发 Application. ThreadExit 事件 告知 有 UI 线程 结束 只有 当 所有 UI 线程 都 结束 程序 中 消息 循环 总数 为 “ 0” 才会 激发 Application. Exit 事件 告知 应用 程序 退出。最后 我们 再来 看一下 Windows Forms 中 消息 循环 的 结构图 如图 8- 14 所示。 8.5 Winform 程序的结构 位置 3319Windows Forms 框架 将 窗体 和 窗口 过程 封 装在 了 一起 Control 或 其 派生 类 下同 类 中的 WndProc 虚 方法 就是 控 件 的 窗口 过程。之所以 将 窗口 过程 声明 为 虚 方法 这是 因为 Windows Forms 框架 是 面向 对象 的 它 充分 地利 用了 面向 对象 编程 中的 “多 态” 特性。因此 所有 Control 类 的 派生 类 均可 以 重写 它的 窗口 过程 从而 从 源头 上 拦截 到 Windows 消息 处理 自己 想要 处理 的 Windows 消息。 窗口 过程 做了 一个 非常 重要的 事将 Window 消息 转换 成了. NET 中的 事件。 第 9 章 沟通无碍网络编程9.1 两种 Socket 通信方式 位置 3518TCP数据 是按 顺序 走在 建立 的 一条 隧道 中 那么 数据 就应 该 遵循 “先走 先 到达” 的 规则 并且 隧道 中的 数据 以 “ 流” 的 形式 传输。发送 方 发送 的 前后 两次 数据 之间 没有 边界 需要 接收 方自 己 根据 事先 规定 好的 “ 协议” 去 判断 数据 边界。 9.1 两种 Socket 通信方式 位置 3555UDP 通信 中 数据 是以 “数 据报” 的 形式 传输 以 一个 整体 发送、 以 一个 整体 接收 因此 UDP 存在 数据 边界。但是 UDP 接 收到 的 数据 是 无序 的 先 发送 的 可能 后接 收 后 发送 的 可能 先 接收 甚至 有的 接收 不到。 9.1 两种 Socket 通信方式 位置 3605在. NET 中 有关 Socket 通信 编程 的 类型 主要 有 5 种 见表 9- 1。 TcpListener 和 TcpClient 的 关系 如图 9- 9 所示图 9- 9 中 TcpListener 侦听 来自 客户 端 的 “连接” 请求 返回 一个 代理 TcpClient 该 代理 与 客户 端 的 TcpClient 进行 数据 交换。UdpClient 在 UDP 通信 中 所处 的 角色 如图 9- 10 所示 NetworkStream 类型 是 System. IO. Stream 类 的 一个 派生 类。NetworkStream 只能 用于 TCP 通信 中。Socket 类型 是一 个 相对 较 基础 的 通信 类型。它 既能 实现 TCP 通信 也能 实现 UDP 通信 可以 认为 TcpListener、 TcpClient 以及 UdpClient 进一步 封装 了 Socket 类型。 9.3 UDP 通信的实现 位置 3840之所以 将 TCP 通信 中 应用 层 协议 的 数据 结构设计 成 字节流 的 形式 是因为 TCP 通信 中 数据 是以 流的 形式 传输 以 字节 流的 形式 格式化 数据 后 更 方便 程序 判断 数据 边界。而在 UDP 通信 中 数据 以 数据 报的 形式 传输 每次 接 收到 的 数据 是 完整 的 对于 当前 局域网 即时 通信 实例 来讲 协议 使用 文本 的 形式 更 方便 程序 处理 数据 当然 我们 完全 也可以 将 UDP 通信 中 应用 层 协议 的 数据 结构设计 成 字节 流的 形式。 9.4 异步编程在网络编程中的应用 位置 3991使用 Socket. BeginSendTo 方法 开始 一个 异步 发送 过程 并为 该 方法 提供 一个 AsyncCallback 的 回 调 参数。该 方法 的 调用 不会 阻塞 调用 线程。我们 在 回 调 方法 OnSend 中 可使用 Socket. EndSendTo 方法 结束 异步 发送 过程 该 方法 返回 实际 发送 的 数据 长度。 9.4 异步编程在网络编程中的应用 位置 4011异步 编程 也能 实现 循环 接收 数据 但却 看 不到 显 式 地 创建 的 线程 也 看不 到 类似 while 这样 的 循环 语句。 9.6 本章思考 位置 4062所有 的 通信协议 本质上 都是 一种 数据结构。通信 双方 都 必须 按照 这种 数据 结构 规定 的 形式 去 发送 或 接收 解析 数据。基于 TCP 协议 的 通信 在 进行 数据 交互 之前 需要 先 建立 连接 类似 打电话。这种 通信 方式 保证 了 数据 传输 的 正确性、 可靠性。基于 UDP 协议 的 通信 在 进行 数据 传输 之前 不需要 建立 连接 类似 发 短信。这种 通信 方式 不能 保证 数据 传输 的 正确性。 第 10 章 动力之源代码中的 “泵”10.2 常见的 “泵” 结构 位置 4150桌面 程序 的 UI 线程 中 包含 一个 消息 循环 确切 地说 应该 是 While 循环。该 循环 不断 地 从 消息 队列 中 获取 Windows 消息 最终 通过 调用 对应 的 窗口 过程 将 Windows 消息 传递 给 窗口 过程 进行 处理。 10.2 常见的 “泵” 结构 位置 4179浏览器 每次 发送 http 请求 时 都 必须 与 Web 服务器 建立 连接。Web 服务器 端 请求 处理 结束 后 连接 立刻 关闭。浏览器 下一 次 发送 http 请求 时 必须 再一次 重新 与 服务器 建立 连接。由此可见 我们 所说 的 HTTP 协议 是 面向 无 连接 的 具体 指 Web 服务器 一次 连接 只 处理 一个 请求 请求 处理 完毕 后 连接 关闭 浏览器 在前 一次 请求 结束 到下 一次 请求 开始 之前 这段 时间 它是 处于 “断开” 状态 的 因此 称 HTTP 协议 是 “ 无连接” 协议。Web 服务器 除了 跟 浏览器 之间 不会 保持 持久 性的 连接 之外 它 也不 会 保存 浏览器 的 状态。也就是说 同一 浏览器 先后 两次 请求 同一个 Web 服务器 后者 不会 保留 第一次 请求 处理 的 结果 到 第二次 请求 阶段如果 第二次 请求 需要 使用 第一次 请求 处理 的 结果 那么 浏览器 必须 自己 将 第一次 的 处理 结果 回 传到 服务器 端。 第 11 章 规绳矩墨模式与原则11.1 软件的设计模式 位置 4308程序 的 运行 意味着 模块 与 模块 之间、 对象 与 对象 之间 不停 地 有数 据 交换。 观察者模式 要 强调 的 是 当 一个 目标 本身 的 状态 发生 改变 或者 满足 某一 条件 时 它 会 主动 发出通知 通知 对 该 变化 感兴趣 的 其他 对象。将 通知者 称为 Subject 主体 将被 通知者 称为 Observer 观察者 11.1 软件的设计模式 位置 4358“观察者 模式” 是 所有 框架 使用 得 最 频繁 的 设计 模式 之一。原因 很 简单“ 观察者 模式” 分隔 开了 框架 代码 和 框架 使用者 编写 的 代码。它是 “ 好莱坞原则” Don\’ t call us we will call you 的 具体 实现 手段 而 “好莱坞 原则” 是 所有 框架 都要 严格遵守 的。 11.1 软件的设计模式 位置 4361在 Windows Forms 框架 中 可以说 “观察者 模式” 无处不在。Windows Forms 框架 中的 “ 观察者 模式” 不是 通过 “ 接口 – 具体” 这种 方式 去 实现 的 而是 更多 地 通过 使用. NET 中的 “ 委托 – 事件” 去 实现。这 在 第 8 章 讲 Winform 程序 结构 时 已经 有所 说明 比如 控 件 处理 Windows 消息 时 最终 是以 “事件” 的 形式 通知 事件 注册 者 那么 这里 的 事件注册者 就是 观察者 模式 中的 “ 观察者” 控件 就是 观察者 模式 中的 “ 主体”。可以 认为 事件 的 发布者 等于 观察者 模式 中的 “主体” Subject 而 事件 的 注册 者 等于 观察者 模式 中的 “ 观察者” 11.1 软件的设计模式 位置 4397根据 各种 设计 模式 的 作用 将 常见 的 23 种 设计模式 分为 3 大类 见表 11- 1。 11.5 本章思考 位置 4605五大原则 及 英文 全称 分别 如下。① 单一 职责 原则 Single Responsibility Principle。② 开闭 原则 Open Closed Principle。③ 里 氏 替换 原则 Liskov Substitution Principle。④ 接口 隔离 原则 Interface Segregation Principle。⑤ 依赖 倒置 原则 Dependency Inversion Principle。 第 12 章 难免的尴尬代码依赖12.1 从面向对象开始 位置 4738类继承 强调 “我是 Is- A” 的 关系 派生 类 “ 是” 基 类 注意 这里 的 “ 是” 代表 派生 类 具备 基 类 的 特性 而 接口继承 强调 “我 能做 Can- Do” 的 关系 实现 了 接口 的 类型 具有 接口 中 规定 的 行为 能力 因此 接口 在 命名 时 均以 “ able” 作为 后缀。 12.1 从面向对象开始 位置 4743在使 用 继承 时 应 遵循 以下 准则。1 严格遵守 “里 氏 替换 原则” 即 基 类 出现 的 地方 派生 类 一定 可以 出现。因此 不要 盲目 地 去使 用 继承 如果 两个 类 没有 衍生 的 关系 就不 应该 有 继承 关系。2 由于 派生 类 会 继承 基 类 的 全部 内容 所以 要 严格控制 好 类型 的 继承 层次 不然 派生 类 的 体积 会 越来越大。另外 继承 是 增加 耦合 的 最重要 因素 基 类 的 修改 必然会 影响 到 派生 类。3 继承 强调 类型 之 间的 通 性 而非 特性。因此 一般 将 类型 都 具有 的 部分 提取 出来 形成 一个 基 类 抽象 类 或者 接口。 12.2 不可避免的代码依赖 位置 4815为了 衡量 对象 之间 依赖 程度 的 高低 引进 了 “ 耦合” 这一 概念。耦合 度 越高 说明 对象 之间 的 依赖 程度 越高。为了 衡量 对象 独立 性的 高低 引进 了 “ 内聚” 这一 概念。内聚性 越高 说明 对象 与外 界 交互 越少 独立性 越 强。很 明显 耦合 与 内 聚 是 两个 相互 对立 又 密切相关 的 概念。 12.3 降低代码依赖 位置 4936除了 上面 说到 的 将相 同 部分 提取 出来 放到 一个 接口 中 有时候 还需 要将 相同 部分 提取 出来 生成 一个 抽象化 的 基 类 如 抽象类。 接口 强调 相同 的 行为 而 抽象 类 一般 强调 相同 的 属性 并且 要使 用在 有 族群 层次 的 类型 设计 中。 12.3 降低代码依赖 位置 4987通过 属性 产生 的 依赖 关系属性注入 比较 灵活 它的 有效期 一般 介于 “构造 注入” 和 “ 方法 注入” 之间。在 很多 场合 3 种 依赖注入 的 方式 可以 组合 使用 即 可以 先 通过 “构造 注入” 让 依赖 者 与 被 依赖 者 产生 依赖 关系 后期 再 使用 “ 属性 注入” 的 方式 更改 它们 之间 的 依赖 关系。“ 需要 注意 的 是 依赖 注入” 是以 “ 依赖 倒置”” 为 前提 的。 12.4 框架的 “代码依赖” 位置 4998注“ 控制转换、 依赖倒置 以及 依赖注入 是 3 个 不同 性质 的 概念。“ 控制转换” 强调 程序控制 权 的 转移 注重 软件 运行 流程“ 依赖倒置” 是一 种 降低 代码 依赖 程度 的 理论 指导思想 它 注重 软件 结构“ 依赖注入” 是对 象之 间 产生 依赖 关系 的 一种 具体 实现 方式 它 注重 编程 实现。“控制 转换” 又称 “ 好莱坞 原则” 它 建议 框架 与 开发者 编写 代码 之间 的 关系 是 Don\’ t call us we will call you 即 整个 程序 的 主动权 在 框架 手中。 12.6 本章思考 位置 5022“依赖 倒置 原则” 中的 “ 倒置” 二字 作 何 解释A正常 逻辑思维 中 高层 模块 依赖 底层 模块 是 天经地义、 理所当然 的 而 “依赖 倒置 原则” 建议 我们 所有 的 高层 模块 不应该 直接 依赖于 底层 模块 而 都 应该 依赖于 一个 抽象。这里 的 “ 倒置” 二字 并不是 “ 反过来” 的 意思 即 底层 模块 反过来 依赖于 高层 模块 它 只是 说明 正常 逻辑思维 中的 依赖 顺序 发生了 变化 把 所有 违背 了 正常 思维 的 东西 都 称之为 “ 倒置”。