標籤:使用 java for sp on 代碼 ad bs ef
前言
這本書這幾年零零散散讀過兩三遍了,作為經典書籍,應該重複讀反覆讀,既然我現在開始寫博了,我也準備把以前覺得經典的好書重讀細讀一遍,並且將筆記整理到部落格中,好記性不如爛筆頭,同時也在寫的過程中也可以加深自己理解的深度,當然同時也和技術社區的朋友們共用
建構函式
- 抽象類別預設的建構函式可訪問為protected,普通類預設為public
- 衍生類別預設調用一個基類構造器(如果沒有無參構造器,則需要顯式調用)
- 不通過建構函式建立對象?
- 靜態方法Object.MemberwiseClone,分配記憶體複製位元組
- 運行時序列化
- 不要在構造器中調用會影響所構造對象的任何虛方法,如果虛方法重寫,導致無法預測的行為
- 這一點,在java中尤其明顯,因為java的內聯初始化是在基類初始化之後
- 基類建構函式虛方法指向子類實現,可是子類並沒有完全初始化完成,想象一下!
- 構造執行個體初始化順序
- 內聯初始化(這一點和java不一樣)
- 調用基類的構造器
- 調用構造器自己的代碼
- 實值型別的構造器
- 實值型別總是提供預設構造器,所以總是可以執行個體化
- 由於總是有預設構造器,實值型別不允許使用內聯初始化文法
- 開發人員無法定義實值型別的無參構造器,只能定義帶參構造器
- 基於棧的實值型別欄位都必須在讀取之前寫入(賦值),並且時全部欄位。否則編譯出錯
- 實值型別在構造器中可以給this賦值,而參考型別的this則時唯讀
- 類型構造器(靜態建構函式)
- static並且強制可訪問性為private
- 一個AppDomain中只會執行一次,並且安全執行緒,適合單例模式
- 實值型別中可以定義類型構造器,但是永遠不要這麼做,為什麼呢?
- 手動調用類型構造器 RuntimeHelpers.RunClassConstructor方法
- 構造器應該避免循環相依性
- 實值型別不允許使用執行個體欄位的內聯初始化,但是靜態欄位卻可以,DateTime就是典型!
- 靜態析構器Finalize不存在,只能通過AppDomain.DomainUnload事件進行登記回調
類型構造器的效能考量
- 時機的不確定性
- 訪問類的非繼承欄位或成員前 precise精確語義,時機精確
- 訪問靜態欄位或靜態執行個體\執行個體方法前或者執行個體構造器之前,before-field-init語義
- 欄位初始化前語義時機不確定,因為CLR保證訪問成員前運行靜態構造器
- 如果沒有顯式定義類型構造器,則靜態欄位使用beforefieldinit標記,否則不使用此標記
- beforefieldinit可以最佳化調用,但是precise不可以精確調用
- 靜態欄位只要在訪問之前初始化就可以了,時機無所謂。而顯式類型構造器可能包含具有副作用的代碼,所以需要精確拿捏啟動並執行時機
屬性
- 有參屬性和無參屬性,靜態、執行個體、抽象和虛屬性
- 自動屬性的缺點
- 屬性或索引器不可以作為out或ref參數傳遞
- DateTime.Now是一個錯誤的設計,為什嗎?
Tips
- 轉型運算式會促使產生代碼調用顯式轉換操作符方法。如果使用as或is,則永遠不會調用這些方法
- 引用參數
- 引用參數 out 無須初始化、方法內不能讀取參數值、必須寫入 ref 必須初始化、方法內可以讀取或寫入
- 引用傳值,可以避免複製位元組,提升執行效率
- 引用傳值ref和out可以應用於建構函式噢!
- 建議:參數盡量使用弱型別參數(比如介面或抽象類別),傳回值最好使用強型別
- 自動集合初始化器,編譯器針對對象調用Add方法,否則編譯出錯
- 代碼內聯
- 是指將一個方法的代碼直接編譯到調用它的方法當中
- 避免在運行時發出調用所產生的開銷
- JIT編譯器在調試代碼時不會內聯屬性方法,方便調試
- 發布版本中,經過內聯最佳化,可能比較快
讀書筆記—CLR via C#章節8-10