主頁 > .NET開發 > [讀書筆記] 《修煉之道:.NET 開發要點精講》

[讀書筆記] 《修煉之道:.NET 開發要點精講》

2020-10-25 18:15:59 .NET開發

《修煉之道:.NET 開發要點精講》

作者:周見智;博圖軒


第 1 章 另辟蹊徑:解讀.NET

1.7 本章思考 > 位置 465

1. 簡述. NET 平臺 中 CTS、 CLS 以及 CLR 的 含義 與 作用,

A: CTS 指 公共 型別 系統, 是. NET 平臺 中 各種 語言 必須 遵守 的 型別 規范; CLS 指 公共 語言 規范, 是. NET 平臺 中 各種 語言 必須 遵守 的 語言 規范; CLR 指 公共 語言 運行時, 它是 一個 虛擬 機,. NET 平臺 中 所有 的 托管 代碼 均需 要在 CLR 中 運行, 可將 其 視為 另外 一個 作業系統,

 

第 2 章 高屋建瓴:梳理編程約定

2.2 方法與執行緒的關系 > 位置 519

只要 我們 確定 了 兩個 方法 只會 運行 在 同一個 執行緒 中, 那么 這 兩個 方法 就不 可能 同時 執行, 跟 方法 所處 的 位置 無關,

只可 能 一前一后 執行, 此時 我們 不需要 考慮 方法 中 訪問 的 公共 資源 的 執行緒 是否 安全,

 

2.7 執行緒安全 > 位置 595

如果 操作 一個 物件( 比如 呼叫 它的 方法 或者 給 屬性 賦值) 為 非 原子 操作, 即可 能 操作 還沒 完成 就 暫停 了, 這個時候 如果 有 另外 一個 執行緒 開始 運行 同時 也 操作 這個 物件, 訪問 了 同樣 的 方法( 或 屬性), 那么 這時 就可能 會 出現 一個 問題: 前一 個 操作 還未 結束, 后 一個 操作 就 開始 了, 前后 兩個 操作 一起 出現 混亂, 當 多個 執行緒 同時 訪問 一個 物件 時, 如果 每次 執行 都得 到 不一樣 的 結果, 甚至 出現 例外, 那么 這個 物件 便是 “非 執行緒 安全”, 造成 一個 物件 非 執行緒 安全 的 因素 有 很多, 除了 前面 提到 的 由于 非 原子 操作 執行 到 一半 就 中斷 以外, 還有 一種 情況 是由 多個 CPU 造成 的, 即 就算 操作 沒有 中斷, 由于 多個 CPU 可以 真正 實作 多 執行緒 同時 運行, 所以 就有 可能 出現 “ 對 同一 物件 同時 操作 出現 混亂” 的 情況,如圖 2- 4 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖

 

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

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(1)

圖 2- 11   網路 七 層 協議

 

第 3 章 編程之基礎:資料型別

3.1 參考型別與值型別 > 位置 844

通常 值 型別 又 分為 以下 兩個 部分,

(1) 簡單 值 型別: 包括 類似 int、 bool、 long 等. NET 內置 型別, 它們 本質上 也是 一種 結構 體,

(2) 復合 值 型別: 使用 Struct 關鍵字 定義 的 結構 體, 比如 System. Drawing. Point 等, 復合 值 型別 可以 由 簡單 值 型別 和 參考 型別 組成,

 

3.3 賦值與復制 > 位置 1118

參考 型別 的 賦值、 淺 復制、 深 復制 的 區別, 如圖 3- 15 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(2)

 

值 型別 的 賦值、 淺 復制、 深 復制 的 區別, 如圖 3- 16 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(3)

 

物件 深 復制 的 程序, 如圖 3- 17 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(4)

 

3.3 賦值與復制 > 位置 1155

NET 中 可以 使用 “序列 化 和 反 序列 化” 的 技術 實作 物件 的 深 復制, 只要 一個 型別 及其 所有 成員 的 型別 都 標示 為 “ 可 序列 化”, 那么 就可以 先 序列 化 該類 型 物件 到 字 節流, 然后 再將 位元組 流 反序 列 化成 源 物件 的 副本, 這樣一來, 源 物件 與 副本 之間 沒有 任何 關聯, 從而 達到 深 復制 的 效果,

 

