《[深入淺出 C#] (第3版)》 - 學習筆記

**《深入淺出 C#》 (第3版)

========== ========== ==========
[作者] (美) Andrew Stellman (美) Jennifer Greene
[譯者] (中) 徐陽 丁小峯 等譯
[出版] 中國電力出版社
[版次] 2016年08月 第1版
[印次] 2018年04月 第4次 印刷
[定價] 148.00元
========== ========== ==========

【引子】

要學習編程,沒有別的辦法,只能通過編寫大量代碼。

編寫代碼是一種技巧,要想在這方面擅長,只能通過實踐。

【第01章】

(P007)

IDE 的優點在於,它能自動地爲你生成一些代碼,但是它的作用僅此而已。

不過,對於編程中最困難的部分,也就是確定你的程序要做什麼以及如何讓它能真正做到, IDE 就無能爲力了。

IDE 爲你做的所有一切,包括它創建的每一行代碼,增加的每一個文件,都是可以修改的,你可以直接手動地編輯文件,也可以通過 IDE 簡便易用的界面來修改。

【第02章】

(P054)

IDE 是一個功能強大的工具,不過僅此而已,這只是一個可供使用的工具。

(P056)

可以認爲 IDE 是一種方便的文件編輯器。它會自動爲你完成縮進,改變關鍵字的顏色,完成括號匹配,甚至還能建議下一個可能的詞是什麼。不過,歸根結底,IDE 所做的只是編輯文件,這些文件包含了你的程序。

(P057)

Windows Runtime 和 .NET Framework 中的工具劃分爲不同的命名空間 (namespace) 。

Windows 8 中的每個程序都在一個稱爲 Windows Runtime (Windows 運行時) 的體系結構上運行。不過在 Windows Runtime 與你的程序之間還有另外的一 “層” ,這稱爲通用語言運行時庫 (Common Language Runtime , CLR) 。

(P060)

每個 C# 程序的代碼結構幾乎完全一樣。所有程序都使用了命名空間、類和方法,使代碼更易於管理。

每次建立一個新程序時,要爲它定義一個命名空間,使程序代碼與 .NET Framework 類和 Windows Store API 類區別開。

類包含程序的一部分。

類中有一個或多個方法。方法總是放在類中。

方法由語句組成。

類文件中方法的順序並不重要。

(P065)

只有使用了 partial 關鍵字時才能將一個類劃分到不同文件中。

(P066)

C# 使用變量類型來限制變量能存儲哪些數據。

(P067)

要想確保不會忘記對變量賦值,最容易的方法是把聲明變量的語句與爲變量賦值的語句結合起來。

(P076)

可以使用 && 操作符或 || 操作符把單個條件測試結合爲一個長測試, && 表示與 (AND) , || 表示或 (OR) 。

(P093)

每個 C# 都必須有一個名爲 Main 的方法。這個方法就是代碼的入口點。

運行代碼時, Main() 方法中的代碼最先執行。

【第03章】

(P108)

對象 (Object) 是 C# 的一個工具,可以用來處理一組類似的事物。

(P109)

類對於對象來說就像是設計藍圖。

一旦構建一個類,可以根據需要使用 new 語句創建多個對象。創建對象後,類中的各個方法都將成爲對象的一部分。

(P111)

從一個類創建新對象時,這稱爲創建這個類的一個實例。

(P115)

靜態方法不要求有實例,而非靜態方法需要先有一個實例。

(P116)

方法是對象做什麼,字段是對象知道什麼。

對象的行爲由方法定義,另外使用字段來跟蹤它的狀態。

(P118)

程序創建一個新對象時,會把它增加到堆中。

(P122)

類圖是一種將類畫在紙上的簡單方法。這是一個很有意義的工具,利用類圖,可以在編寫代碼之前設計代碼。

(P133)

對象初始化方法可以節省你的時間,使代碼更緊湊,更可讀 ······ 而且 IDE 會幫助你編寫對象初始化方法。

【第04章】

(P141)

實際上,編程中所做的幾乎每一件事都是在以這樣或那樣的方式處理數據。

(P142)

sbyte 中的 “s” 代表 “有符號” (signed) ,表示它可以是一個負數 (“符號” 就是一個負號) 。

“u” 代表 “無符號” (unsigned) 。

(P143)

在 C# 代碼中轉義序列寫爲兩個字符,不過程序會把各個轉義序列作爲單個字符存儲在內存中。

(P144)

所有數據都會佔據內存中的一定空間。

利用變量,可以在內存中預留足夠的空間來存儲數據。

(P147)

將一個數值賦至 float 時,需要在這個數字後面加一個 “F” ,告訴編譯器這是一個 float 而不是 double 。否則,代碼將不能編譯。

(P148)

使用 “+” 將一個字符串與另一種類型的某個值或變量連接時,它會自動地將數字轉換爲字符串。

(P149)

形參 (parameter) 是方法中定義的參數。實參 (argument) 是向方法傳入的參數。

(P158)

要讓一個對象留在堆中,它必須被引用。對象的最後一個引用消失一段時間後,對象也會消失。

(P170)

對象使用 this 關鍵字時,就是指自己,這個引用指向調用 this 的對象。

(P171)

創建一個新引用但是還沒有爲它設置任何對象時,它也會有一個值。剛開始時會設置爲 null ,這說明它不指向任何對象。

如果一個對象有一個引用,而且以後不再使用這個對象,將它的引用設置爲 null 時,就會立即將這個對象標誌爲可以回收 (除非別處還有這個對象的一個引用) 。

(P172)

要使用對象中的方法和字段,唯一的途徑就是通過引用。

變量之所以稱爲變量,就是因爲它們總在改變。

在一個實例化對象的代碼中,實例可以使用這個特殊的 this 變量,其中包含它自己的一個引用。

【第05章】

(P211)

充分考慮到你的懶惰 —— 如果沒有加上 “private” 或 “public” 聲明, C# 就會認爲這個字段是私有的。

(P214)

一個對象要得到另一個對象私有字段中存儲的數據,只有一個辦法 : 就是使用能返回該數據的公共字段和方法。

寫一個類時,一定要保證爲其他對象提供了一個途徑來得到它們需要的數據。私有字段是封裝的一個很重要的部分,但是它們並不是全部。要編寫一個封裝性好的類,意味着要爲其他對象提供一個合理的、易於使用的途徑來得到它們需要的數據,但不允許它們非法截獲你的類本身依賴的數據。

一個對象要得到另一個不同類型對象的私有字段中存儲的數據,唯一的途徑就是使用返回該數據的公共方法。

(P217)

封裝是指讓一個類對另一個類隱藏信息。這有助於避免程序中的 bug 。

(P219)

如果你今天能很好地封裝類,明天重用這些類會容易得多。

(P221)

如果沒有合適的理由,就不要將字段或方法聲明爲公共。如果程序中的所有字段都聲明爲公共字段,可能會把問題搞得一團糟。不過也不要把一切都設置爲私有。先花一些時間來考慮這個問題,哪些字段確實需要公共的,而哪些不必,以後這會爲你節省很多時間。

(P226)

對象初始化方法中只能初始化公共字段和屬性。

(P227)

要向類增加一個構造函數,只需要增加一個與類同名的方法,而且沒有返回值。

(P229)

方法中能做的,在屬性中也都可以做。

屬性 (獲取和設置存取方法) 是一種特殊類型的 C# 方法,只有當讀寫屬性時纔會運行。

(P231)

如果在變量前增加 this 關鍵字,就是在告訴編譯器你所指的是字段,而不是參數。

(P234)

封裝可以使你的類以後更易於理解和重用。

【第06章】

(P241)

對於只有一行代碼的代碼塊,大括號是可選的。

(P260)

使用 override 關鍵字向子類增加一個方法,可以替換它繼承的方法。覆蓋一個方法之前,需要在基類中將這個方法標誌爲 virtual 。

(P265)

任何方法需要一個類作爲參數時,完全可以輸入擴展這個類的一個子類的實例。

(P268)

如果子類只是增加一個與超類方法同名的方法,那麼它只是隱藏了超類方法而不是覆蓋這個方法。

(P271)

如果你想覆蓋一個基類中的方法,一定要用 virtual 關鍵字標誌這個方法,如果希望在子類中覆蓋這個方法,就要使用 override 關鍵字。如果沒有做到這一點,很可能會無意中隱藏方法。

(P272)

即使子類覆蓋了基類中的方法或屬性,有時仍可能希望訪問基類中的這個成員。幸運的是,可以使用 base 關鍵字,利用這個關鍵字就可以訪問基類中的任何方法。

【第07章】

(P296)

使用接口要求一個類包含接口中所列的全部方法和屬性。如果類沒有做到這一點,編譯器會報錯。

只要編譯代碼時類中有接口要求的方法和屬性,接口並不關心這些方法或屬性是如何得來的。

(P297)

接口不存儲數據,所以不能增加任何字段。

公共接口中的所有方法都會自動成爲公共方法,因爲接口就是用來定義實現該接口的類的公共方法和屬性。

(P298)

接口的目的是讓一個類可以完成多個任務,而不依賴於繼承,因爲繼承會帶來很多額外的負擔,你必須繼承每一個方法、屬性和字段,而不只是與處理特定任務有關的那些成員。

(P300)

在接口中不需要輸入 “public” 因爲會自動將接口中的各個屬性和方法置爲公共。

(P303)

跟蹤對象時,接口引用與對象引用同樣有效。

可能並不需要一個對象引用,完全可以創建一個新對象,並把它直接賦給一個接口引用變量。

(P304)

利用 “is” 可以比較接口,也可以比較其他類型。

(P305)

由於所有接口中都沒有具體的方法體,所以不需要考慮調用基類構造函數或方法。繼承接口 (子接口) 只需彙集它繼承的接口的所有方法和屬性。

(P306)

任何類可以實現任何接口,只要它能信守承諾,實現該接口的所有方法和屬性。

(P307)

“is” 指出一個對象實現了什麼, “as” 則告訴編譯器如何看待一個對象。

(P309)

向上強制轉換唯一的缺點是,只能使用基類的屬性和方法。

一旦將一個子類向上強制轉換爲基類,由於訪問對象使用的是基類引用,所以只能訪問這個基類的方法和屬性。

(P310)

向下強制轉換的第一步是使用 “is” 關鍵字檢查有沒有這種可能。

(P312)

如果向下強制轉換是非法的, as 語句只會返回 null 。

接口中不允許有任何語句。

接口就像一個清單,編譯器檢查這個清單來確保類實現了指定的方法。

(P315)

我們把一個類的方法、字段和屬性稱爲它的成員 (members) 。所有成員都可以標誌 public 或 private 訪問修飾符。

將一個類成員標誌爲 private 時,只有這個類中的成員或該類的其他實例能訪問這個成員。

不能把一個類標誌爲 private ,除非這個類位於另一個類內部,在這種情況下,它只對它的 “容器” 類的實例可用。

類成員默認爲私有,如果希望它是公共的,需要明確標識爲公共。

聲明一個類成員時如果沒有訪問修飾符,就默認爲 private 。

protected 對於子類表示 public ,對其他表示 private 。

子類不能訪問其基類中的私有字段,另外必須使用 base 關鍵字才能訪問基類對象的公共成員。

標誌 protected 的類成員可以由該類中所有其他成員訪問,另外該類子類中的所有成員也可以訪問。

聲明一個類或接口時,如果沒有訪問修飾符,默認設置爲 internal 。

如果你沒有使用多個程序集, internal 對於類和接口來說就相當於 public 。

(P318)

利用屬性,可以讓一個東西在其他對象看來像是一個字段,但由於它實際上是一個方法,這樣就不會真正存儲任何數據。

接口引用只知道這個接口中定義的方法和屬性。

(P320)

抽象類可以有字段和方法,而且也可以繼承其他類,這與正常類是一樣的。

抽象類可以包含屬性和方法的聲明,與接口一樣,子類必須實現這些屬性和方法。

抽象類和具體類之間最大的區別在於,不能使用 new 創建抽象類的實例。

如果一個方法有聲明但是沒有語句或方法體,這稱爲一個抽象方法 (abstract method) 。就像繼承接口一樣,繼承抽象類的子類必須實現所有抽象方法。

只有抽象類可以有抽象方法。如果把一個抽象方法放在一個類中,就必須標誌這個類爲抽象類,否則無法編譯。

(P322)

將一個類標誌爲 abstract 時, C# 不允許你編寫代碼來實例化這個類。這很像接口,它就相當於一個模板,繼承它的子類都要以它爲模板。

爲類聲明增加 abstract 關鍵字,這就告訴 C# 這是一個抽象類,不能實例化。

(P323)

接口中的各個方法自動作爲抽象方法,所以在接口中不需要像抽象類中那樣使用 abstract 關鍵字。

抽象類可以有抽象方法,不過也可以有具體方法。

(P330)

封裝是指創建一個對象,使用私有字段在內部記錄它的狀態,另外通過公共屬性和方法使其他類只能使用它們需要看到的那部分內部數據。

(P331)

將一個類的實例用在需要其他類型 (如父類或這個類實現的接口) 的語句或方法中,這就是在使用多態。

【第08章】

(P353)

enum 數據類型只允許某個數據取某些特定的值。

大括號裏的內容稱爲枚舉項列表 (enumerator list) ,其中每一項稱爲枚舉項 (enumerator) 。整體稱爲枚舉 (enumeration) 。

(P354)

可以把一個 int 強制轉換爲一個 enum ,還可以把一個 (基於 int 的) enum 強制轉換回 int 。

(P358)

用數組來存儲一個固定的值或引用列表還不錯,但是如果需要移動數組元素,或者要增加超出數組容量的更多元素,就有些麻煩了。

(P363)

List 有一大特點 : 創建 List 時你不需要知道它會有多長。 List 會自動伸縮來適應它的內容。

foreach 循環也能處理數組,實際上, foreach 循環適用於任何集合。

(P367)

“泛型” (generic) 是指,儘管 List 的一個特定實例只能存儲一種特定類型,但 List 類適用於任何類型。

創建一個新的 List 對象時,總要提供一個類型,告訴 C# 它會存儲什麼類型的數據。

(P368)

集合初始化方法可以使代碼更緊湊,可以把創建列表和增加一組初始項結合在一起。

(P371)

要讓 List 的內置 Sort() 方法對某個類排序,只需讓這個類實現 IComparable<T> 接口,並增加一個 CompareTo() 方法。

List.Sort() 方法知道如何對實現了 IComparable<T> 接口的類或類型排序。這個接口只有一個成員,即 CompareTo() 方法。 Sort() 使用一個對象的 CompareTo() 方法與其他對象比較,並使用其返回值 (一個 int) 來確定哪一個在前。

但是有時需要對沒有實現 IComparable 接口的對象列表排序,對此, .NET 中的另一個接口可以提供幫助。可以向 Sort() 傳入一個實現了 IComparer<T> 的類的實例。這個接口也只有一個方法。List 的 Sort() 方法使用這個比較對象的 Compare() 方法來比較一對對象,從而得出它們在有序列表中的先後順序。

(P373)

要使用 IComparer<T> 排序時,需要創建實現這個接口的類的一個新實例。這個對象的存在是爲了幫助 List.Sort() 確定如何對數組排序。

(P377)

每個 .NET 對象都有一個名爲 ToString() 的方法,這個方法可以把對象轉換爲一個字符串。默認地,它只是返回類名。

聯接字符串的 “+” 操作符會自動調用一個對象的 ToString() 。

Console.WriteLine() 或 String.Format() 也會在傳入對象時自動調用這個方法,如果你想把一個對象轉換爲一個字符串,這會很方便。

(P379)

集合初始化方法適用於任何 IEnumerable<T> 對象,只要它有一個 Add() 方法。

集合實現 IEnumerable<T> 時,就爲你提供了一種方法,可以編寫循環按順序循環處理其中的內容。

(P380)

如果你想把一個對象集合增加到一個更通用的列表中,協變就非常有用。

List.AddRange() 方法,這個方法可以把一個列表的內容增加到另一個列表中。

(P401)

隊列是先入先出型 (first-in first-out) ,這說明入隊列的第一個對象也就是最先取出使用的對象。

棧是先進後出型 (first-in last-out) ,最先進入棧的對象將最後一個取出。

可以使用 foreach 循環處理棧或隊列,因爲它們都實現了 IEnumerable 。

【第09章】

(P410)

流 (stream) 是 .NET Framework 爲程序提供的讀寫數據的方法。

如果希望對文件讀寫數據,就會使用一個 Stream 對象。

(P411)

流允許讀寫數據。要針對所處理的數據使用適當類型的流。

(P412)

FileStream 一次只能關聯一個文件。

流向文件寫字節,所以需要將要寫的 string 轉換爲一個 byte 數組。

關閉文件,使其他程序能夠訪問這個文件。

如果忘記關閉流,這是一個嚴重的問題。如果沒有關閉流,文件會被鎖定,其他程序在你關閉這個流之前將無法使用這個文件。

(P413)

StreamWriter 會爲你自動創建和管理一個 FileStream 對象。

可以向 StreamWriter() 構造函數傳入一個文件名。如果傳入了文件名,書寫器 (writer) 會自動打開這個文件。 StreamWriter 還有一個重載構造函數,允許你指定它的追加模式 : 傳入 true 表示要把數據增加 (或追加) 到一個現有文件的末尾,傳入 false 會告訴流要刪除現有文件,再創建一個同名的新文件。

如果保持流打開並關聯到一個文件,就會鎖定這個文件一直處於打開狀態,其他程序將無法使用這個文件。所以一定要關閉文件。

(P417)

StreamReader (繼承自 TextReader) 是一個從流讀取字符的類。它本身不是一個流。將一個文件名傳入這個類的構造函數時,它會爲你創建一個流,調用它的 Close() 方法時它會關閉這個流。

StreamReader 還有一個重載的構造函數,有一個 Stream 參數。

EndOfStream 屬性指出文件中是否還有未讀的數據。

(P418)

可以把流串起來,一個流可以寫到另一個流,而這個流又可以再寫到下一個流······ 最後通常是網絡或者一個文件流。

(P424)

File 類完成少量操作時速度更快,而 FileInfo 更適合完成大量任務。

(P425)

StreamReader 和 StreamWriter 會爲你將字節轉換爲字符,這稱爲編碼和解碼。

如果只是按部就班地向一個文本文件讀寫文本行,那麼只需要 StreamReader 和 StreamWriter 就足夠了。

(P429)

在一個 using 塊中聲明一個對象,這個對象的 Dispose() 方法會自動調用。

對於任何實現了 IDisposable 接口的類,只要調用了它的 Dispose() 方法,會給立即釋放它佔用的資源。通常這是結束對象處理的最後一步。

(P430)

每個流都有一個 Dispose() 方法,它會關閉這個流。所以如果在一個 using 語句中聲明流,它總會自行關閉。

所有流都實現了 IDisposable ,所以只要使用流,都應當在一個 using 語句中聲明。這樣能確保流總是會關閉。

可以在 using 語句上再羅列其他 using 語句,不需要另外的大括號或縮進。

(P437)

switch 語句將一個變量與多個可能值進行比較。

每個 case 都必須以 “break;” 結束,這樣 C# 才能知道一種情況在哪裏結束,下一個在從哪裏開始。

還可以用 “return” 結束一個 case ,只要一個 case 不會繼續 “落入” 下一個 case ,程序就能編譯。

switch 語句的體是一系列 case 語句,將 switch 關鍵字後面的變量與一個特定值比較。

每個 case 都包括一個 case 關鍵字,後面時要比較的值和一個冒號。然後是一系列語句,最後是 “break;” 。如果這個 case 中的值與比較值匹配,就會執行這些語句。

(P444)

將對象複製到文件或者從文件讀出一個對象很簡捷。可以完成串行化和逆串行化。

(P451)

使用 File.Create() 時,這會創建一個新文件,如果已經有這樣一個文件,這個方法會把原文件刪除而創建一個全新的文件。另外還有一個 File.OpenWrite() 方法,它會打開原來的文件,並從頭開始覆蓋。

【第10章】

(P498)

定義頁面控件的 XAML 會成爲一個 Page 對象,它的字段和屬性包含這些 UI 控件的運用。

(P512)

XAML 中的數據綁定是指源屬性與目標屬性之間的一種關係,源屬性是爲控件提供數據的對象 (數據對象) 的一個屬性,目標屬性是顯示該數據的控件 (控件對象) 的一個屬性。要建立數據綁定,控件的數據上下文必須設置爲這個數據對象的一個引用。必須將控件的綁定 (binding) 設置爲一個綁定路徑,就是要綁定到對象的這個屬性。一旦完成這些設置,控件就會自動讀取源屬性,並作爲控件的內容顯示這個數據。

(P513)

.NET 提供了 ObservableCollection<T> ,這是一個專門爲數據綁定建立的集合類。

創建 XAML 代碼實現數據綁定時,它會使用一個 Binding 對象的實例來建立綁定,這個對象把目標屬性的名字存儲爲一個字符串。

(P525)

頁面中的靜態資源會在頁面首次加載時實例化,應用中的對象可以在任何時候使用這些靜態資源。

(P526)

可以讓數據對象通知它們的目標屬性和綁定控件,告訴它們數據已經改變。你要做的就是實現 INotifyPropertyChanged 接口,這個接口中包含一個名爲 PropertyChanged 的事件。只要一個屬性有變化,就會觸發這個事件,可以看到綁定控件會自動自行更新。

(P527)

要通知一個綁定控件某個屬性有變化,你要做的就是調用 OnPropertyChanged() ,並提供發生變化的那個屬性的名字。

【第11章】

(P544)

可以使用一個新技術重新構建你之前已經構建的一個程序,這是掌握這種新技術的一個非常好的方法。

方法中有一個 await 時,在這個方法的聲明中必須有一個 async 。

(P546)

使用二進制串行化時,你寫的是 “純” 數據 : 會把內存中的實際字節連起來,寫到一個文件中,另外會爲二進制格式化工具提供足夠的信息,使它能確定哪些字節對應對象圖中的哪些類成員。

(P547)

數據契約 (data contract) 是與類關聯的一個正式約定。這個契約使用 [DataContract] 和 [DataMember] 屬性來定義串行化時要讀寫什麼數據。

如果你想串行化一個類的實例,可以爲它建立一個數據契約,在最上面增加 [DataContract] 屬性,然後爲要串行化的各個類成員增加 [DataMember] 屬性。

(P557)

要讓一個 async 方法調用另一個異步方法,被調用的方法的返回類型必須是 Task 類 (或者,如果這個方法需要返回一個值,返回類型也可以是它的子類 Task<T>) 。

推薦的命名約定是在使用 await 操作符調用的異步方法名末尾增加 Async 。

async 修飾符、 await 關鍵字以及 Task 類都是爲了更容易地編寫異步代碼。

【第12章】

(P585)

把可能拋出異常的代碼放在 try 塊中。如果沒有發生異常,它會正常地運行, catch 塊中的語句將被忽略。不過,如果 try 塊中的某個語句確實拋出了一個異常, try 塊中的其餘語句就不會再執行。

(P587)

Watch 窗口中完成的所有修改只會影響內存中的數據,而且只持續到程序運行結束。重啓程序時,在 Watch 窗口中修改的值又會還原。

(P588)

把斷點加在 try 塊的開始大括號上。

(P595)

在 catch 塊中指定一個異常類型時,如果提供了一個變量名,代碼就可以使用這個變量名來訪問這個 Exception 對象。

(P601)

要記住,在 “using” 語句中聲明一個引用時,會在這個塊末尾自動調用它的 Dispose() 方法。

使用 using 語句時,就是在充分利用 finally 來確保總會調用它的 Dispose() 方法。

(P602)

IDisposable 是避免常見異常和問題的一個非常有效的方法。處理實現了這個接口的類時,一定要使用 using 語句。

只有當一個類實現了 IDisposable 時,才能在 “using” 語句中使用這個類;否則,程序將不能編譯。

如果想在 using 語句中使用你的對象,它必須實現 IDisposable 。

IDisposable 接口只有一個成員 : Dispose() 方法。這個方法中的所有代碼都會在 using 語句的最後執行 ······ 或者手動調用 Dispose() 時執行。

實現 Dispose 的一個原則是你的 Dispose() 方法可以調用多次而沒有任何副作用。

(P604)

要記住,如果你的代碼沒有處理一個異常,這個異常會在調用棧中向上傳遞。讓異常向上傳遞是一種完全合法的異常處理方法,有時這比使用一個 try / catch 塊來處理異常可能更合適。

(P606)

只要使用流就應當使用 using 塊! 切記,切記,切記!

【第13章】

(P618)

最終化方法不能有參數,因爲只需要告訴 .NET “搞定!” 就行了,除此之外不需要再說其他的。

(P619)

對象的最終化方法在所有引用都消失之後運行,而且是在對象被垃圾回收之前。對象的所有引用都消失時纔會發生垃圾回收。不過,並不是引用一消失就進行垃圾回收。

如果不是一個 “玩具” 性的程序,就不要使用 GC.Collect() ,這是一個很糟糕的做法,這一點再強調也不爲過,因爲這會干擾 CLR 的垃圾回收器。但這個方法非常適合用來學習垃圾回收和最終化方法。

(P627)

struct 可以實現接口,不過不能派生其他類,另外 struct 是密封的,所以不能派生 struct 。

struct 不是對象,它們可以有方法和字段,但是不能有最終化方法。 struct 不能繼承其他類或 struct ,也不能被其他的類或 struct 繼承。

(P630)

設置一個 struct 等於另一個 struct 時,就是在爲該 struct 中的數據創建一個全新的副本。這是因爲 struct 是一個值類型。

(P634)

通過使用 out 參數,方法可以返回多個值。

每次調用一個有 out 參數的方法時,傳入實參時都要使用 out 關鍵字。

(P636)

如果希望方法有默認值,可以使用可選參數和命名參數。

(P637)

只需要在值類型後面增加一個問號 (?) ,它就會變成一個可爲空的類型,可以將它設置爲 null 。

每個可爲空的類型都有一個名爲 Value 的屬性,可以獲取或設置這個值。

它們還有一個名爲 HasValue 的屬性,如果值不爲 null 就會返回 true 。

值類型都可以轉換爲一個可爲空的類型。

不過要把可爲空的類型再賦給一個值類型,則需要進行強制轉換。

如果 HasValue 爲 false , Value 屬性會拋出一個 InvalidOperationException 異常,強制類型轉換也同樣會拋出這個異常 (因爲強制轉換等價於使用 Value 屬性) 。

(P640)

如果希望爲類提供很好的封裝, struct 會很有意義,因爲返回一個 struct 的只讀屬性會建立它的一個全新副本。

(P642)

擴展方法總是靜態方法,而且必須放在靜態類中。

(P644)

定義擴展方法的類必須是一個靜態類。

(P648)

記住, as 關鍵字只用於類,不能用於 struct 。

【第14章】

(P653)

使用 LINQ 調用的方法實際上就是用來擴展數組的一些擴展方法。

(P667)

Take() 從 LINQ 查詢的第一個結果集中取出指定數目的數據項。可以把這些數據項放在另一個 var 中,然後再把它轉換到一個列表中。

利用 LINQ ,可以編寫查詢使用很少的代碼完成非常複雜的工作。

(P670)

from 子句會完成兩個工作 : 它告訴 LINQ 查詢使用哪個集合,另外爲要查詢的集合中的各個成員指定一個名字。

select 子句告訴 LINQ 這個序列中應該有什麼。

(P679)

每個組包含一個共同的成員,稱爲“組鍵”。使用 “by” 關鍵字來指定組鍵。每個組序列有一個 Key 成員,其中包含這個組的組鍵。

【第15章】

(P702)

你希望對象只考慮它自己,而不考慮其他對象。這裏就分離了各個對象的關注點。

(P704)

一個事件有一個發佈者,而且可以有多個訂購者。

(P705)

事件採用先來先服務的原則進行處理,最先訂購的對象最先得到通知。

(P706)

event 關鍵字後面是 EventHandler 。它不是 C# 的保留字,這是 .NET 中定義的一個關鍵字。之所以需要這個關鍵字,是爲了告訴訂購這個事件的對象 : 它們的事件處理方法應該是什麼樣子。

使用 EventHandler 時,就是在告訴其他對象它們的事件處理方法應當有兩個參數,一個是名爲 sender 的 object ,另一個參數是一個 EventArgs 引用,名爲 e 。 sender 是產生事件的那個對象的引用, e 是 EventArgs 對象的一個引用。

(P709)

EventHandler 的作用就是定義事件的簽名,它告訴訂購這個事件的對象該如何定義事件處理方法。

一個事件總由一個對象產生。但是一個事件可以由多個對象響應。

(P716)

EventHandler 的泛型參數必須是 EventArgs 的一個子類。

(P731)

創建一個委託時,只需要爲這個委託指向的方法指定方法簽名。

爲工程增加一個委託時,就是在增加一個委託類型 (delegate type) 。用它創建一個字段或變量時,就是在創建這個委託類型的一個實例。

(P732)

委託總是出現在所有其他類外面。

(P738)

回調是另一種使用委託的方式。

(P739)

向工程增加一個委託,就是在創建一個新類型,可以存儲方法的引用。

事件使用委託來通知對象有某些動作發生。

如果對象需要對某個對象中發生的事情做出反應,可以訂購這個對象的事件。

EventHandler 是一種委託,處理事件時這個委託很常用。

可以把多個事件處理方法串鏈到一個事件上。因此要用 “+=” 爲事件設置事件處理方法。

在使用事件或委託之前,一定要檢查是否非 null ,以防止出現 NullReferenceException 異常。

一個對象將一個方法的引用傳入另一個對象,使它 (只有它) 能返回信息,這就稱爲一個回調。

利用事件,任何方法都可以匿名地訂購對象的事件,而回調允許對象控制接收哪些委託。

回調和事件都使用委託來引用和調用其他對象中的方法。

(P740)

“回調” 只是使用委託 (或事件,完全可以使用一個私有事件來建立回調) 的一種方法。回調只是在兩個類之間建立一種關係,一個對象可以請求另一個對象的通知。相比之下,在事件中,則是一個對象要求得到事件通知。

【第16章】

(P749)

視圖模型就像是水管,利用一些工具 (你已經知道如何使用這些工具) 把視圖中的對象連接到模型中的對象。

(P764)

模型可以觸發一個事件,告訴應用的其餘部分某個重要的狀態發生了變化,而不需要引用模型以外的類。這樣更容易構建,因爲它與其餘的 MVVM 層是解耦合的。

(P781)

MVVM 模式將視圖與視圖模型解耦合,另外將視圖模型與模型解耦合。

(P794)

把輔助方法放在一個靜態類中,而且類名以 “Helper” 結尾可以讓你的代碼更易讀。

(P795)

標誌爲 readonly (只讀) 的字段只能在聲明或構造函數中修改。
**

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章