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

来源:https://www.cnblogs.com/GATTACA2011/archive/2020/03/14/12494907.html
-Advertisement-
Play Games

《框架設計 CLR Via C#》 (第2版) [作者] (美) Jeffrey Richter[譯者] (中) 周靖 張傑良[出版] 清華大學出版社[版次] 2006年11月 第1版[印次] 2007年02月 第2次 印刷[定價] 68.00元 【前言】 Microsoft .NET Framew ...


《框架設計 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 語句傳遞值類型的變數。因為未裝箱值類型實例沒有同步塊索引成員,因此它們不能用於同步。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作者:Moon Light Dream 出處:https://www.cnblogs.com/Moon Light Dream/ 轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任 什麼是go cache KV存儲引擎有很多,常用的如redis,rocksd ...
  • 關於這篇博文 關於面向對象的話題,是一個十分重要的話題,由於博主是做java出身的,在java 中,由於java的嚴謹性,面向對象可以搞死你(/很難很難),在python中,所有的 一切都TM怎麼簡單,所以,這放出了詳細的python面向對象階段的學習筆記,供你 去翻閱,實例代碼也在裡面。 項目地址 ...
  • @Param()註解 引用類型不需要加 如果只有一個基本類型的,可以忽略 在 sql 中引用的就是在註解中設定的欄位名 高級結果映射 多對一: 一對多 動態 sql if choose (when, otherwise) trim (where, set) foreach 實體類 if 如果上面的 ...
  • 1. SpringMVC的Controller實現方式 SpringMVC實現Controller的方式主要有控制器實現方式與全註解實現方式,其中全註解實現方式是當前項目中比較常用的一種方式。 1.1.控制器實現方式 1.1.1. 實現Controller介面 創建一個類實現Controller介面 ...
  • 1. 迭代器簡介 為了提高C++編程的效率,STL(Standard Template Library)中提供了許多容器,包括vector、list、map、set等。然而有些容器(vector)可以通過下標索引的方式訪問容器裡面的數據,但是大部分的容器(list、map、set)不能使用這種方式訪 ...
  • 由於研究Libra等數字貨幣編程技術的需要,學習了一段時間的Rust編程,一不小心刷題上癮。 “歐拉計劃”的網址: "https://projecteuler.net" 英文如果不過關,可以到中文翻譯的網站: http://pe cn.github.io/ 這個網站提供了幾百道由易到難的數學問題,你 ...
  • 本人免費整理了Java高級資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分散式等教程,一共30G,需要自己領取。傳送門:https://mp.weixin.qq.com/s/osB-BOl6W-ZLTSttTkqMPQ 第 ...
  • 省市遞歸載入到TreeView只是遞歸的一個表現方式,使用遞歸可以達到很多的功能,類似我們電腦本地的 磁碟目錄文件載入等等均是該原理,十分便捷。 後續還會附上目錄載入、以及添加、刪除、修改父子節點等等 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...