《.NET 設計規範》第 9 章:常用的設計模式

来源:http://www.cnblogs.com/liqingwen/archive/2017/08/24/7413104.html
-Advertisement-
Play Games

第 9 章:常用的設計模式 9.1 聚合組件 考慮為常用的特性域提供聚合組件。 要用聚合組件來對高層的概念(物理對象)進行建模,而不是對系統級的任務進行建模。 要讓聚合組件的名字與眾所周知的系統實體相對應,比如 MessageQueue、Process 或 EventLog,這樣就能使類型更加引人註 ...


第 9 章:常用的設計模式

9.1 聚合組件

  考慮為常用的特性域提供聚合組件。

  要用聚合組件來對高層的概念(物理對象)進行建模,而不是對系統級的任務進行建模。

  要讓聚合組件的名字與眾所周知的系統實體相對應,比如 MessageQueue、Process 或 EventLog,這樣就能使類型更加引人註目。

  要在設計聚合組件時使初始化儘可能地簡單,這樣用戶只需進行簡單的初始化就可以使用組件。如果某一項初始化是必需的,那麼由於沒有對組件進行初始化而引發的異常應該明確地告訴用戶應該怎麼做。

  不要要求聚合組件的用戶在一個場景中顯式地實例化多個對象。

  要保證讓聚合組件支持 Create-Set-Call 使用模式,這樣用戶就可以先實例化組件,然後設置它的屬性,最後調用一些簡單的方法,以實現大多數場景。

  要為所有的聚合組件提供預設構造函數或非常簡單的構造函數。

  要為聚合組件提供可讀寫的屬性來與構造函數中的所有參數相對應。

  要在聚合組件中使用事件,不要使用基於委托的 API。

  考慮用事件來代替需要被覆蓋的虛成員。

  不要要求聚合組件的用戶在常用場景中使用繼承、覆蓋方法及實現介面。

  不要要求聚合組件的用戶在常用場景中除了編寫代碼之外,還要做其他的做工。例如,不應該讓用戶用配置文件來配置組件,也不應該讓用戶生成資源文件,等等。

  考慮讓聚合組件能夠自動切換狀態。

  不要涉及有多種狀態的因數類型。

  考慮將聚合組件集成到 VS 的設計器中。

  考慮把聚合組件和因數類型分開,各自放在不同的程式集中。

  考慮把聚合組件內部的因數類型暴露給外界訪問。

 

9.2 Async 模式

  要實現基於事件的 Async 模式 - 如果類型是一個支持可視化設計器的組件(也就是說類型實現了 IComponent)。

  要實現經典的 Async 模式 - 如果必須支持等待句柄。

  考慮在實現高層 API 時使用基於事件的 Async 模式。例如,聚合組件就應該實現該模式。

  考慮在實現底層 API 時使用經典的 Async 模式,在這種情況下更強大的功能、更少的記憶體消耗、更好的靈活性、更少的磁碟占用要比可用性更重要。

  避免在同一個類型中甚至是一組相關的類型中同時實現兩種 Async 模式。

  要在為非同步操作定義 API 時遵循下麵的約定。給定名為 Operation 的同步方法,應該提供名為 BeginOperation 和 EndOperation 的方法,它們的方法簽名如下麵所示(註意,輸出參數不是必需的)。

  要確保 Begin 方法的返回類型實現了 IAsyncResult 介面。

  要確保同步方法的按值傳遞和按引用傳遞的參數在 Begin 方法中都是按值傳遞的。同步方法的輸出參數不應該出現在 Begin 方法的簽名中。

  要確保 End 方法的返回類型與同步方法的返回類型相同。

  要確保同步方法的任何輸出參數和按引用傳遞的參數都作為 End 方法的輸出參數。同步方法中安置傳遞的參數不應該出現在 End 方法的簽名中。

  不要繼續執行非同步操作 - 如果 Begin 方法拋出了異常。

  要一次通過下麵的機制來通知調用方非同步操作已經完成。

    將 IAsyncResult.IsCompleted 設為 true。

    激活 IAsyncResult.AsyncWaitHandle 返回的等待句柄。

    調用非同步回調函數。

  要通過從 End 方法中拋出異常來表示無法成功地完成非同步操作。

  要在 End 方法被調用時同步完成所有尚未完成的操作。。

  考慮拋出 InvalidOperationException 異常 - 如果用戶用同一個 IAsyncResult 兩次調用 End 方法,或 IAsyncResult 是從另一個不相關的 Begin 方法返回的。

  要把 IAsyncResult.CompletedSynchronously 設為 true - 當且僅當非同步回調函數將在調用 Begin 方法的線程中運行的時候。

  要確保在正確的線程中調用事件處理程式。與經典 Async 模式相比,這是使用基於事件的 Async 模式的主要好處之一。

  要確保無論是操作已經完成,還是操作出錯,還是操作被取消,都是種會調用事件處理程式。不應該讓應用程式無休止地等待一間永遠不會發生的事件

  要確保在非同步操作失敗後,訪問時間參數類的屬性會引發異常。換句話說,如果有錯誤導致操作無法完成,那麼就不應該允許用戶訪問操作的結果。

  不要為返回值為空的方法定義新的事件處理程式或事件參數類型。要使用 AsyncCompletedEventArgs,AsyncCompletedEventHandler 或 EventHandler<AsyncCompletedEventArg>。

  要確保如果在一個一步操作中實現了 PaogressChanged 事件,那麼在操作的完成事件被觸發之後,不應該再出現此類事件。

  要確保如果使用了標準的 ProgressChangedEventArgs,那麼 ProgressPercentage 始終能用來表示進度的百分比(不一定要完全精確,但表示的一定要百分比)。如果使用的不是標準進度,那麼從 ProgressChangedEventArgs 派生一個子類會更合適,這種情況下應該保持 ProgressPercentage 為 0 ;

  要在有增量結果需要報告的時候出發 ProgressChanged 事件。

  要對 ProgressChangedEventArgs 進行擴展來保存增量結果數據,並用擴展後的時間參數類來定義 ProgressChanged 事件。

  要把增量結果報告與進度報告分開。

  要為每個非同步操作定義單獨的 <MethodName>ProgreessChanged 事件和相應的事件參數類,來處理該操作的增量結果數據。

  

