C 讀書筆記 Effective C

2022-02-12 13:52:26 字數 3433 閱讀 4477

我看的書是《effective c#中文版——改善c#程式的50種方法》,bill wagner著,李建忠譯。書比較老了,04年寫的,主要針對c#1.0,但我相信其中的觀點現在仍有價值。(平心而論,和effective c++有差距,畢竟該書成書時對c#的研究不過幾年。)

下面是對這本書條款內容的一些歸納和個人理解,由於我比較熟悉c++,因此也會有也一些c++的對比。

條款1:使用屬性代替可訪問的資料成員

1. 屬性具有資料成員的訪問語法,這是最易於使用的語法。

2. 屬性事實上是方法,因而支援多型,且利於日後進行擴充套件,如多執行緒同步訪問等。

3. .net中的庫功能,很多是針對屬性的,例如資料繫結。

4. 兩者效能相當。

條款2:執行是常量(readonly)優於編譯時常量(const)

1. 兩者生成的il碼不同,後者是進行常量替換,而前者具有更好的二進位制相容性(程式集b依賴於程式集a的乙個常量,如果a中的這個常量修改了,const宣告要求ab都重行編譯,而readonly只要求a重編)。

2. 兩者效能相當。

條款3:操作符is或as優於強制轉型

1. as比強制轉型具有更簡練的語法(等價於as的強制轉型,需要包括異常捕獲等處理)。

2. 對於valuetype的派生類,由於不能為null,所以不能使用as,應該選擇is。

3. 強制轉型會考慮使用者定義型別轉換(explicit;但注意,使用者自定義轉型是靜態的,不支援多型),而as只取決於繼承鏈。

條款4:使用conditional特性代替#if條件編譯

1. 前者能讓程式邏輯更清晰,儘管它的最小應用單元是方法而後者是語句。

2. 前者有更好的效能。conditional是讓客戶的呼叫**消失,而對應功能的條件編譯只是讓語句消失,函式呼叫開銷依舊。

條款5:總是提供tostring方法

1. 人最容易理解的是字串,而object.tostring的預設行為是返回型別名,正確的實現tostring有利於除錯和ui等。

2. console.writeline和string.format等都支援iformattable介面,注意iformattable.tostring和object.tostring的相容。

條款6:明辨值型別和引用型別的使用場合

1. 需要打包一組資料,且記憶體占用不多的時候考慮使用struct。

2. 具有資料和邏輯,或者雖然邏輯簡單但資料塊佔記憶體很大,考慮使用class。

條款7:將值型別盡可能實現為具有常量性和原子性的型別

1. 常量值型別,更容易編寫原子邏輯。如,包含姓名和身份證號兩個欄位的struct,其兩個欄位是繫結的,單獨修改一般會帶來錯誤,所以與其在執行時用方法邏輯來維持兩者匹配,不如乾脆將兩個欄位都宣告為readonly,如果想修改就用新值來構造新物件。

2. 針對常量物件更容易編寫多執行緒邏輯。

條款8:確保0為值型別的有效狀態

1. 因為clr預設將物件初始化為二進位制0,所以應該保證二進位制0在程式邏輯中是合法值。如列舉值應該從0開始定義。

條款9:理解幾個相等判斷之間的關係

1. 對於valuetype:(對應struct)

public override bool equals(object obj);

它的實現主要是通過反射來對比各個字段,因此這個預設實現效率很低。valuetype的預設實現中,並不能直接將兩個二進位制塊進行memcmp,因為形如struct a這樣的結構,二進位制層次上的對比是沒有意義的。事實上,c#編譯器也沒有提供自動生成t.equals的服務(即對於使用者沒有提供equals實現的struct,編譯器何不自動生成逐字段對比的c#**?),原因不明。

所以,如果特定struct效能攸關,應該手工實現equals進行逐字段比較以獲得更佳效能。另外考慮實現語法糖operator==來呼叫equals。

2. 對於object: (對應class)

public static bool referenceequals(object obja, object objb);

public static bool equals(object obja, object objb);

public virtual bool equals(object obj);

public static bool operator == (object obja, object objb);

object基類中的預設實現全是引用比較,即用於判斷是否是同一物件。其中referenceequals提供最底層實現,operator ==呼叫referenceequals, static equals進行物件非空驗證然後呼叫virtual equals, 而virtual equals預設也是呼叫referenceequals。

如果需要給引用型別提供其他比較語義,如string,則實現virtual equals,然後過載operator ==呼叫virtual equals。

條款10:理解gethashcode方法的缺陷。

1. 實現gethashcode的要求(3點):

正確性要求——

(1)相等的物件必須有相同的hash code,即equals返回true的物件,gethashcode返回值也應該相同。值相等的物件當然應該在同乙個hash捅中。實現方法:用於生成hash code的字段,一定都要參與equals的實現。

(2)物件生命期中,gethashcode返回值應該不變。避免在hash表中查詢已經插入的物件卻找不到。實現方法:用於生成hash code的字段,最好宣告為readonly。

效能要求——

(1)gethashcode盡量返回均勻分布的值。

2. object的預設實現是返回自增的全域性物件id,valuetype的預設實現是返回第乙個欄位的gethashcode。本條的兩點恐怕只適用於c#1.0,我測試在c#3.5中,實現已經變化。

條款11:優先採用foreach迴圈語句

1. foreach會自動針對不同的容器,生成不同的il碼以優化效率。例如對陣列,foreach不會通過ienumerable遍歷,而是直接使用下標。

2. foreach可以正確遍歷起始下標非0的陣列和多維陣列。下標非0陣列是通過array.createinstance建立的。

3. foreach遍歷陣列,因為可以保證訪問陣列的每個元素的時候不越界,故foreach對應的下標訪問實現不會有下標越界檢查的開銷。在我使用的c#3.5中測試,foreach並沒有加速效果,恐怕因為在高版本中,下標越界檢查已經移到了clr的實現中(il的ldelem),故foreach並不比for迴圈快。

條款12:變數初始化器優於賦值語句

1. 初始化順序:(c#3.5,參見step欄位)

1class

initorder28

}9...10

initorder a

=new

initorder ;

2. 物件的建構函式可以有多個,比起在多個建構函式中分別初始化字段,字段初始化器更容易維護。

條款13:使用靜態構造器初始化靜態類成員

《effective C 》讀書筆記

1,c 關鍵字explicit c 中,乙個引數的 建構函式 或者除了第乙個引數外其餘引數都有預設值的多參建構函式 承擔了兩個角色。1 是個 構造器,2 是個預設且隱含的型別轉換操作符 所以,有時候在我們寫下如 aaa 這樣的 且恰好 的型別正好是aaa單引數構造器的引數型別,這時候 編譯器就自動呼...

Effective C 讀書筆記

一 讓自己習慣c 1 條款01 視c 為聯邦語言 c 的組成可分為四部分 1.c c 仍然以c語言為基礎。區塊 語句 預處理 內建資料型別 陣列 指標等都來自c。2.object oriented c c with classes所訴說的 classes 包括構造和析構 封裝 繼承 多型 virtu...

讀書筆記 Effective C

部分條款過於深奧,部分條款已了然於心,僅記錄當下所識所學 對於常量巨集定義,最好用const代替 define 對於函式巨集定義,最好用inline代替 define include ifdef ifndef仍被需要 內建物件記得手動初始化 使用成員初始列替換賦值操作 以local static替換...