第 4 章 物以類聚:物件也有生命

4.1 堆和堆疊 > 位置 1235

堆疊 主要 用來 記錄 程式 的 運行 程序, 它有 嚴格 的 存盤 和 訪問 順序; 而 堆 主要 存盤 程式 運行 期間 產生 的 一些 資料, 幾乎沒有 順序 的 概念,

 

4.1 堆和堆疊 > 位置 1269

堆 跟 堆疊 的 本質 都是 一段 記憶體塊,

 

4.2 堆中物件的出生與死亡 > 位置 1272

堆疊 中的 物件 由 系統 負責 自動 存入 和 移 除, 正常 情況下, 跟我 們 程式 開發 的 關聯 并不 大,

 

4.2 堆中物件的出生與死亡 > 位置 1354

謹慎 使用 物件 的 析構方法, 析 構 方法 由 CLR 呼叫, 不受 程式控制, 而且 容易 造成 物件 重生, 析 構 方法 除了 用作 管理 非 托管 資源 外, 幾乎 不能 用作 其他 用途,

 

4.3 管理非托管資源 > 位置 1381

GC. 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 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(5)

每一個 委托 型別 都有 一個 公開 的 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 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(6)

在 圖 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

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(7)

圖 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. 1 
    MethodInfo _method; //NO. 2 
    
    public 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 所示, 為 弱 委托 的 結構,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(8)

本 小節 示例 代碼 中的 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

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(9)

圖 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 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(10)

物理 容器 中的 元素 之間 不能 相互 通信, 物理 容器 也不 可能 為 其內 部 元素 提供 服務; 邏輯 容器 中的 組件 之間 可以 通過 邏輯 容器 作為 橋梁, 進行 資料 交換; 同時, 邏輯 容器 還能 給 各個 組件 提供 服務, 所謂 服務, 就是 指 邏輯 容器 能夠 給 組件 提供 一些 訪問 支持, 比如 某個 組件 需要 知道 它的 所屬 容器 中共 包含 有 多少 個 組件, 那么 它 就 可以向 容器 發出 請求, 容器 收到 請求 后 會為 它 回傳 一個 獲取 組件 總數 的 介面,

 

在 本章 7. 1. 1 小節 中 我們 提 到過 IComponent 介面 中有 一個 ISite 型別 的 屬性, 當時 說 它是 起到 一個 “定位” 的 作用, 現在 看來, 組件 與 容器 之間 的 紐帶 就 是它, 組件 通過 該 屬性 與 它 所屬 容器 取得 了 聯系,

 

7.2 容器 – 組件 – 服務模型 > 位置 2574

