《框架設計 CLR Via C# (第2版)》 - 學習筆記

《框架設計 CLR Via C#》 (第2版)

========== ========== ==========
[做者] (美) Jeffrey Richter
[譯者] (中) 周靖 張傑良
[出版] 清華大學出版社
[版次] 2006年11月 第1版
[印次] 2007年02月 第2次 印刷
[訂價] 68.00元
========== ========== ==========

【前言】

Microsoft .NET Framework 的目標不是爲構建一種特定類型的應用程序的開發人員提供一個抽象技術。相反,它的目標是爲平臺或者 Microsoft Windows 操做系統自己提供一個抽象技術。

.NET Framework 爲全部類型的應用程序提高了抽象等級。這意味着開發人員只需學習和掌握一個編程模型和一套 API (應用程序編程接口) ,無論開發人員是用它們來構建控制檯應用程序、圖形應用程序、網站,仍是構建由其餘應用程序使用的組件。

【第01章】

(P011)

一個方法只有在首次調用時纔會形成必定的性能損失。之後對該方法的全部調用都以本地代碼的形式全速運行,由於不須要再次執行驗證和編譯成本地代碼。

【第02章】

(P039)

程序集是進行重用、版本控制和安全保護的一個基本單元。它容許咱們將類型和資源文件劃分到單獨的文件中。

(P047)

在 Visual Studio 中新建一個 C# 項目時,會自動建立一個 AssemblyInfo.cs 文件。

(P051)

配置文件包含的是 XML 代碼,能夠和一個應用程序關聯到一塊兒,或者和機器關聯到一塊兒。因爲使用的不是註冊表設置,而是一個單獨的文件,因此文件可以方便地進行備份,管理員也能將應用程序方便地複製到另外一臺機器 —— 只需複製必要的文件,就能順便複製管理策略。

(P052)

對於可執行應用程序 (EXE) 來講,配置文件必須在應用程序的基目錄中,並且必須採用 EXE 文件的全名做爲文件名,再附加一個 .config 擴展名。

對於 Microsoft ASP.NET Web 窗體應用程序,文件必須在 Web 應用程序的虛構根目錄中,並且老是命名爲 Web.config 。除此以外,子目錄也能夠包含它們本身的 Web.config 文件,並且配置設置會得以繼承。

【第03章】

(P062)

不能將一個弱命名的程序集放到 GAC 中。

建議編程人員儘可能避免全局部署,儘可能使用私有部署。

【第04章】

(P083)

C# 不須要任何特殊語法便可將一個對象強制轉換成它的任何基類型,由於向基類型的轉換被認爲是一種安全的隱式轉換。

C# 要求開發人員將一個對象顯式轉換成它的任何派生類型,由於這樣的轉型可能在運行時失敗。

(P084)

類型假裝是形成許多安全漏洞的根源,並會破壞應用程序的穩定性和可靠性。所以,類型安全性是 CLR 的一個極其重要的目標。

在 C# 語言中進行強制類型轉換的另外一種方式是使用 is 操做符。 is 操做符檢查一個對象是否兼容於指定的類型,並返回一個 Boolean 值 : true 或 false 。注意 is 操做符永遠不會拋出異常。

(P085)

假如對象引用爲 null ,那麼 is 操做符老是返回 false ,由於無可用的對象來檢查其類型。

as 操做符的工做方式與強制類型轉換同樣,只是它永遠不會拋出一個異常 —— 相反,若是對象不能轉型,結果就是 null 。因此,正確的作法是檢查最終生成的引用是否爲 null 。若是企圖直接使用最終生成的引用,會形成一個 System.NullReferenceException 異常。

(P089)

事實上, .NET Framework 甚至根本沒有發佈一個 System.IO.dll 程序集。

【第05章】

(P098)

編譯器直接支持的任何數據類型都稱爲基元類型 (primitive type) 。基元類型直接映射到 Framework 類庫 (FCL) 中存在的類型。

(P101)

C# 老是對結果進行截斷處理,而不進行舍入。

(P104)

值類型的實例一般是在一個線程的堆棧上分配的 (雖然它們也能夠嵌入一個引用類型對象中) 。

在表明值類型實例的一個變量中,並不包含一個指向實例的指針。相反,變量中包含了實例自己的字段。

因爲變量已經包含實例的字段,因此在對實例的字段進行處理時,再也不須要提領一個指針。

值類型的實例不受垃圾收集器的制約。所以,它們的使用緩解了託管堆上的壓力,並減小了一個應用程序在其生存期內須要進行的垃圾收集次數。

