在軟件工程的發展歷史中,用于編寫計算機程序的方法經歷了幾次思維模式的重大轉變。每種思維模式都以前一種為基礎,宗旨都是增強代碼的組織,并降低復雜性。本書將帶領你體驗相同的思維模式轉變過程。
本書開始幾章會指導你學習順序編程結構。在這種編程結構中,語句按編寫順序執行。該結構的問題在于,隨著需求的增加,復雜性也會指數級增加。為降低復雜性,將代碼塊轉變成方法,從而產生了結構化編程模型。在這種模型中,可以從一個程序中的多個位置調用同一個代碼塊,而不需要復制代碼。但即使有這種結構,程序還是會很快變得臃腫不堪,需要進一步抽象。所以,在此基礎上人們又提出了面向對象編程的概念,這將在第6章中討論。在此之后,你將繼續學習其他編程方法,比如基于接口的編程和LINQ(以及它促使集合API發生的改變),并最終學習通過特性(attribute)進行初級的聲明性編程(第18章)。
【本書有以下三個主要職能】。
全面講述C#語言,其內容已遠遠超過了一本簡單的教程,可為你進行高效率軟件開發打下堅實基礎。
對于已熟悉C#的讀者,本書探討了一些較為復雜的編程思想,并深入討論了語言較新版本(C# 8.0和.NET Framework 4.8/.NET Core 3.1)的新功能。
它是你永遠的案頭參考—即便在你精通了這種語言之后。
成功學習C#的關鍵在于,要盡可能快地開始編程。不要等自己成為一名理論“專家”之后才開始寫代碼。所以不要猶豫,馬上開始寫程序吧。作為迭代開發思想的追隨者,我希望即使一名剛開始學習編程的新手,在第2章結束時也能動手編寫基本的C#代碼。
許多主題本書沒有討論。你在本書中找不到ASP.NET、Entity Framework、Xamarin、智能客戶端開發以及分布式編程等主題。雖然這些主題與.NET有關,但它們都值得用專門的書分專題講述。幸好市面上已經有豐富的圖書供讀者選擇。本書重點在于C#及基類庫中的類型。讀完本書之后,你在上述任何領域繼續深入學習都會游刃有余。
【本書面向的讀者】
寫作本書時,我面臨的一個挑戰是如何在持續吸引高級開發者眼球的同時,不因使用assembly、link、chain、thread和fusion等字眼而打擊初學者的信心,否則許多人會以為這是一本講冶金而不是程序設計的書。本書的主要讀者是已經有一定編程經驗,并想多學一種語言來“傍身”的開發者。但我還是小心地編排了本書的內容,使之對各種層次的開發者都有足夠大的價值。
初學者:假如你是編程新手,本書將幫助你從入門級程序員過渡為C#開發者,消除以后在面臨任何C#編程任務時的畏懼心理。本書不僅要教會你語法,還要教你養成良好的編程習慣,為將來的編程生涯打下良好基礎。
熟悉結構化編程的程序員:學習外語最好的方法就是“沉浸法”。類似地,學習一門計算機語言最好的方法就是在動手中學習,而不是等熟知了它的所有“理論”之后再動手。基于這個前提,本書最開始的內容是那些熟悉結構化編程的開發者很容易上手的。到第5章結束時,這些開發者應該可以開始寫基本的控制流程序。然而,要成為真正的C#開發者,記住語法只是第一步。為了從簡單程序過渡到企業級開發,C#開發者必須從對象及其關系的角度來思考問題。為此,第6章的“初學者主題”開始介紹類和面向對象開發。歷史上的C、COBOL和FORTRAN等結構化編程語言雖然仍在發揮作用,但作用會越來越小,所以,軟件工程師們應該逐漸開始了解面向對象開發。C#是進行這一思維模式轉變的理想語言,因為它本來就是基于“面向對象開發”這一中心思想來設計的。
熟悉“基于對象”和“面向對象”理念的開發者:C++、Python、TypeScript、Visual Basic和Java程序員都可歸于此類。對于分號和大括號,他們可是一點兒都不陌生!簡單瀏覽一下第1章的代碼,你會發現,從核心上講,C#類似于你熟知的C和C++風格的語言。
C#專家:對于已經精通C#的讀者,本書可供你參考不太常見的語法。此外,對于在其他地方強調較少的一些語言細節以及微妙之處,我提出了自己的見解。最重要的是,本書提供了編寫可靠和易維護代碼的指導原則及模式。另外,在你教別人學C#時,本書也頗有助益。
從C# 3.0到C# 8.0最重要的一些增強包括:
字符串插值(第2章)
隱式類型的變量(第3章)
元組(第3章)
可空引用類型(第3章)
模式匹配(第4章)
擴展方法(第6章)
分部方法(第6章)
默認接口成員(第8章)
匿名類型(第12章)
泛型(第12章)
Lambda語句和表達式(第13章)
表達式樹(第13章)
匿名類型(第15章)
標準查詢操作符(第15章)
查詢表達式(第16章)
動態編程(第18章)
用任務編程庫(TPL)和async進行多線程編程(第20章)
用PLINQ進行并行查詢處理(第21章)
并發集合(第22章)
考慮到許多人還不熟悉這些主題,本書圍繞它們展開了詳細的討論。涉及高級C#開發的還有“指針”這一主題,該主題將在第23章討論。即使是有經驗的C#開發者,也未必能很透徹地理解這一主題。
【本書特色】
本書是語言參考書,遵循核心C#語言規范(C# Language Specification)。為了幫助讀者理解各種C#構造,書中用大量例子演示了每一種特性,而且為每個概念都提供了相應的指導原則和最佳實踐,以確保代碼能順利編譯,避免留下隱患,并獲得最佳的可維護性。為增強可讀性,所有代碼均進行了特殊格式處理,而且每章內容都用思維導圖來概括。
【示例代碼】
請登錄華章網站(www.hzbook.com)或訪問https://IntelliTect.com/EssentialCSharp下載示例代碼。
【思維導圖】
每章開頭都有一幅“思維導圖”作為提綱,目的是為讀者提供針對每章內容的快速參考。
每章主題顯示在思維導圖的中心,高級主題圍繞中心展開。利用思維導圖,讀者可方便地搭建自己的知識體系,可以從一個主題出發,更清楚地理解其周邊的各個具體概念,避免中途糾纏于一些不相干的枝節問題。
【分類解說】
根據編程水平的不同,可以利用書中的標志來幫助自己輕松找到適合自己的內容。
初學者主題:特別針對入門級程序員提供的定義或解釋。
高級主題:可以讓有經驗的開發者將注意力放在他們最關心的內容上。
標注:用有底紋的標注框強調關鍵點,引起讀者的注意。
語言對比:分散在正文中的補充內容描述了C#和其他語言的關鍵差異,為熟悉其他語言的讀者提供指引。
【本書內容組織】
總體來說,軟件工程的宗旨就是管理復雜性。本書基于該宗旨來組織內容。第1章~第5章介紹結構化編程,學習這些內容后,可以立即開始寫一些功能簡單的代碼。第6章~第10章介紹C#的面向對象構造,新手應在完全理解這幾章的內容之后,再開始接觸本書其余部分更高級的主題。第12章~第14章介紹更多用于降低復雜性的構造,講解當今幾乎所有程序都要用到的通用設計模式。理解了它們之后,才可以更輕松地理解如何通過反射和特性來進行動態編程。后續章節將廣泛運用它們來實現線程處理和互操作性。
本書最后專門用一章(第24章)講解CLI。這一章在開發平臺的背景下對C#語言進行了描述。之所以要放到最后,是因為它非C#特有,且不涉及語法和編程風格問題。不過,該章適合在任何時候閱讀,或許最恰當的時機是在閱讀完第1章之后。
下面是每一章的內容提要。(章號加黑表明那一章含有C# 7.0和C# 8.0的內容。)
第1章:本章在展示了用C#寫的HelloWorld程序之后對其進行細致分析。目的是讓讀者熟悉C#程序的“外觀和感覺”,并理解如何編譯和調試自己的程序。另外,還簡單描述執行C#程序的上下文及其中間語言(Intermediate Language,IL)。
第2章:任何有用的程序都要處理數據,本章介紹C#的基元數據類型。
第3章:本章深入講解數據類型的兩大類別—值類型和引用類型。然后講解隱式類型局部變量、元組、可空修飾符以及C# 8.0引入的可空引用類型。最后深入討論基元數組結構。
第4章:計算機最擅長重復性操作,為利用該能力,需知道如何在程序中添加循環和條件邏輯。本章還討論C#操作符、數據轉換和預處理器指令。
第5章:本章討論方法及其參數的細節,其中包括通過參數來傳值、傳引用和通過out參數返回數據。C# 4.0新增了默認參數,本章將解釋如何使用。
第6章:前面已學過類的基本構成元素,本章合并這些構造,以獲得具有完整功能的類型。類是面向對象技術的核心,它定義了對象模板。本章還包括C# 8.0中新引入的可空屬性。
第7章:繼承是許多開發者的基本編程手段,C#更是提供了一些獨特構造,比如new修飾符。本章討論繼承語法的細節,其中包括重寫(overriding)。
第8章:本章討論如何利用接口來定義類之間的“可進行版本控制的交互契約”(versionable interaction contract)。C#同時包含顯式和隱式接口成員實現,可實現一個額外的封裝等級,這是其他大多數語言所不支持的。隨著默認接口成員的引入,該章有一節討論接口版本控制。
第9章:盡管不如定義引用類型那么頻繁,但有時確有必要定義行為和C#內置基元類型相似的值類型。本章介紹如何定義結構(struct),同時也強調其特殊性。
第10章:本章討論更高級的類型定義,解釋如何實現操作符,比如+和轉型操作符,并描述如何將多個類封裝到一個庫中。此外,還演示如何定義命名空間和XML注釋,并討論如何基于垃圾回收機制來設計令人滿意的類。
第11章:本章延伸討論第5章引入的異常處理機制,描述如何利用異常層次結構創建自定義異常。此外,還強調了異常處理的一些最佳實踐。
第12章:泛型或許是C# 1.0最缺少的功能。本章全面討論自2.0引入的泛型機制。此外,C# 4.0增加了對協變和逆變的支持,本章將在泛型背景中探討它們。
第13章:正因為有了委托,C#才與其前身語言(C和C++等)有了顯著不同,它定義了在代碼中處理事件的模式。這幾乎完全消除了寫輪詢例程的必要。Lambda表達式是使C# 3.0的LINQ成為可能的關鍵概念。通過學習本章,你將知道Lambda表達式是在委托的基礎上構建起來的,它提供了比委托更優雅和簡潔的語法。本章內容是后面討論的集合API的基礎。這里還介紹了另一個C# 2.0特性—匿名方法。
第14章:封裝起來的委托(稱為事件)是公共語言運行時(Common Language Runtime,CLR)的核心構造。
第15章:通過討論Enumerable類的擴展方法,介紹C# 3.0引入的一些簡單而強大的改變。Enumerable類造就了集合API,即“標準查詢操作符”,本章對其進行詳細討論。
第16章:如果只使用標準查詢操作符,會形成讓人難以辨認的長語句。查詢表達式提供了一種類似SQL風格的語法,有效解決了該問題。本章會詳細討論這種表達式。
第17章:構建用于操縱業務對象的自定義API時,經常需要創建自定義集合。本章討論了具體做法,還介紹了能使自定義集合的構建變得更簡單的上下文關鍵字。
第18章:20世紀80年代末,程序結構的思維模式發生了根本性的變化,面向對象的編程是這個變化的基礎。類似地,特性(attribute)使聲明性編程和嵌入元數據成為可能,因而引入了一種新的思維模式。本章探討特性的方方面面,并討論如何通過反射機制來獲取它們。本章還討論如何通過基類庫(Base Class Library,BCL)中的序列化框架來實現文件的輸入和輸出。C# 4.0新增了dynamic關鍵字,能將所有類型檢查都移至運行時進行,因而極大地擴展了C#的能力。
第19章:大多數現代程序都要求用線程執行長時間運行的任務,同時確保對并發事件的快速響應。隨著程序越來越復雜,必須采取其他措施來保護這些高級環境中的數據。多線程應用程序的編寫比較復雜。本章討論如何操縱線程,并提供一些最佳實踐來避免將多線程應用程序弄得一團糟。
第20章:本章深入研究基于任務的異步模式及其附帶的async/await語法。它為多線程編程提供了一種極其簡化的方法。此外,還包括異步流的C# 8.0概念。
第21章:引入性能改進的一種簡單方法是使用parallel對象或并行LINQ庫并行迭代數據。
第22章:本章以第21章為基礎,演示如何利用一些內建線程處理模式來簡化對多線程代碼的顯式控制。
第23章:必須意識到C#是一種相對年輕的語言,許多現有的代碼是用其他語言寫成的。為了用好這些現有代碼,C#通過P/Invoke提供了對互操作性(調用非托管代碼)的支持。此外,C#允許使用指針,也允許執行直接內存操作。雖然使用了指針的代碼要求特殊權限才能運行,但它具有與C風格的API完全兼容的能力。
第24章:事實上,C#被設計成一種在CLI頂部工作的最有效的編程語言。本章討論C#程序與底層“運行時”及其規范的關系。
希望本書能作為你建立C#專業能力的豐富資源,并且在精通C#后你仍能將其作為對較少使用的領域的參考。
第1章 C# 概述 / 1
1.1 Hello, World / 2
1.2 C#語法基礎 / 9
1.3 使用變量 / 16
1.4 控制臺輸入和輸出 / 18
1.5 托管執行和CLI / 24
1.6 多個.NET框架 / 28
1.7 小結 / 30
第2章 數據類型 / 31
2.1 基本數值類型 / 31
2.2 更多基本類型 / 38
2.3 數據類型轉換 / 49
2.4 小結 / 55
第3章 更多數據類型 / 56
3.1 類型的劃分 / 56
3.2 將變量聲明為可空 / 58
3.3 隱式類型的局部變量 / 62
3.4 元組 / 64
3.5 數組 / 68
3.6 小結 / 82
第4章 操作符和控制流程 / 83
4.1 操作符 / 83
4.2 控制流程概述 / 95
4.3 代碼塊 / 99
4.4 代碼塊、作用域和聲明空間 / 101
4.5 布爾表達式 / 102
4.6 關于null的編程 / 107
4.7 按位操作符 / 112
4.8 控制流程語句 / 116
4.9 跳轉語句 / 124
4.10 C#預處理器指令 / 129
4.11 小結 / 135
第5章 方法和參數 / 136
5.1 方法的調用 / 137
5.2 方法的聲明 / 141
5.3 using指令 / 145
5.4 Main()的返回值和參數 / 149
5.5 高級方法參數 / 151
5.6 遞歸 / 159
5.7 方法重載 / 162
5.8 可選參數 / 164
5.9 用異常實現基本錯誤處理 / 167
5.10 小結 / 178
第6章 類 / 179
6.1 類的聲明和實例化 / 182
6.2 實例字段 / 184
6.3 實例方法 / 186
6.4 使用this關鍵字 / 187
6.5 訪問修飾符 / 193
6.6 屬性 / 194
6.7 構造函數 / 207
6.8 不可空引用類型屬性與構造函數 / 214
6.9 可空特性 / 217
6.10 解構函數 / 219
6.11 靜態成員 / 220
6.12 擴展方法 / 228
6.13 封裝數據 / 229
6.14 嵌套類 / 231
6.15 分部類 / 233
6.16 小結 / 236
第7章 繼承 / 237
7.1 派生 / 238
7.2 重寫基類 / 244
7.3 抽象類 / 253
7.4 所有類都從System.Object派生 / 258
7.5 用is操作符進行模式匹配 / 259
7.6 switch語句中的模式匹配 / 264
7.7 避免對多態類對象使用模式匹配 / 265
7.8 小結 / 267
第8章 接口 / 268
8.1 接口概述 / 269
8.2 通過接口實現多態性 / 270
8.3 接口實現 / 274
8.4 在實現類和接口之間轉換 / 278
8.5 接口繼承 / 278
8.6 多接口繼承 / 280
8.7 接口上的擴展方法 / 281
8.8 版本升級 / 283
8.9 擴展方法與默認接口成員 / 295
8.10 比較接口和抽象類 / 296
8.11 比較接口和特性 / 297
8.12 小結 / 297
第9章 值類型 / 298
9.1 結構 / 301
9.2 裝箱 / 305
9.3 枚舉 / 311
9.4 小結 / 319
第10章 合式類型 / 321
10.1 重寫object的成員 / 321
10.2 操作符重載 / 331
10.3 引用其他程序集 / 337
10.4 類型封裝 / 342
10.5 定義命名空間 / 343
10.6 XML注釋 / 346
10.7 垃圾回收 / 349
10.8 資源清理 / 352
10.9 推遲初始化 / 363
10.10 小結 / 365
第11章 異常處理 / 366
11.1 多異常類型 / 366
11.2 捕捉異常 / 368
11.3 重新拋出現有異常 / 370
11.4 常規catch塊 / 371
11.5 異常處理規范 / 372
11.6 自定義異常 / 374
11.7 重新拋出包裝的異常 / 377
11.8 小結 / 380
第12章 泛型 / 381
12.1 如果C#沒有泛型 / 381
12.2 泛型類型概述 / 386
12.3 約束 / 395
12.4 泛型方法 / 407
12.5 協變性和逆變性 / 411
12.6 泛型的內部機制 / 416
12.7 小結 / 419
第13章 委托和Lambda表達式 / 420
13.1 委托概述 / 421
13.2 聲明委托類型 / 424
13.3 Lambda表達式 / 430
13.4 匿名方法 / 435
13.5 委托沒有結構相等性 / 436
13.6 外部變量 / 438
13.7 表達式樹 / 441
13.8 小結 / 447
第14章 事件 / 448
14.1 使用多播委托實現Publish-Subscribe模式 / 449
14.2 理解事件 / 460
14.3 小結 / 468
第15章 支持標準查詢操作符的集合接口 / 469
15.1 集合初始化器 / 470
15.2 IEnumerable使類成為集合 / 472
15.3 標準查詢操作符 / 476
15.4 匿名類型之于LINQ / 503
15.5 小結 / 509
第16章 使用查詢表達式的LINQ / 510
16.1 查詢表達式概述 / 510
16.2 查詢表達式只是方法調用 / 525
16.3 小結 / 526
第17章 構建自定義集合 / 528
17.1 更多集合接口 / 529
17.2 主要集合類 / 530
17.3 提供索引器 / 544
17.4 返回null或者空集合 / 547
17.5 迭代器 / 547
17.6 小結 / 559
第18章 反射、特性和動態編程 / 560
18.1 反射 / 560
18.2 nameof操作符 / 569
18.3 特性 / 571
18.4 使用動態對象進行編程 / 585
18.5 小結 / 592
第19章 多線程概述 / 594
19.1 多線程基礎 / 595
19.2 異步任務 / 600
19.3 取消任務 / 614
19.4 使用System.Threading / 618
19.5 總結 / 619
第20章 基于任務的異步模式編程 / 620
20.1 同步執行高延遲操作 / 621
20.2 使用TPL實現異步執行高延遲操作 / 623
20.3 使用async/await實現基于任務的異步模式 / 627
20.4 異步返回值ValueTask / 632
20.5 異步流 / 633
20.6 IAsyncDisposable接口以及await using聲明和語句 / 637
20.7 使用LINQ和IAsync-Enumerable / 637
20.8 返回void的異步方法 / 639
20.9 異步Lambda表達式和本地函數 / 642
20.10 任務調度器和同步上下文 / 647
20.11 在Windows UI程序中使用async/await / 649
20.12 小結 / 652
第21章 并行迭代 / 653
21.1 并行執行循環迭代 / 653
21.2 并行執行LINQ查詢 / 661
21.3 小結 / 666
第22章 線程同步 / 667
22.1 線程同步的意義 / 668
22.2 計時器 / 689
22.3 小結 / 691
第23章 平臺互操作性和不安全代碼 / 692
23.1 平臺調用 / 693
23.2 指針和地址 / 703
23.3 通過委托執行不安全代碼 / 710
23.4 小結 / 711
第24章 公共語言基礎結構 / 712
24.1 CLI的定義 / 712
24.2 CLI的實現 / 713
24.3 .NET Standard / 715
24.4 基類庫 / 716
24.5 將C#編譯成機器碼 / 716
24.6 運行時 / 717
24.7 程序集、清單和模塊 / 720
24.8 公共中間語言 / 722
24.9 公共類型系統 / 722
24.10 公共語言規范 / 723
24.11 元數據 / 723
24.12 .NET Native和AOT編譯 / 724
24.13 小結 / 724