9.3 依賴屬性

  要提供依賴屬性 - 如果需要用他們來支持各種 WPF 特性,比如樣式、觸發器、數據綁定、動畫、動態資源以及繼承。

  要在設計依賴屬性的時候繼承自 DependencyObject 或它的子類型。該類型實現的屬性存儲區非常高效,它還自動支持 WPF 的數據綁定。

  要為每個依賴屬性提供常規的 CLR 屬性和存放 System.Windows.DependencyProperty 實例的公有靜態只讀欄位。

  要通過調用 DependencyObject.GetValue 和 DependencyObject.SetValue 的方式來實現依賴屬性。

  要用依賴屬性的名字加上“Property”尾碼來命名依賴屬性的靜態欄位。

  不要顯式地在代碼中設置依賴屬性的預設值,應該在元數據中設置預設值。

  不要在屬性的訪問器中添加額外的代碼,而應該使用標準代碼來訪問靜態欄位。

  不要使用依賴屬性來保存保密數據。任何代碼都能訪問依賴屬性,即使它們是私有的。

  不要把依賴屬性的驗證邏輯放在訪問器中,而應該把驗證毀掉函數傳給 DependencyProperty.Register 方法。

  不要在依賴屬性的訪問器中實現屬性改變的通知,而應該向 PropertyMetadata 註冊改變通知的回調函數,後者是依賴屬性本身提供的一項特性,為了支持改變通知,必須使用該特性。

  不要在依賴屬性的訪問器中實現屬性強制賦值邏輯,而應該向 PropertyMetadata 註冊強制賦值的回調函數。後者是依賴屬性本身提供的一項特性,為了支持強制賦值,必須使用該特性。

  