全部值類型都是密封 (sealed) 類型,目的是防止將一個值類型用做其餘任何引用類型或值類型的基類型。

(P105)

在 C# 中,使用 struct 聲明的類型是值類型,使用 class 聲明的類型是引用類型。

(P106)

值類型對象有兩種表示形式 : 未裝箱 (unboxed) 形式和已裝箱 (boxed) 形式,而引用類型老是處於已裝箱形式。

引用類型的變量包含堆中的對象的地址,默認狀況下,在建立一個引用類型的變量時,它被初始化爲 null ,代表引用類型變量當前並不指向一個有效的對象。

試圖使用一個 null 引用類型變量,會拋出一個 NullReferenceException 異常。

相反,值類型的變量老是包含其基礎類型的一個值,並且值類型的全部成員都初始化爲 0 。因爲值類型變量不是指針,因此在訪問一個值類型時,不可能拋出一個 NullReferenceException 異常。

(P107)

將一個值類型的變量賦給另外一個值類型變量時,會執行一次逐字段的複製。將引用類型的變量賦給另外一個引用類型的變量時,只複製內存地址。

值類型的變量是自成一體的對象,對一個值類型變量執行的操做不可能影響另外一個值類型變量。

(P109)

爲了將一個值類型轉換成一個引用類型,可使用一個名爲 「裝箱」 (boxing) 的機制。下面總結了對一個值類型的實例進行裝箱操做時在內部發生的事情 :

1. 從託管堆中分配好內存。分配的內存量是值類型的各個字段所須要的內存量加上託管堆上的全部對象都有的兩個額外成員 (即類型對象指針和同步塊索引) 所須要的內存量;

2. 值類型的字段複製到新分配的堆內存;

3. 返回對象的地址。如今,這個地址是對一個對象的引用,值類型如今是一個引用類型;

(P110)

拆箱其實就是獲取一個指針的過程,該指針指向包含在一個對象中的原始值類型 (數據字段) 。事實上,指針指向的是已裝箱實例中的未裝箱部分。

在對一個對象進行拆箱操做時候,只能將其轉型爲未裝箱時的值類型。

(P115)

調用一個方法時,假如它沒有爲傳給它的一種特定的值類型準備一個重載版本,那麼最終確定會調用接受一個 Object 參數的重載版本。將一個值類型實例做爲一個 Object 來傳遞,會形成裝箱操做的發生,從而對性能產生不利影響。

未裝箱的值類型是比引用類型更爲 「輕型」 的類型。這要歸究於如下兩個緣由 :

1. 它們不在託管堆上分配;

2. 它們沒有堆上的每一個對象都有的額外成員,也就是一個類型對象指針和一個同步塊索引;

