4.类型设计规范《.NET设计规范》
類是引用類型的一般情況,占了框架中的大多情況,類的流行歸于它支持面向?qū)ο蟮奶卣?#xff0c;以及它的普遍的適用性,基類和抽象類是兩個特殊的邏輯分組,它們與擴張性有關(guān)。
由于CLR不支持多繼承,接口類型可以用來模擬多繼承,既能被引用類型實現(xiàn),也能被值類型實現(xiàn)。
結(jié)構(gòu)是值類型的一般情況,應該用于小而簡單的類型,就像編程語言的基本類型一樣。
枚舉是值類型的一個特例,它用來定義一小組值。
靜態(tài)類是那些用來容納靜態(tài)成員的類型,常用來提供對其他操作的快速訪問。
委托、異常、Attribute、數(shù)據(jù)、集合都是引用類型的特例,各有各自的用途。
- √ 要 確保每個類型由一組定義明確、相互關(guān)聯(lián)的成員組成,而不要僅僅是一些無關(guān)功能的隨機集合。
4.1.類型和名字空間
在設計大型框架之前,應該決定如何將功能劃分到一組功能域中,這些功能域由名字空間表示,為了確保一組有條理的名字空間包含的類型能很好的集成,不發(fā)生沖突,以及不會重復,自頂向下的設計很有必要。導致了下面的規(guī)范:
- √ 要 用名字空間把類型組織成一個相關(guān)的特性域的層次結(jié)構(gòu)。
- × 避免 非常深的名字空間層次(難于瀏覽,需要經(jīng)常回溯)
- × 避免 有太多的名字空間
- × 避免 把為高級方案而設計的類型和常見的編程任務而設計的類型放在同一個名字空間中。
(方便用戶更容易理解框架的基本概念,而且更容易在常見的場景中使用框架) - × 不要 不指定類型的名字空間就定義類型。
標準子名字空間的命名
很少使用的類型應該放在子名字空間中,以免擾亂主名字空間,我們確定了幾組類型,應該把它們從主名字空間中區(qū)分離出來。
僅用于設計時的類型應該放在名為 .Design 的子名字空間。
如:System.Windows.Forms.Design;
System.Messaging.Design;
權(quán)限類型應該放在 .Permissions 子名字空間。
許多框架需要支持與舊系統(tǒng)的互操作性(interoperability)。
4.2 類和結(jié)構(gòu)之間的選擇
引用類型在堆上分配,由垃圾收集器管理;而值類型要么在棧上分配并在棧展開時釋放,要么內(nèi)聯(lián)在容納它的類型中并在容納它的類型被釋放時釋放。因此,與引用類型的分配與釋放相比,值類型的分配與釋放開銷更低。
引用類型的數(shù)組不是非內(nèi)聯(lián)分配的,意為數(shù)組元素只是一些引用,指向那些位于堆中的引用類型的實例。而值類型的分配是內(nèi)聯(lián)的,數(shù)組的元素就是值類型的真正實例。因此值類型的分配和釋放的開銷要比引用類型的大的多,在大多情況下,值類型數(shù)組具有更好的局部性。
值類型在被強制轉(zhuǎn)換為對象或裝箱。因為裝箱和對象是在堆上分配的,且由垃圾收集器管理,所以太多的裝拆箱操作會對堆、垃圾收集器,并對系統(tǒng)性能造成影響。相比之下,在對引用類型執(zhí)行轉(zhuǎn)換操作,不會發(fā)生裝箱操作。
引用類型的賦值是復制引用,而值類型的賦值復制整個值,對大的引用類型復制開銷要比值類型小的多。
引用類型是引用傳遞,值類型是值傳遞。改變引用類型的一個實例會影響其他的實例,改變值類型的實例,不會影響到它的副本。
框架中的大多數(shù)類型應該是類,但是在某些特殊情況下,由于值類型所具有的特征,使用結(jié)構(gòu)更合適。
- √ 考慮 定義結(jié)構(gòu)而不是類 —— 如果該類型的實例比較小,生命周期比較短,經(jīng)常被內(nèi)嵌在其他對象中。
× 避免 定義結(jié)構(gòu),除非該類型具有以下特征:
它在邏輯上代表一個獨立的值,與基本類型相似(int)
它的實例大小小于16字節(jié)
它是不可變的
它不需要被經(jīng)常裝箱
在所有的其他情況下,應該將類型定義為類。
4.3 類和接口之間的選擇
一般來說,類是用來暴露抽象的優(yōu)先選擇。
接口的缺點在于當需要允許API不斷演化時,它的靈活性不如類,一旦你發(fā)布了一個接口,它的成員就永遠固定了,給接口添加任何東西都會破壞已經(jīng)實現(xiàn)該接口的已有類型。
類提供了更多的靈活性,你可以給一個已發(fā)布的類添加成員。只要添加的方法不是抽象的,任何已有的派生類無需改變?nèi)阅芾^續(xù)使用。
√ 要 優(yōu)先采用類而不是接口
與基于接口的API相比,基于類的API容易演化得多,因為可以給類型添加成員而不會破壞已有的代碼。√ 要 用抽象類而不是接口來解除協(xié)定與實現(xiàn)之間的耦合。
抽象類經(jīng)過正確的設計,同樣能夠解除協(xié)定與實現(xiàn)之間的耦合,與接口能達到的程度不相上下。- √ 要 定義接口,如果需要提供一個多態(tài)的值類型層次結(jié)構(gòu)的話。
值類型不能自其它類型繼承,但是她們可以實現(xiàn)接口。
- √ 考慮 通過定義接口來達到與多重繼承相類似的效果。
4.4 抽象類的設計
- × 不要 在抽象類型中定義公有的或內(nèi)部受保護的構(gòu)造函數(shù)。
只有當用戶需要創(chuàng)建一個類型的實例時,該類型的構(gòu)造參數(shù)才是公有的,由于你無法創(chuàng)建一個抽象類的實例,因此如果抽象類型具有公有構(gòu)造函數(shù),那么這樣的設計不僅錯誤,而且會誤導用戶。
- √ 要 為抽象類定義受保護的構(gòu)造函數(shù)或內(nèi)部構(gòu)造函數(shù)。
更常見的情況是受保護的構(gòu)造函數(shù),唯一的目的是允許子類型被創(chuàng)建時,基類能夠做自己的初始化。
內(nèi)部構(gòu)造函數(shù)可以用來把該抽象類的具體實現(xiàn)限制在定義該抽象類的程序集中。
public abstract class Claim {internal Claim(){...} }- √ 要 為發(fā)布的抽象類提供至少一個繼承自該類的具體類型。
這有助于驗證該抽象類的設計是否正確。例如,System.IO.FileStream 是 System.IO.FileStream 抽象類的一個實現(xiàn)。
4.5 靜態(tài)類的設計
靜態(tài)類定義為一個只包含靜態(tài)成員的類。
如果一個類被定義為靜態(tài),那么它就是密封的、抽象的,不能覆蓋或者聲明任何實例成員。
靜態(tài)類是在純面向?qū)ο笤O計和簡單性之間的一個權(quán)衡,它們被廣泛用來提供一下訪問其他操作(比如System.IO.File)的快捷方式,存放擴展方法,或者以一種不完全面向?qū)ο蟮姆绞絹硖峁┮恍┕δ堋?#xff08;System.Enviroment)
√ 要 盡量少用靜態(tài)類
靜態(tài)類僅被用作輔助類,來支持框架的面向?qū)ο蟮暮诵摹?/p>× 不要 把靜態(tài)類當做雜物箱。
每一個靜態(tài)類都應該有其明確的目的。× 不要 聲明或覆蓋(override)靜態(tài)類中的實例成員。
√ 要 把靜態(tài)類定義為密封的、抽象的,并添加一個私有的實例構(gòu)造函數(shù)。
4.6 接口的設計
雖然大多數(shù)情況下API用類或結(jié)構(gòu)來構(gòu)建最好,但是在有些情況下,接口更合適。甚至某些情況接口是唯一的選擇。
CLR 不支持多繼承,但允許類型實現(xiàn)一個或多個接口,因此通常用接口來實現(xiàn)多繼承。
另一種適合定義接口的情況是,為多種類型(包括值類型)創(chuàng)建一個公共接口。雖然值類型無法繼承除了 System.ValueType 之外的其他類型,但他們可以實現(xiàn)接口,所以提供了一個公共的基類型,使用接口是唯一的選擇。
√ 要 定義接口,如果你需要包括值類型在內(nèi)的一組類型支持一些公共的API。
√ 考慮 定義接口,如果需要讓已經(jīng)繼承自其它類型的類型支持該接口提供的功能。
- × 避免 使用記號接口(沒有成員的接口)
√ 要 為接口提供至少一個實現(xiàn)該接口的類型。
√ 要 為你定義的每個接口提供至少一個使用該接口的API(一個以接口為參數(shù)的方法或是一個類型為該接口的屬性)
例如,List<T>.Sort 使用了 IComparer<T> 接口。× 不要 給已發(fā)行的接口再添加成員。
這樣做會破壞該接口的實現(xiàn),為了避免版本的問題,應該創(chuàng)建一個新的接口。
一般來說,在為托管代碼設計可重用的程序庫時,你應該選擇類而不是接口。
4.7 結(jié)構(gòu)的設計
通用目的的值類型通常稱為 struct(結(jié)構(gòu))。
× 不要 為結(jié)構(gòu)提供默認的構(gòu)造函數(shù)。(C#不允許結(jié)構(gòu)有默認的構(gòu)造函數(shù))
× 不要 定義可變的值類型。
√ 要 確保所有的實例數(shù)據(jù)都為0,false,或null時,結(jié)構(gòu)仍處于有效狀態(tài)。(可以防止在創(chuàng)建一個結(jié)構(gòu)時創(chuàng)建出無效的實例)
√ 要 為值類型實現(xiàn) IEquatable<T>。
值類型的 Object.Equals 方法會導致裝箱,默認的實現(xiàn)并不高效,因為使用了反射,IEquatable<T>.Equals 性能好的多,不會導致裝箱。× 不要 顯示的擴展 System.ValueType,實施上大多數(shù)編程語言步允許這么做。
4.8 枚舉的設計
枚舉是一種特殊的值類型,有兩種類型的枚舉:簡單枚舉 和 標記枚舉(flag enum)。
簡單枚舉 代表小型的、閉合的一組選擇。例如(一組顏色):
Public enumColor{ Red, Green, Blue, …… }標記枚舉 的設計是為了支持對枚舉值進行按位操作。標記枚舉的常見例子是一個選擇列表,
[Flags] Public enumAttributeTargets {Assembly=0x0001,Module=0x0002,Cass=0x0004,Struct=0x0008 }√ 要 用枚舉來加強那些表示值的集合的參數(shù)、屬性以及返回值的類型性。
√ 要 優(yōu)先使用枚舉而不要使用靜態(tài)常量。(枚舉是一個包含一組靜態(tài)常量的結(jié)構(gòu))
× 不要 把枚舉用于開放的集合(比如操作系統(tǒng)版本、朋友的名字等)
× 不要 提供為了今后使用而保留的枚舉值。
× 避免 顯示的暴露只有一個值的枚舉。
× 不要 把 sentinel 值包含在枚舉值中。
- √ 要 為簡單枚舉類型提供零值。(應該考慮把該值稱為 None 之類的東西,如果這樣的值不適合用于某個特定的枚舉,那么應該把該枚舉中最常用的默認值賦值為0)
√ 考慮 以 Int32 作為枚舉的基本實現(xiàn)類型。
√ 要 用復數(shù)名詞或者名詞短語來命名標記枚舉,用單數(shù)名詞或者名詞短語來命名簡單枚舉。
× 不要 直接擴充 System.Enum。
System.Enum 是一個特殊的類型,被 CLR 用來創(chuàng)建用戶定義的枚舉。
4.8.1 標記枚舉的設計
- √ 要 對標記枚舉使用 System.FlagsAttribute,不要把該 attribute 用于簡單枚舉。
- √ 要 用2的冪次方作為標記枚舉的值,這樣就可以通過按位或操作自由組合他們。
- √ 考慮 為常用的標記組合提供特殊的枚舉值。
位操作是一個高級概念,對應簡單任務來說不是必須的,FileAccess.ReadWrite 就是這樣一個例子
× 避免 讓創(chuàng)建的標記枚舉包含某些無效的組合。
× 避免 把0用作標記枚舉的值,除非該值表示“所有標記都被清除“,而且按下一條規(guī)范進行了適當?shù)拿?/p>
C#中字面常量0可以隱式地轉(zhuǎn)換為任何枚舉類型,因此你可以編寫這樣的代碼:
if (Foo.SomeFlag == 0) ...CLR規(guī)定任何值類型的默認值“所有的位都清零“。
- √ 要 把標記枚舉的零值命名為 None,對其標枚舉來說,該值必須始終意味著“所有標記均被清除”。
但是,該規(guī)則只適用于標記枚舉,對于非標記枚舉的情況,避免使用0值實際上是不利的,所有的枚舉類型一開始都為零值。
4.8.2 給枚舉添加值
常會發(fā)現(xiàn)在需要在程序發(fā)行之后需要給一個枚舉添加值。如果新添加的值是一個已有API的返回值,那么就存在潛在的應用程序兼容性問題。
- √ 考慮 給枚舉添加值,盡管有那么一點兼容性的風險。
如果有實際數(shù)據(jù),表明給枚舉添加值會導致應用程序的不兼容,可以考慮添加一個新的API來返回新老枚舉值,這樣就能確保仍然兼容現(xiàn)有的應用程序。
4.9 嵌套類型
嵌套類型是一個定義在另一個類型的作用域內(nèi)的類型。另一個類型被稱為外層類型。嵌套類型能夠訪問外層類型的所有成員。可以訪問定義在外層類型的私有字段以及定義在外層類型的所有父類的受保護字段。
一般來說,盡量少用嵌套類型,嵌套類型與外層類型緊密耦合,不適合將它們作為通用類型。嵌套類型適合用來對它們的外層類型的實現(xiàn)細節(jié)建模。
√ 要 在想讓一個類型能夠訪問外層類型的成員時才使用嵌套類型。
× 不要 用嵌套類型進行邏輯分組,應該用名字空間來達到此目的。
× 避免 公開的暴露嵌套類型,唯一的例外是如果只需要在極少數(shù)的場景中聲明嵌套類型的變量,比如派生子類,或者其他高級自定義場景中。
一般避免使用嵌套類型,只有在開發(fā)人員幾乎不需要聲明該類型的變量時才使用嵌套類型。(例如集合的枚舉器)× 不要 使用嵌套類型,如果該類型可能會被除了它的外層類型之外的類型引用。
× 不要 使用嵌套類型,如果它們需要被客戶代碼實例化。
× 不要 把嵌套類型定義為接口的成員。
一般來說盡量少用嵌套類型,而且應該避免將嵌套類型公開暴露給外界。
轉(zhuǎn)載于:https://www.cnblogs.com/tangge/p/6851477.html
總結(jié)
以上是生活随笔為你收集整理的4.类型设计规范《.NET设计规范》的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA WEB之Spring4.x J
- 下一篇: ZOJ 3827 Information