9.4 Disopse 模式

  要為含有可處置類型實例的類型實現基本 Dispose 模式。

  要為類型實現基本 Dispose 模式並提供終結方法 - 如果類型持有需求由開發人員顯式釋放的類型,而且後者本身沒有終結方法。

  考慮為類實現基本 Dispose 模式 - 如果類本身並不持有非托管資源或可處置對象,但是它的子類型卻可能會持有非托管資源或可處置對象。

  要按下麵的方法來實現 IDisposable 介面,即先調用 Dispose(true),然後再調用 GC.SuppressFinalize(this)。

  不要將無參數的 Dispose 方法定義為虛方法。

  不要為 Dispose 方法聲明除了 Dispose() 和 Dispose(bool) 之外的任何其它重載方法。

  要允許多次調用 Dispose(bool) 方法。他可以在第一次調用之後就什麼也不做。

  避免從 Dispose(bool) 方法中拋出異常,除非是緊急情況,所處的進程已經遭到破壞(比如泄漏、共用狀態不一致,等等)。

  要從成員中拋出 ObjectDisposedException 異常 - 如果該成員在對象終結之後就無法繼續使用。

  考慮在 Dispose() 方法之外在提供一個 Close() 方法 - 如果 close 是該領域中的一個標準術語。

  避免定義可終結類型。

  不要定義可終結的值類型。

  要將類型定義為可終結類型 - 如果該類型要負責釋放非托管資源,且非托管資源本身不具備終結方法。

  要為所有的可終結類型實現基本 Dispose 模式。

  不要在終結方法中訪問任何可終結對象,這樣做存在很大的風險,因為被訪問的對象可能已經被終結了。

  要將 Finalize 方法定義為受保護的。

  不要在終結方法中放過任何異常,除非是致命的系統錯誤。

  考慮創建一個用於緊急情況的可終結對象 - 如果終結方法在應用程式域被強制卸載或線程異常退出的情況下都務必要執行。

 

9.5 Factory 模式

  要優先使用構造函數,而不是優先使用工廠,因為與特殊的對象構造機制相比,構造函數一般來說更容易使用、更一致,也更方便。

  考慮使用工廠 - 如果構造函數提供的對象創建機制不能滿足要求。

  要使用工廠 - 如果開發人員可能不清楚待創建的對象的確切類型,比如對基類或介面編程就屬於這種情況。

  考慮使用工廠方法 - 如果這是讓操作不言自明的唯一方法。

  要在轉換風格的操作中使用 factory。

  要儘量將工廠操作方法實現為方法,而不是實現為屬性。

  要通過方法的返回值而不是方法的輸出參數來返回新創建的對象實例。

  考慮把 Create 和要創建的類型名連在一起,一次來命名工廠方法。

  考慮把要創建的類型名和 Factory 連在一起,一次來命名工廠類型。例如,可以考慮把創建 Control 對象的工廠類型命名為 ControlFactory。

  

9.6 對 LINQ 的支持

  要實現 IEnumerabl<T>,其目的是為了得到基本的 LINQ 支持。

  考慮實現 ICollection<T>,其目的是為了提高查詢的性能。

  考慮實現 IQueryable<T> - 如果必須要訪問傳給 IQueryable 的成員的查詢表達式。

  不要草率地實現 IQueryable<T>,要理解這樣做可能會對性能產生什麼影響。

  要在 IQueryable<T> 的方法中拋出 NotSupportedException - 如果你的數據源上不支持該操作。

  要在新類型中將 Query 模式實現為實例方法 - 如果在 LINQ 以外的場合,這些方法在類型中仍然有存在的意義。否則,應該將它們實現為擴展方法。

  要讓實現了 Query 模式在類型實現了 IEnumerable<T>。

  考慮在設計 LINQ 操作符時,讓它們返回領域特有的可枚舉類型。雖然從本質上來說,Select 查詢方法可以返回任何類型,但是大家通常都希望查詢的結果是可枚舉類型。

  避免只實現 Query 模式的一部分 - 如果不希望退回到基本的 IEnuerable<T> 實現。

  要為有序序列定義單獨的類型,從而將它和對應的無序序列分開。這樣的類型應該定義 ThenBy 方法。

  要推遲執行實際的查詢操作。對 Query 模式的大多數成員來說,我希望它們只是創建一個新的對象,併在枚舉的時候才產生集合重負荷查詢條件的元素。

  要將用於查詢的擴展方法放在主命名空間中的一個名為“Linq” 的子命名空間中。例如,為 System.Data 特性定義的擴展方法被放在 System.Data.Linq 命名空間。

  要在參數中使用 Expression<Func<...>>,而不是 Func<...> - 如果需要查詢查詢表達式。

 

9.7 Optional Feature 模式

  考慮將 Optional Feature 模式用於抽象中的可選特性。

  要提供一個簡單的布爾屬性來讓用戶檢測對象是否支持可選特性。

  要在積累中將可選特性定義為虛方法,併在該方法中拋出 NotSupportedException 異常。

  