因爲未裝箱的值類型沒有同步塊索引,因此不能使用 System.Threading.Monitor 類型的各類方法 (或者使用 C# 的 lock 語句) 讓多個線程同步訪問這個實例。

(P121)

Object 的 Equals 方法實現的只是 「同一性」 (identity) ,而不是 「相等性」 (equality) 。

(P122)

若是想要檢查同一性 (看兩個引用是否指向同一個對象) ,那麼務必調用 ReferenceEquals ,而不該使用 C# 的 == 操做符 (除非先把兩個操做數都轉型爲 Object) ,由於其中某個操做數的類型可能重載了 == 操做符,爲其賦予有別於 「同一性」 的其餘語義。

因爲 CLR 的反射機制較慢,因此在定義本身的值類型時,應該重寫 Equals 方法,並提供本身的實現,以便在用類型的實例進行值相等性比較時提升性能。

【第06章】

(P130)

定義類型時,若是沒有顯式地指定類型的可見性, C# 編譯器會將類型的可見性設爲 internal (二者之中約束性比較強的一個) 。

(P131)

定義類型 (包括嵌套類型) 的成員時,能夠指定成員的可訪問性。成員的可訪問性代表目標代碼能夠合法訪問哪些成員。

(P132)

在 C# 中,若是沒有顯式地聲明成員的可訪問性,那麼,編譯器一般 (並不老是) 將成員的可訪問性默認設爲 private (可訪問性修飾符中約束性最強的一個) 。

(P133)

類不能將基類方法的可訪問性設置得更嚴格,由於派生類的用戶一般能夠強制轉換基礎類型來得到對基類方法的訪問。

關鍵字 static 僅能夠用於類,而不能用於結構 (值類型) ,這是由於 CLR 要求值類型必須實例化,而且沒有方法中止或阻止該實例化過程。

(P134)

經過使用關鍵字 static 定義的類將致使 C# 編譯器將該類同時標記爲 abstract 和 sealed 。

(P137)

屬性和事件其實是做爲方法實現的。

(P142)

暴露狀態極容易產生問題,它使對象的行爲沒法預測,並且還公開潛在的安全漏洞。

在類的內部,始終將本身的方法、屬性和事件定義爲 private 和非虛擬的。

【第07章】

(P147)

常量老是被看成靜態成員,而不是實例成員。

(P149)

只讀字段只能在構造器方法中寫入數值 (稱之爲一次寫,即在對象首次建立時寫入數值) ,編譯器和驗證機制確保只讀字段不能被構造器外的任何其餘方法寫入。須要注意的是,能夠採用反射 (reflection) 來修改 readonly 字段。

(P150)

當某個字段是引用類型,而且該字段標記爲 readonly 時,它就是不可改變的引用,而不是字段所引用的對象。

【第08章】

(P151)

構造器是容許將類型實例初始化爲有效狀態的特殊方法。

建立引用類型的實例時,首先爲實例的數據字段分配內存,接着初始化對象的系統開銷字段 (類型對象指針和同步塊索引) ,最後調用類型的實例構造器設置對象的初始狀態。

建立引用類型對象時,在調用類型的實例構造器以前,爲對象分配的內存始終被清零。構造器沒有顯式賦值的全部字段保證都有一個 0 或者 null 值。

若是定義的類中沒有顯式地定義任何構造器,那麼,許多編譯器 (包括 C# 編譯器) 將定義一個默認的 (無參數的) 構造器,該構造器的實現只是調用基類的無參構造器 (parameterless constructor) 。

(P152)

若是類的修飾符爲 abstract ,那麼編譯器生成的默認構造器的可訪問性爲 protected ;不然,構造器的可訪問性爲 public 。

若是基類沒有提供無參構造器,那麼,派生類必須顯式地調用基類的構造器,不然編譯器會報錯。

若是類的修飾符爲 static (sealed 和 abstract) ,那麼,編譯器就根本不會在類的定義中生成一個默認的構造器。

一個類型能夠定義多個實例構造器。每一個構造器都必須擁有一個不一樣的簽名,並且每一個構造器能夠擁有不一樣的可訪問性。

對於可驗證的代碼 (verifiable code) ,類的實例構造器在訪問從基類繼承的任何字段以前,必須調用其基類的構造器。

最終,類的實例構造器將調用基類 System.Object 的公有無參構造器。該構造器不執行任何代碼,只是簡單地返回,由於基類 System.Object 沒有定義實例數據字段,所以它的構造器沒有代碼能夠執行。

C# 語言提供了一個簡單的語法,容許在構建類型實例的過程當中初始化引用類型中定義的字段。

C# 編譯器提供了一個方便的語法來內聯初始化實例字段,而且將這個語法轉換成構造器方法中的代碼以執行初始化。

(P154)

CLR 確實容許在值類型上定義構造器,可是執行值類型上定義的構造器的唯一方法是編寫代碼顯式地調用這些構造器。

(P155)

值類型的實例構造器只有在被顯式調用時纔會執行。

(P156)

除了實例構造器外, CLR 還支持類型構造器 (type constructor) ,也稱爲靜態構造器 (static constructor) 、類構造器 (class constructor) 或者類型初始化器 (type initializer) 。

和實例構造器用來設置類型的實例的初始狀態同樣,類型構造器用來設置類型的初始狀態。

默認狀況下,類型不在類型內部定義類型構造器。若是類型定義了類型構造器,那麼類型構造器的數量不能超過一個。另外,類型構造器永遠沒有參數。

(P157)

定義類型構造器的方法相似於定義無參實例構造器,唯一的區別在於必須將類型構造器標記爲 static 。

類型構造器一般也應是私有的, C# 會自動地將類型構造器標記爲 private 。

由於 CLR 保證每一個應用程序域的類型構造器只執行一次,並且是線程安全的,因此最後合在類型構造器中初始化類型的單實例對象。

(P158)

若是類型構造器拋出一個未處理的異常, CLR 就會認爲類型不可以使用。試圖訪問該類型的任何字段或者方法都將致使 CLR 拋出一個 System.TypeInitializationException 異常。

類型構造器中的代碼只能訪問類型的靜態字段,而且它的常規用途就是初始化這些靜態字段。就像對待實例字段同樣, C# 提供了一個簡單的語法來初始化類型的靜態字段。

雖然 C# 不容許值類型使用內聯字段初始化語法初始化實例字段,但容許用它來初始化靜態字段。

(P162)

CLR 規範將操做符重載方法指定爲 public 和 static 方法。

(P166)

在關鍵字 implicit 或關鍵字 explicit 以後,能夠指定關鍵字 operator 向編譯器代表該方法是一個轉換操做符方法。在關鍵字 operator 後面,還須要指定對象要強制轉換成什麼類型,而在圓括號中,須要指定要進行強制轉換的對象的類型。

(P167)

默認狀況下, CLR 假定全部方法參數是按值傳遞的。

在方法中,必須知道傳遞的每一個參數是引用類型仍是值類型,由於編寫的用來處理參數的代碼會所以存在明顯的差別。

從 CLR 的角度看,關鍵字 out 和關鍵字 ref 是等效的,這就是說,不管使用哪一個關鍵字,都會生成相同的元數據和 IL 代碼。

(P168)

從 IL 和 CLR 的角度看, out 和 ref 功能相同 —— 它們都生成一個所傳遞實例的指針;這兩個關鍵字的區別在於須要編譯器進一步保證代碼的正確性。

(P170)

按引用傳遞給方法的變量的類型必須與方法簽名中聲明的類型相同。

(P172)

關鍵字 params 是應用於方法簽名的最後一個參數。

關鍵字 params 向編譯器代表將 System.ParamArrayAttribute 實例的自定義屬性應用到參數上。

(P173)

只有方法的最後一個參數才能夠標記關鍵字 params (ParamArrayAttribute) 。該參數必須標識一個一維數組,但類型不限。對方法的最後一個參數傳遞 null 或者 0 個條目的數組的引用都是合法的。

【第09章】

(P178)

CLR 支持靜態屬性、實例屬性、抽象屬性和虛擬屬性。

(P181)

在 C# 中,使用與數組相似的語法對外提供有參屬性 (索引器) 。換句話說,咱們能夠將索引器看做 C# 開發人員重載運算符 [] 的一種方式。

(P182)

全部的索引器必須至少擁有一個參數,也能夠擁有多個參數。這些參數以及返回類型能夠是任意的數據類型 (除了 void) 。

和無參屬性的 set 訪問器方法類似,索引器的 set 訪問器方法一樣也包含了一個隱藏的參數,在 C# 中稱之爲 value 。該參數代表 「索引元素 (indexed element)」 指望的新值。

CLR 自己並不區別無參屬性和有參屬性,對 CLR 來說,每一個屬性只是定義在類型中的一對方法和一塊元數據。

(P183)

在 C# 中,每一個類型均可以定義多個索引器,只要索引器的參數集不一樣便可。

(P185)

當定義有訪問器方法的屬性擁有不一樣的可訪問性時,C# 語法要求屬性自己必須聲明爲最低約束性的可訪問性,並且約束性較強的可訪問性只能夠應用於一個訪問器方法。

【第10章】

(P186)

公共語言運行庫 (Common Language Runtime , CLR) 的事件模型創建在委託 (delegate) 這一機制之上。委託是一種類型安全的調用回調方法 (callback method) 的方式。回調方法意味着哪一個對象接收對象所訂閱事件的通知。

(P187)

按照約定,全部傳遞給事件處理程序的用於存放事件信息的類都應該繼承自 System.EventArgs ,而且類的名稱應該以 EventArgs 結束。

(P188)

定義一個不須要傳遞任何額外信息的事件時,能夠直接使用 EventArgs.Empty ,不用構建一個新的 EventArgs 對象。

事件成員使用 C# 關鍵字 event 定義。每一個事件成員都有一個給定的可訪問性 (一般都爲 public ,以便於其餘代碼也能夠訪問這個事件成員) 、一個表示即將被調用方法的原型的委託類型以及一個名稱 (能夠是任意有效的標識符) 。

(P189)

事件模式要求全部的事件處理程序的返回類型都爲 void 。

按照約定,類應定義一個受保護的虛方法,當引起事件時,這個類及其派生類中的代碼能夠調用這個虛方法。

(P190)

設計一個對外提供事件的類型 :

第一步 : 定義一個類型用於存放全部須要發送給事件通知接收者的附加信息;

第二步 : 定義事件成員;

第三步 : 定義一個負責引起事件的方法,來通知已訂閱事件的對象事件已經發生;

第四步 : 定義一個方法,將輸入轉化爲指望事件;

(P193)

C# 要求代碼使用 += 和 -= 操做符在鏈表上添加和移除委託。

(P196)

事件必須同時擁有 add 和 remove 訪問器方法。

【第11章】

(P201)

在 .NET Framework 中,字符老是表示成 16 位 Unicode 代碼值,這簡化了全球應用程序的開發。一個字符表示成 System.Char 結構 (一個值類型) 的一個實例。

(P202)

可使用三種技術實現各類數值類型與 Char 實例的相互轉換 : 轉型 (強制類型轉換) 、使用 Convert 類型、 使用 IConvertible 接口。

(P204)

System.String 是任何一個應用程序使用得最多的類型之一。

一個 String 表明一個不可變的順序字符集。

String 類型直接派生自 Object ,這使其成爲一個引用類型。所以,String 對象 (它的字符數組) 老是存在於堆上,而不在線程的堆棧上。

在 C# 中,不能經過 new 操做符在一個直接量字符串的基礎上構造一個 String 對象。

公共語言運行庫 (CLR) 事實上採起一種特殊的方式來構造直接量 String 對象。

(P205)

在字符串以前添加 @ 符號,使編譯器知道字符串是一個逐字字符串。

(P206)

要想高效地執行大量字符串操做,請使用 StringBuilder 類。

String 類必須是密封類 (sealed) 。

(P217)

用 StringBuilder 對象構造好字符串以後,爲了將 StringBuilder 的字符數組 「轉換」 成一個 String ,只需調用 StringBuilder 的 ToString 方法。在內部,該方法只是返回對 StringBuilder 內部維護的字符串字段的一個引用。這使 StringBuilder 的 ToString 方法能夠很是快地執行,由於字符數組不須要複製。

(P218)

數組的動態擴容會損害性能。要避免這個危害,須要設置一個合適的初始容量。

StringBuilder 只在如下兩種狀況下分配一個新的對象 :

1. 試圖動態構造一個字符串,它的長度超過了事先設置的 「容量」 ;

2. 試圖在調用 StringBuilder 的 ToString 方法以後修改數組;

(P220)

System.Object 定義了一個 public 、 virtual 、無參數的 ToString 方法,因此在任何類型的一個實例上都能調用這個方法。

ToString 的 System.Object 實現的是返回對象所屬類型的全名。

(P222)

IFormatProvider 接口的基本思路是 : 假如一個類型實現了該接口,就認爲類型的一個實例可以提供依賴於語言文化的格式化信息,而與調用線程關聯的語言文化應被忽略。

(P224)

在內部, Format 方法會調用每一個對象的 ToString 方法來獲取對象的一個字符串表示。

採起在大括號中指定格式信息的方式,能夠對一個對象的格式化進行更多的控制。

(P227)

能解析一個字符串的任何類型都提供了一個名爲 Parse 的 public static 方法。該方法獲取一個 String 對象,並返回類型的一個實例。從某個角度來講, Parse 至關於一個 factory 方法。

(P229)

在 CLR 中,全部字符都是以 16 位 Unicode 代碼值的形式來表示的,並且全部字符串都由 16 位 Unicode 代碼值構成。

(P231)

Encoding 是一個抽象基類,它提供了幾個靜態只讀屬性,每一個屬性都返回從 Encoding 派生的一個類的實例。

(P232)

一旦得到從 Encoding 派生的一個對象以後,就能夠調用 GetBytes 方法,將一個字符串或者一個字符數組轉換成一個字節數組 (該方法有幾個重載版本) 。要將字節數組轉換成字符數組,須要調用 GetChars 方法或者更有用的 GetString 方法 (這兩個方法都有幾個重載版本) 。

【第12章】

(P240)

枚舉類型不能定義任何方法、屬性或事件。

枚舉類型只是一個在其中定義一系列常量字段和實例字段的結構。

【第13章】

(P247)

全部數組類型都隱式地從 System.Array 抽象類派生,後者又派生自 System.Object 。這意味着數組始終爲引用類型,是在託管堆上進行分配的,應用程序的變量或字段中包含的是對數組的引用,而不是對數組自己所含元素的引用。

【第14章】

(P262)

在 CLR 中,類始終繼承自一個並且只有一個類 (最終確定繼承自 Object) 。

CLR 還容許開發人員定義一個接口,接口實際只是爲一組方法簽名指定一個名稱的方式。

類繼承的一個重要特性是,在但願出現基類型實例的任何地方,均可以替換成派生類的實例。

接口繼承容許在但願出現已命名接口類型的實例的任何地方,均可以替換成實現接口的一個類型的實現。

接口是一組已命名的方法簽名。

接口還能夠定義事件、無參數的屬性和參數化的屬性 (在 C# 中是索引器) ,由於不管怎樣,全部這些在本質上都是方法。

一個接口不能定義任何構造器方法。

接口也不容許定義任何實例字段。

(P263)

在定義接口類型時,能夠爲所欲爲地指定 可視性 / 可訪問性 (public , protected , internal 等) 。

(P264)

C# 編譯器要求將實現了接口的方法標記爲 public 。 CLR 要求將接口方法標記爲 virtual 。

(P267)

注意,用 C# 定義一個顯式接口方法時,不容許指定訪問性 (好比公共或私有) 。可是,在編譯器生成方法的元數據時,其訪問性被設置爲私有,目的是防止使用類實例的任何代碼直接調用接口方法。要想調用接口方法,只能經過一個接口類型的變量來進行。

泛型接口提供了出色的編譯時類型安全性。

(P268)

泛型接口的第二個好處是在操做值類型時,不須要太多裝箱操做。

有的泛型接口繼承了非泛型版本,因此咱們所寫的類必須實現接口的泛型和非泛型版本。

泛型接口的第三個好處是,類能夠實現同一個接口若干次,只要使用不一樣的類型參數。

【第15章】

(P280)

對於一個經過委託來調用另外一個類型的私有成員的類型而言,這樣作不會損害其安全性或可訪問性,只要這個委託對象是由具備足夠安全性或可訪問性的代碼來建立的。

將一個方法綁定到一個委託時, C# 和 CLR 都容許引用類型的協變 (covariance) 和反協變 (contra-variance) 。協變指的是一個方法能返回從委託的返回類型派生的一個類型。反協變指的是一個方法的參數類型能夠是委託的參數類型的基類。

注意,協變與反協變只能用於引用類型,不能用於值類型或 void 。

(P282)

全部委託類型都繼承自 MulticastDelegate 。

System.MulticastDelegate 類繼承自 System.Delegate ,後者自己繼承自 System.Object 。

委託類能夠在一個類型內部 (即嵌套在另外一個類型內) 或在全局範圍內定義。簡單地說,由於委託是類,在能夠定義類的任何地方,均可以定義委託。

(P285)

鏈式委託指的是由一系列委託對象組成的集合,它容許調用集合中各個委託所表示的全部方法。

Delegate 類的公共靜態方法 Combine 用於添加一個委託到委託鏈。

(P288)

C# 編譯器自動爲委託類型的實例提供了運算符 += 和 -= 重載。這些運算符分別調用 Delegate.Combine 和 Delegate.Remove 。

(P292)

當 C# 編譯器看到指望收到委託對象引用的地方使用了 delegate 關鍵字,就會自動在類中定義一個新的私有方法。這個新方法叫作匿名方法 (anonymous method) ,由於編譯器自動爲咱們建立了方法名,並且一般狀況下,咱們並不須要知道這個名稱。

(P294)

若是回調代碼引用了任何一個參數,在 delegate 關鍵字後面必須包含括號、參數類型和變量名稱。返回類型仍然能夠從委託的類型推斷出來,並且若是返回類型不爲 void ,還必須在內聯的回調代碼內部包含一個 return 語句。

【第16章】

(P301)

定義一個泛型類型或方法時,它爲類型指定的任何變量 (好比 T) 都稱爲 「類型參數」 (type parameter) 。 T 是一個變量名,在源代碼中可以使用一個數據類型的任何位置,都能使用 T 。

因爲在可以指定一個數據類型的任何地方使用 T 變量,因此在方法內部定義一個局部變量時,或者在一個類型中定義字段時,也可使用 T 。

使用一個泛型類型或者方法時,指定的具體數據類型被稱爲 「類型實參」 (type argument) 。

(P307)

具備泛型類型參數的一個類型被稱爲 「開放式類型」 (open type) , CLR 禁止構造開放式類型的任何實例。

(P311)

沒有泛型接口,每次試圖使用一個非泛型接口來操縱一個值類型時,都會進行裝箱,並且會丟失編譯時的類型安全性。這會嚴重限制泛型類型的應用。

(P312)

CLR 支持泛型委託,目的是保證任何類型的對象都能以一種類型安全的方式傳給一個回調方法。

(P313)

定義一個泛型引用類型、值類型或者接口時,這些類型中定義的任何方法均可以引用類型指定的一個類型參數。類型參數能夠做爲方法的參數,做爲方法的返回值,或者做爲方法內部定義的一個局部變量來使用。然而, CLR 還容許一個方法指定它獨有的類型參數。這些類型參數可用於參數、返回值或者局部變量。

(P317)

類型參數能夠指定零個或者一個主要約束 (primary constraint) 。主要約束能夠是一個引用類型,它標識了一個沒有密封的類。

指定一個引用類型約束時,至關於向編譯器承諾 : 一個指定的類型實參要麼是與約束類型相同的類型,要麼是從約束類型派生的一個類型。

(P318)

class 約束向編譯器承諾一個指定的類型實參是引用類型。任何類類型、接口類型、委託類型或者數組類型都知足這個約束。

struct 約束向編譯器承諾一個指定的類型實參是值類型。包括枚舉在內的任何值類型都知足這個約束。

一個類型參數能夠指定零個或者多個次要約束,次要約束表明的是一個接口約束。指定一個接口類型約束時,是向編譯器承諾一個指定的類型實參是實現了接口的一個類型。因爲能指定多個接口約束,因此爲類型實參指定的類型必須實現全部接口約束 (以及全部主要約束,若是指定了的話) 。

(P319)

一個類型參數能夠指定零個或者一個構造器約束。指定構造器約束至關於向編譯器承諾 : 一個指定的類型實參是實現了一個 public 無參數構造器的一個非抽象類型。注意,若是同時指定了構造器約束和 struct 約束, C# 編譯器會認爲這是一個錯誤,由於這是多餘的;全部值類型都隱式提供了一個 public 無參數構造器。

(P320)

將一個泛型類型變量轉型爲另外一個類型是非法的,除非將其轉換爲與一個約束兼容的類型。

將泛型類型變量設爲 null 是非法的,除非將泛型類型約束成一個引用類型。

(P321)

Microsoft 的 C# 團隊認爲有必要容許開發人員將一個變量設爲一個默認值。因此, C# 編譯器容許使用 default 關鍵字來實現這個操做。

【第17章】

(P323)

聲明式編程是指使用數據而不是寫源代碼來指示 應用程序 / 組件去作某事。

「自定義特性」 是一種特殊的技術,它容許和命令式編程 (C# 源代碼) 配合使用聲明式編程。這種組合式編程爲編程人員提供了極大的靈活性,並容許以一種很是簡潔的方式表達開發人員的編碼意圖。

(P326)

爲了保持與 「公共語言規範」 (CLS) 相容,自定義特性類必須直接或間接地從公共抽象類 System.Attribute 派生。

能夠將多個特性應用於單個目標元素。

將多個特性應用於單個目標元素時,注意特性的順序是可有可無的。

在 C# 中,能夠將每一個特性都封閉到一對方括號中,也能夠在一對方括號中封閉多個以逗號分隔的特性。

假如特性類的構造器不獲取參數,圓括號就是無關緊要的。

【第18章】

(P344)

C# 提供了一個所謂的 「空結合操做符 (null-coalescing operator)」 ,即 「??」 操做符,它要獲取兩個操做數。假如左邊的操做數不爲 null ,就返回這個操做數的值。若是左邊的操做數爲 null ,就返回右邊的操做數的值。利用空結合操做符,能夠方便地設置變量的默認值。

空結合操做符的一個妙處在於,它既能用於引用類型,也能用於可空值類型。

【第19章】

(P352)

try 塊中包含的代碼一般要求執行公共資源清理操做,或者要求執行異常恢復操做,或者兩種操做都要求。

資源清理代碼應放在一個單獨的 finally 塊中。

try 塊一樣能夠包含可能會拋出異常的代碼。

異常恢復代碼應該放在一個或多個 catch 塊中。

咱們應該爲應用程序能夠從中恢復的每一種異常都建立一個 catch 塊。

一個 try 塊必須至少有一個 catch 塊或者 finally 塊與其關聯,單獨一個 try 塊是沒有任何意義的,並且 C# 也會阻止咱們這樣作。

catch 塊中包含的是響應異常時須要執行的代碼。一個 try 塊能夠有 0 個或者多個 catch 塊與其關聯。若是 try 塊中的代碼沒有拋出異常,那麼 CLR 永遠不會執行與該 try 塊相關聯的全部 catch 塊中的代碼。這時,線程就會跳出全部的 catch 塊,直接執行 finally 塊中的代碼 (若是存在的話)。 在 finally 塊中的代碼執行完畢後,線程就會繼續執行 finally 塊後面的語句。

(P354)

一個 try 塊並不要求必須有一個 finally 塊與其關聯,有時候 try 塊中的代碼並不須要任何清理工做。可是,若是確實有 finally 塊,那麼它必須出如今全部的 catch 塊以後。並且,一個 try 塊最多隻能有一個與其關聯的 finally 塊。

當線程執行完 finally 塊中包含的代碼後,線程當即開始執行緊跟在 finally 塊後的語句。記住, finally 塊中的代碼是清理代碼,該代碼只有在 try 塊中發起的操做須要進行清理時才被執行。應該避免將可能拋出異常的代碼放在 finally 塊中。

(P376)

System.Exception 類型提供了一個公共的只讀屬性 StackTrace 。 catch 塊能夠讀取該屬性來獲取異常的堆棧跟蹤,異常的堆棧跟蹤指出了異常通過的路徑中所發生的事件,它對於咱們檢測異常緣由、進而修正代碼來講很是有用。

【第20章】

(P397)

Finalize 方法在垃圾收集結束時被調用,有 5 種事件會致使一個對象的 Finalize 方法被調用 :

1. 第 0 代對象充滿;

2. 代碼顯式地調用 System.GC 的靜態方法 Collect ;

3. Windows 報告內存不足;

4. CLR 卸載應用程序域;

5. CLR 被關閉;

(P399)

一個對象要成爲可終結的對象,那麼在它的類型及其基礎類型中 (除 Object 以外) ,必須至少有一個類型重寫了 Object 的 Finalize 。

(P401)

Finalize 方法很是有用,由於它能夠確保託管對象在釋放內存的同時不會泄露本地資源。可是 Finalize 方法的問題在於咱們並不能肯定它會在什麼時候被調用,並且因爲它並非一個公共方法,咱們也不能顯式地調用它。

要提供肯定釋放或者關閉對象的能力,一個類型一般要實現一種釋放模式 (dispose pattern) 。釋放模式定義了開發人員在實現類型的顯式資源清理功能時要遵循的一些約定。若是一個類型實現了釋放模式,那麼使用該類型的開發人員將可以知道當對象再也不被使用時如何顯式地釋放掉它所佔用的資源。

(P406)

記住 Close 方法並非釋放模式正式定義的一部分,有些類型提供了 Close 方法,而有些類型則不會提供 Close 方法。

(P409)

FileStream 類型只支持字節的讀寫操做。若是咱們但願支持字符或者字符串的讀寫操做,可使用 System.IO.BinaryWriter 類型。

注意 StreamWriter 的構造器接受一個 Stream 對象的引用做爲參數,容許 FileStream 對象的引用做爲參數進行傳遞。 BinaryWriter 對象內部會保存 Stream 對象的引用。當咱們向一個 StreamWriter 對象寫入數據時,它會將數據緩存在本身的內存緩衝區中。當 StreamWriter 對象的內存緩衝區充滿時, StreamWriter 對象纔會將數據寫入 Stream 對象。

【第21章】

(P438)

CLR 初始化時建立的第一個應用程序域稱爲默認程序域 (default AppDomain) ,該應用程序域只有在進程終止時纔會被銷燬。

(P443)

在 Windows 中,線程一般在進程的上下文中建立,並且線程的生存期與該進程的生存期相同。可是線程和應用程序域之間沒有一對一的關係。

【第22章】

(P466)

GetType 方法在運行時返回對象的類型 (晚綁定) ;

typeof 方法返回指定類的類型 (早綁定) ;

(P476)

調用 GetMembers 方法返回的數組中的每個元素都是前面反射類型層次結構中的一個具體類型 (除非指定了 BindingFlags.DeclaredOnly 標記) 。儘管 Type 的 GetMembers 方法能夠返回全部類型的成員,但 Type 還提供有一些方法能夠返回特定的成員類型,例如 GetNestedTypes , GetFields , GetConstructors , GetMethods , GetProperties 以及 GetEvents 方法。這些方法返回的都是一個數組,其元素分別爲下述對象的引用 : Type 對象、 FieldInfo 對象、 ConstructorInfo 對象、 MethodInfo 對象、 PropertyInfo 對象以及 EventInfo 對象。

【第24章】

(P511)

除了縮短並簡化了代碼外, lock 語句還能夠保證對 Monitor.Exit 方法的調用,所以確保了即便在 lock 塊內發生異常時也能夠釋放同步塊。

(P535)

永遠不要爲 Monitor.Enter 方法或者 C# 的 lock 語句傳遞值類型的變量。由於未裝箱值類型實例沒有同步塊索引成員,所以它們不能用於同步。

編程

相關文章
相關標籤/搜索