Component、 Site 以及 Container 3 個 型別 均 包含 有 獲取 服務 的 方法 GetService, 現在 我們 可以 整理 一下 組件 向 容器 請求 服務 的 流程, 如圖 7- 6 所示,
[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(11)

 

注: 容器 將 組件 添加 進來 時( 執行 Container. Add), 會 初始化 該 組件 的 Site 屬性, 讓 該 組件 與 容器 產生 關聯, 只有 當 這一 程序 發生 之后, 組件 才能 獲取 容器 的 服務,

 

7.2 容器 – 組件 – 服務模型 > 位置 2601

在 我們 向 表單 設計 器 中 拖動控制元件 時, 是 會 執行 類似 “new Button();” 這樣 的 代碼, 在 記憶體 中 實體化 一個 組件 實體,

 

7.2 容器 – 組件 – 服務模型 > 位置 2655

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(12)

圖 7- 10   表單 設計 器 中的 組件 與 生成 的 源 代碼

在 圖 7- 10 中, 圖中 左邊 顯示 我們 拖放 到 設計 器 中的 一個 Button 控 件, 在 這個 程序中, 表單 設計 器 除了 會 實體 化 一個 Button 控 件( 圖中 左邊 Form2 中), 還會 為我 們 生成 右邊 的 代碼,

 

7.3 設計時與運行時 > 位置 2672

任何 組件 都有 兩種 狀態: 設計時 和 運行時,

判斷 組件 的 當前狀態 有 以下 兩種 方法,

(1) 判斷 組件 的 DesignMode 屬性, 每個 組件 都有 一個 Bool 型別 的 DesignMode 屬性, 正如 它的 字面 意思, 如果 該 屬性 為 true, 那么 代表 組件 當前 處于 設計 時 狀態; 否則 該 組件 處于 運行時 狀態,

(2) 隨便 請求 一個 服務, 看 回傳 來的 服務 介面 是否 為 null, 前面 提 到過, 當 一個 組件 不屬于 任何 一個 容器 時, 那么 它 通過 GetService 方法 請求 的 服務 肯定 回傳 為 null,

 

注:(1)(2) 方法 均不 適合 嵌套組件, 因為 表單 設計 器 只會 將 最外 層 組件 的 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 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(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 所示,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(14)

 

8.5 Winform 程式的結構 > 位置 3319

Windows Forms 框架 將 表單 和 視窗 程序 封 裝在 了 一起, Control( 或 其 派生 類, 下同) 類 中的 WndProc 虛 方法 就是 控 件 的 視窗 程序, 之所以 將 視窗 程序 宣告 為 虛 方法, 這是 因為 Windows Forms 框架 是 面向 物件 的, 它 充分 地利 用了 面向 物件 編程 中的 “多 態” 特性, 因此, 所有 Control 類 的 派生 類 均可 以 重寫 它的 視窗 程序, 從而 從 源頭 上 攔截 到 Windows 訊息, 處理 自己 想要 處理 的 Windows 訊息,

 

視窗 程序 做了 一個 非常 重要的 事: 將 Window 訊息 轉換 成了. NET 中的 事件,

 

第 9 章 溝通無礙:網路編程

9.1 兩種 Socket 通信方式 > 位置 3518

(TCP)資料 是按 順序 走在 建立 的 一條 隧道 中, 那么 資料 就應 該 遵循 “先走 先 到達” 的 規則, 并且 隧道 中的 資料 以 “ 流” 的 形式 傳輸, 發送 方 發送 的 前后 兩次 資料 之間 沒有 邊界, 需要 接收 方自 己 根據 事先 規定 好的 “ 協議” 去 判斷 資料 邊界,

 

9.1 兩種 Socket 通信方式 > 位置 3555

UDP 通信 中, 資料 是以 “數 據報” 的 形式 傳輸, 以 一個 整體 發送、 以 一個 整體 接收, 因此 UDP 存在 資料 邊界, 但是 UDP 接 收到 的 資料 是 無序 的, 先 發送 的 可能 后接 收, 后 發送 的 可能 先 接收, 甚至 有的 接收 不到,

 

9.1 兩種 Socket 通信方式 > 位置 3605

在. NET 中 有關 Socket 通信 編程 的 型別 主要 有 5 種, 見表 9- 1,

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(15)

 

TcpListener 和 TcpClient 的 關系 如圖 9- 9 所示

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(16)

圖 9- 9 中, TcpListener 偵聽 來自 客戶 端 的 “連接” 請求, 回傳 一個 代理 TcpClient, 該 代理 與 客戶 端 的 TcpClient 進行 資料 交換,

UdpClient 在 UDP 通信 中 所處 的 角色 如圖 9- 10 所示:

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(17)

 

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, 

[讀書筆記] 《修煉之道:.NET 開發要點精講》插圖(18)

 

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: 正常 邏輯思維 中, 高層 模塊 依賴 底層 模塊 是 天經地義、 理所當然 的, 而 “依賴 倒置 原則” 建議 我們 所有 的 高層 模塊 不應該 直接 依賴于 底層 模塊, 而 都 應該 依賴于 一個 抽象, 這里 的 “ 倒置” 二字 并不是 “ 反過來” 的 意思( 即 底層 模塊 反過來 依賴于 高層 模塊), 它 只是 說明 正常 邏輯思維 中的 依賴 順序 發生了 變化, 把 所有 違背 了 正常 思維 的 東西 都 稱之為 “ 倒置”,

 

原創文章,轉載請注明: 轉載自 獨立觀察員?博客

本文鏈接地址: [讀書筆記] 《修煉之道:.NET 開發要點精講》[http://dlgcy.com/dotnet-program-key-point/]

 

微信公眾號

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/190590.html

標籤:.NET技术

上一篇:ASP.NET MVC過濾器粗略總結

下一篇:abp(net core)+easyui+efcore實作倉儲管理系統——出庫管理之三(五十二)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more