9.8 Simulated Convariance 模式

  考慮使用 Simulated Convariance 模式 - 如果需要有一種統一的類型來表示泛型類型的所有實例。

  要確保以等價的方式來實現根基類型成員和對應的泛型類型成員。

  考慮使用抽象基類來表示根基類型,而不是使用介面來表示根基類型。

  考慮用非泛型類型作為根基類型 - 如果這樣的類型已經存在。

  

9.9 Template Method 模式

  避免將公有成員定義為虛成員。

  考慮使用 Template Method 模式來更好地控制擴展性。

  考慮以非秀成員的名字加“Core”尾碼為名字,來命名為該費虛成員提供擴展點的受保護的虛成員。

  

9.10 超時

  要優先讓用戶通過參數來制定超時長度。

  要優先使用 TimeSpan 來表示超時長度。

  要在超時後拋出 System.TimeoutException 異常。

  不要通過返回錯誤碼的方式來告訴用戶發生了超時。

  

9.11 可供 XAML 使用的類型

  考慮提供預設構造函數 - 如果想讓類型能用於 XAML。

  要提供標記擴展 - 如果想讓 XAML 讀取程式能夠創建不可變的類型。。

  避免定義新的類型轉換器,除非這樣的轉換是自然而直觀的。一般來說,應該將類型轉換器的使用範圍限制在 .NET 框架中已經使用了類型轉換器的地方。

  考慮將 ContentPropertyAttribute 用於最常用的屬性,從而得到更方便的 XAML 語法。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Vista 和 Windows7 系統都很註重系統的安全性,在提高安全性的同時,也給我們某些應用帶來不便,例如需要安裝插件或證書,可能會彈出“由於無法驗證發行者,所以WINDOWS已經阻止此軟體”的相關提示,而致使無法正常使用該軟體,下麵分享解決此類提示的方法: 按下麵兩步操作,一般情況都可以解決此 ...
  • 為什麼降呢?就從看到朋友的win10是盜版說起。正好手頭上有辦法激活win7旗艦,還有個小心思,看著10特不舒服,估計又加了一些小白完全用不上的東西,想到就動手,以下是倒騰了幾天的小總結。 首先,win是不支持降級的很明顯,不管怎麼試都失敗,u盤安裝,硬碟安裝還是cd裝,統統無用,在磨夠了耐性以後, ...
  • 一、Oracle VirtualBox安裝Linux7.0全屏設置 1. 點擊菜單欄 設備 –> 分配光碟機 –> 選擇一個虛擬光碟,找到VirtualBox安裝目錄下的VBoxGuestAdditions.iso,載入此鏡像。2. 啟動Linux系統並用root身份登入系統3. 執行掛載命令,將虛擬 ...
  • 參考了兩種方法: 1. 解決fatal: unable to connect to github.com問題 http://blog.csdn.net/greenqingqingws/article/details/11808745原因:需要用https才能讀到數據解決方法:輸入命令git conf ...
  • 有話要說 為什麼要用Linux?要用Linux的原因太多,想說說不完啊。 如果你說用Linux只是為了裝逼,那證明你真的還很菜。不排除有裝逼了因素,那也只占非常少的比例,可以忽略不計。 我們反問一下,為什麼不用Linux呢?回答無非就是:沒有圖像界面、操作太複雜、和我以前的使用習慣不一樣、學習新的系 ...
  • 添加PPA: 1、首先進入ubuntu系統,system—>administration—>update manager—>setting,在軟體源界面,點擊other software 標簽頁,點擊左下角的add,將獲得的PPA地址粘貼進來。 2、也可以在終端輸入,sudo gedit /etc/ ...
  • 本節課程,需要完成擴增子分析解讀1質控 實驗設計 雙端序列合併 先看一下擴增子分析的整體流程,從下向上逐層分析 分析前準備 # 進入工作目錄 cd example_PE250 上一節回顧:我們拿到了雙端數據,進行了質控、並對實驗設計進行了填寫和檢查、最後將雙端數據合併為單個文件進行下游分析。 接下來 ...
  • 對值類型和引用類型的誤解 (引用類型存儲在堆上,值類型存儲在棧上) 對值類型和引用類型的誤解 (引用類型存儲在堆上,值類型存儲在棧上) 在學習C#基礎篇幅的時候總是逃不掉值類型和引用類型,很多新手包括我以前對它的理解也只是停留在"引用類型存儲在堆上,值類型存儲在棧上". 這個誤區主要歸咎於我們根本沒 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...