Microsoft 基本設計方針規則程式碼分析規則集
您可以使用「Microsoft 基本設計方針規則」規則集,將重點放在讓程式碼更易懂又好用。 如果專案包含程式庫程式碼或您想強制執行最佳做法以便於維護程式碼,則應包含這個規則集。
「基本設計方針規則」包含「Microsoft 最小建議規則」規則集中的所有規則。 如需最小規則的清單,請參閱 Microsoft 最小建議規則:程式碼分析規則集。
下表說明「Microsoft 基本設計方針規則」規則集中的所有規則。
規則 |
說明 |
---|---|
呼叫泛型型別的靜態成員時,必須為型別指定型別引數。 呼叫不支援介面的泛型執行個體 (Instance) 成員時,必須為成員指定型別引數。 在上述兩種情況下,指定型別引數的語法不同且容易混淆。 |
|
System.Collections.Generic.List<(Of <(T>)>) 是專為效能而非繼承所設計的泛型集合。 因此,List 不包含任何虛擬成員。 應該改為公開專為繼承所設計的泛型集合。 |
|
型別包含會傳回 void 的委派,其簽章包含兩個參數 (第一個參數是物件,第二個參數則是可指派給 EventArgs 的型別),而包含組件則會以 .NET Framework 2.0為目標。 |
|
推斷是指如何利用傳遞到泛型方法的引數型別,而不是利用型別引數的明確規格,來決定泛型方法的型別引數。 若要啟用推斷,泛型方法的參數簽章必須包含與方法之型別參數具有相同型別的參數。 在上述情形中,不必指定型別引數。 使用所有型別參數的推斷時,呼叫泛型和非泛型執行個體方法之語法是相同的;這簡化泛型方法的可用性。 |
|
泛型型別所包含的型別參數越多,就越難了解並記住每個型別參數所代表的含意。 若是只包含一種型別參數 (如 List<T>),以及包含兩個型別參數的某些特定情況 (如 Dictionary<TKey, TValue>),這些通常都清楚易懂。 不過,如果包含了兩個以上的型別參數,則對大多數使用者而言都會變得難以理解。 |
|
巢狀型別引數就是也是泛型型別的型別引數。 若要呼叫其簽章含有巢狀型別引數的成員,則使用者必須具現化 (Instantiate) 一個泛型型別,並將這個型別傳遞給第二個泛型型別的建構函式。 必要程序及語法十分複雜,且應予以避免。 |
|
外部可見的方法包含 System.Object 型別的參考參數。 使用泛型方法可讓所有型別 (遵守條件約束) 傳遞給方法,而不需要先將型別轉型為參考參數型別。 |
|
如同其他實值型別一般,未初始化的列舉其預設值為零。 非旗標屬性的列舉型別應該要定義含有零值的成員,使列舉型別可以指定一個有效的預設值。 如果已套用 FlagsAttribute 屬性的列舉定義零值成員,則其名稱應該是 "None",以表示列舉中未設定任何值。 |
|
若要放寬集合的可用性,請實作其中一個泛型集合介面。 接著,使用該集合填入泛型集合型別。 |
|
當方法宣告將基底型別指定為參數,則從此基底型別衍生的任何型別都可以當做對應引數傳遞給方法。 如果不需要以衍生參數型別提供額外的功能,則可以使用基底型別讓方法獲得更廣泛的使用。 |
|
只有衍生型別 (Derived Type) 可以呼叫抽象型別上的建構函式。 因為公用建構函式會建立型別的執行個體,而且您無法建立抽象型別的執行個體,因此具有公用建構函式的抽象型別設計不正確。 |
|
公用或保護的型別會實作加法或減法運算,但不會實作等號比較運算子。 |
|
Common Language Specification (CLS) 會定義命名限制、資料型別及組件必須遵守的規則 (如果組件會使用於跨程式設計語言時)。 良好的設計會要求所有組件使用 CLSCompliantAttribute 明確表示 CLS 符合性。 如果這個屬性未出現於組件中,則表示組件不符合標準。 |
|
ComVisibleAttribute 會判斷 COM 用戶端如何存取 Managed 程式碼。 良好的設計會要求組件明確表示 COM 的可視性。 COM 的可視性可以針對整個組件進行設定,然後針對個別型別及型別成員進行覆寫。 如果這個屬性不存在,則 COM 用戶端可以看到組件的內容。 |
|
定義自訂屬性時,請使用 AttributeUsageAttribute 標示它,以指出可以在原始程式碼的哪個位置套用自訂屬性。 屬性的意義和預期的用法將決定它在程式碼中的有效位置。 |
|
屬性可以定義必須在將屬性套用至目標時指定的強制引數。 這些引數也稱為位置引數,因為它們會當做位置參數提供給屬性建構函式。 對於每個強制引數而言,屬性 (Attribute) 還須提供對應的唯讀屬性 (Property),才可以在執行時期擷取引數值。 屬性也可以定義選擇性引數,也稱為具名引數。 這些引數會依照名稱提供給屬性 (Attribute) 建構函式,且必須有對應的讀取/寫入屬性 (Property)。 |
|
索引子 (也就是索引屬性) 應使用單一索引。 多維索引子會大幅降低程式庫的可用性。 |
|
公用或保護的方法具有以 "Get" 開頭的名稱,該名稱不採用任何參數並且會傳回不是陣列的值。 此方法可能是成為屬性的不錯候選者。 |
|
當引數的正確數目未知,而且變數引數都是相同的型別 (或可以相同的型別傳遞) 時,需使用參數陣列而不是重複的引數。 |
|
允許使用預設參數的方法受制於 Common Language Specification (CLS)。不過,CLS 允許編譯器 (Compiler) 忽略已指派給這些參數的值。 若要在程式語言之間維護您要的行為,則必須以提供預設參數的方法多載取代使用預設參數的方法。 |
|
列舉型別是一種實值型別 (Value Type),用以定義一組相關的具名常數。 當列舉的具名常數可以有意義地加以結合時,會將 FlagsAttribute 套用至此列舉。 |
|
列舉型別是一種實值型別 (Value Type),用以定義一組相關的具名常數。 根據預設,System.Int32 資料型別會用於儲存常數值。 雖然您可以變更這個基礎型別,但是大多數案例中仍不需要或不建議進行變更。 |
|
此規則會偵測具有事件常用名稱的方法。 如果方法因回應清楚定義的狀態變更而被呼叫,應該由事件處理常式叫用該方法。 呼叫方法的物件應該要引發事件,而不是直接呼叫方法。 |
|
不應該攔截一般例外狀況。 請攔截較明確的例外狀況,或重新擲回一般例外狀況當做 catch 區塊中的最後一個陳述式。 |
|
無法提供整組的建構函式會導致難以正確地處理例外狀況。 |
|
巢狀型別是在其他型別範圍內宣告的型別。 巢狀型別在封裝包含型別 (Containing Type) 私用的 (Private) 實作細節時相當有用。 因為有這樣的用途,所以巢狀型別不應為外部可見的。 |
|
這項規則要求 ICollection 實作提供強型別成員,讓使用者在使用介面所提供的功能時,不需將引數轉換為 Object 型別。 這項規則假設實作 ICollection 的型別會這樣做,以管理效力比 Object 還強之型別的執行個體集合。 |
|
公用或受保護的型別實作 System.IComparable 介面。 它不會覆寫 Object.Equals,也不會多載等號、不等、小於或大於的語言特定比較運算子。 |
|
此規則要求 IEnumerator 實作同時也提供 Current 屬性的強型別版本,如此當使用者使用介面提供的功能時,就不需要將傳回值轉型為強型別。 |
|
這項規則要求 IList 實作提供強型別成員,讓使用者在使用介面所提供的功能時,不需將引數轉換為 System.Object 型別。 |
|
型別或成員是以並未指定其 ObsoleteAttribute.Message 屬性 (Property) 的 System.ObsoleteAttribute 屬性 (Attribute) 標記。 編譯以 ObsoleteAttribute 標記的型別或成員後,會顯示屬性 (Attribute) 的 Message 屬性 (Property),以提供使用者有關過時型別或成員的相關資訊。 |
|
索引子 (也就是索引的屬性) 應該使用整數型別或字串型別的索引。 這些型別通常會用於資料結構的索引,可以提升程式庫的可用性。 Object 型別的使用應限制於無法在設計階段指定特定整數型別或字串型別的情況。 |
|
當唯讀屬性是可接受並經常需要時,設計方針就會禁止使用唯寫屬性,因為允許使用者設定值然後不讓使用者檢視該值,無法提供任何安全性的保護。 同時,如果沒有讀取權限,則無法檢視共用物件的狀態,進而限制這些物件的使用性。 |
|
對參考型別而言,等號比較運算子的預設實作 (Implementation) 永遠都是正確的。 根據預設,只有當兩項參考都指向相同物件時才會相等。 |
|
型別會宣告 protected 成員,如此繼承的型別即可存取或覆寫成員。 根據預設,密封型別無法被繼承,這表示無法呼叫密封型別上的受保護方法。 |
|
型別會將方法宣告為 virtual,讓繼承型別可以覆寫 virtual 方法的實作。 根據預設,密封型別無法被繼承。 這會讓密封型別上的虛擬方法失去意義。 |
|
型別會在命名空間之內宣告以防止名稱衝突,而且可當做組織物件階層架構中相關型別的一種方法。 |
|
欄位的主要用法應該是當做實作詳細資料。 欄位應該是私用或內部的,而且應使用屬性公開。 |
|
公用或受保護的型別只包含靜態成員,因此不會利用 sealed (C# 參考) (NotInheritable) 修飾詞宣告它們。 預定不會繼承的型別應該以 sealed 修飾詞標記,以禁止使用它做為基底型別。 |
|
公用或巢狀公用型別只宣告靜態成員,而且具有公用或保護的預設建構函式。 建構函式不是必要的,因為呼叫靜態成員不需型別的執行個體。 為了安全,字串多載應該使用字串引數呼叫 URI 多載。 |
|
如果方法使用 URI 字串表示,應該提供採用 URI 類別的對應多載,這樣就可以透過安全的方式提供這些服務。 |
|
此規則假設方法會傳回統一資源識別元 (URI)。 URI 的字串表示方式容易發生剖析和編碼錯誤,並且可能因此產生安全性弱點。 System.Uri 類別以安全的方式提供這些服務。 |
|
此規則假設屬性代表統一資源識別元 (URI)。 URI 的字串表示方式容易發生剖析和編碼錯誤,並且可能因此產生安全性弱點。 System.Uri 類別以安全的方式提供這些服務。 |
|
型別會宣告方法多載,這些方法多載的差別只在於以 System.Uri 參數取代字串參數。 接受字串參數的多載不會呼叫接受 URI 參數的多載。 |
|
外部可見的型別會延伸某些基底型別 (Base Type)。 請使用其他作法。 |
|
具象型別就是具有完整實作 (Implementation) 且因此能加以具現化 (Instantiated) 的型別。 若要讓成員能廣泛使用,請使用建議的介面取代具象型別。 |
|
內部例外狀況只會在自己的內部範圍內顯示。 當例外狀況超出內部範圍後,只能使用基本例外狀況來攔截例外狀況。 如果內部例外狀況是繼承自 T:System.Exception、T:System.SystemException 或 T:System.ApplicationException,外部程式碼就沒有足夠的資訊可以知道應該如何處理此例外狀況。 |
|
執行個體方法宣告參數或區域變數,而其名稱符合宣告型別的執行個體欄位,因此導致錯誤。 |
|
這個規則會測量整個方法中線性獨立路徑的數目,此數目是由條件分支的數目與複雜度決定。 |
|
因為以 Common Language Runtime 為目標的語言不需要區分大小寫,因此,命名空間、型別、成員和參數的識別項不能只有大小寫的不同。 |
|
命名空間 (Namespace) 名稱或型別名稱符合程式語言中的保留關鍵字。 命名空間和型別的識別項不應該符合語言所定義的關鍵字,而這些語言的目標為 Common Language Runtime。 |
|
方法簽章包括不用於方法主體中的參數; |
|
未使用的區域變數和不必要的設定,會增加組件的大小並降低效能。 |
|
常見的效能最佳化作法是在處理器暫存器中儲存值,而非記憶體,這稱為「註冊 (Enregistering) 值」。 若要增加所有區域變數都能註冊的機率,請將區域變數的數目限制為 64。 |
|
當型別宣告明確的靜態建構函式時,Just-In-Time (JIT) 編譯器會將檢查加入至型別的每個靜態方法和執行個體建構函式,確保之前已呼叫該靜態建構函式。 靜態建構函式檢查會降低效能。 |
|
private 或 internal (組件層級) 成員在組件中沒有呼叫端,不會由 Common Language Runtime 叫用,而且委派也未叫用該成員。 |
|
組件層級型別的執行個體不是由組件中的程式碼所建立。 |
|
.NET Framework Class Library 會提供擷取自訂屬性的方法。 根據預設,這些方法會搜尋屬性繼承階層架構。 密封屬性會減少對整個繼承階層架構的搜尋,並且可以改進效能。 |
|
不規則陣列是一種陣列,其元素也是陣列。 組成元素的陣列大小可以不相同,對於某些資料集而言較不會浪費空間。 |
|
對於實值型別而言,Equals 的繼承實作會使用 Reflection 程式庫,並比較所有欄位的內容。 但是 Reflection 相當耗費運算資源,而且可能不需要比較每個欄位是否相等。 如果希望使用者比較或排序執行個體,或是使用執行個體做為雜湊資料表索引鍵,則您的實值型別應實作 Equals。 |
|
即使屬性是唯讀,所傳回的陣列不會是寫入保護。 若要保持陣列為防止遭他人修改,屬性必須傳回陣列複本。 一般而言,使用者不了解呼叫這類屬性所造成的不良效能影響。 |
|
使用 String.Length 屬性或 String.IsNullOrEmpty 方法比較字串,明顯地會比使用 Equals 還快。 |
|
請盡可能避免使用完成項,因為追蹤物件存留期 (Lifetime) 時將會產生額外的效能負荷。 空白完成項只會增加額外負荷,而沒有任何好處。 |
|
不會存取執行個體資料或不會呼叫執行個體方法的成員,可以標記為 static (在 VB 中為 Shared)。 將方法標記為 static 之後,編譯器將對這些成員發出非虛擬呼叫位置。 這麼做可以讓重視效能的程式碼獲得可觀的效能。 |
|
偵測到似乎不能在組件內存取的私用欄位。 |
|
這將使原始錯誤變得難以偵測及偵錯。 |
|
已定義平台叫用方法,而且 .NET Framework Class Library 中有具同等功能的方法存在。 |
|
對例外狀況型別為 (或衍生自) ArgumentException 的預設 (無參數) 建構函式進行呼叫,或將錯誤的字串引數傳遞至例外狀況型別為 (或衍生自) ArgumentException 的參數化建構函式。 |
|
既非常數,亦非唯讀的靜態欄位不是安全執行緒。 必須小心控制對這類欄位的存取,而且需要進階的程式設計技巧同步 (Synchronize) 對類別物件的存取。 |
|
從外部可見的列舉會以 FlagsAttribute 標記,並且有一個或多個不是二的次方,或組合列舉上其他定義值之次方的值。 |
|
在 finally 或 fault 子句中引發例外狀況時,新的例外狀況會隱藏作用中的例外狀況。 在 filter 子句中引發例外狀況時,執行階段會以無訊息模式攔截例外狀況。 這將使原始錯誤變得難以偵測及偵錯。 |
|
完成項必須使用系列存取修飾詞 (Modifier)。 |
|
您不得變更繼承成員的存取修飾詞 (Modifier)。 將繼承成員變更為私用不會防止呼叫端存取方法的基底類別 (Base Class) 實作。 |
|
雖然 Common Language Runtime 允許使用傳回型別區分其他部分都相同的成員,但這個功能不屬於 Common Language Specification,也不是 .NET 程式語言共通的功能。 |
|
public 型別會實作等號比較運算子,但不會覆寫 Object.Equals。 |
|
偵測到運算子多載,且找不到預期的具名替代方法。 具名的替代成員會提供與運算子相同的功能存取,並且可供以不支援多載運算子 (Overloaded Operator) 的語言設計程式的開發人員使用。 |
|
型別實作等號比較運算子或不等比較運算子,但未實作相反的運算子。 |
|
可寫入的集合屬性允許使用者以不同的集合來取代該集合。 唯讀屬性會從取代過程中停止集合,但是仍然允許設定個別成員。 |
|
公用或保護的型別包含使用 VarArgs 呼叫慣例之公用或保護的方法,而不是 params 關鍵字。 |
|
實值型別會覆寫 Object.Equals,但不會實作等號比較運算子。 |
|
呼叫字串參數名稱包含 "uri"、"URI"、"urn"、"URN"、"url" 或 "URL", 而且此方法的宣告型別會包含具有 System.Uri 參數的對應方法多載。 |
|
型別具有以 System.Runtime.Serialization.OptionalFieldAttribute 屬性標示的欄位,而且型別不提供還原序列化事件處理方法。 |