C_Sharp_(程式語言)

**C#**是微軟推出的一種基於.NET 框架和後來的.NET 的、物件導向的進階程式語言。C# 是一種由 C 和 C++ 衍生出來的物件導向的程式語言。它在繼承 C 和 C++ 強大功能的同時去掉了一些它們的複雜特性，使其成為 C 語言家族中的一種高效強大的程式語言。C# 以.NET 框架類別程式庫作為基礎，擁有類似 Visual Basic 的快速開發能力。C# 由安德斯・海爾斯伯格主持開發，微軟在 2000 年發布了這種語言，希望藉助這種語言來取代 Java。C# 已經成為 Ecma 國際和國際標準組織的標準規範。

## 命名

C# 的發音為「C sharp」，「#」讀作「sharp」（/ʃɑːp/），命名啟發於音樂上的音名「**C♯**」，在音樂中「C♯」表示 C 升半音，為比 C 高一點的音節，且「#」形似 4 個加號，微軟藉助這樣的命名，以表示 C# 在一些語言特性方面對 C++ 的提升的意思。

由於顯示器（標準字體、瀏覽器等）的技術限制，且大部分的鍵盤配置上不存在升記號（♯），所以井號（#）被用於此程式語言的名稱中，約定在 ECMA-334 C# 語言規範中。

## 設計目標

ECMA 標準列出的 C# 設計目標：

* C# 旨在設計成為一種「簡單、現代、通用」，以及物件導向的程式設計語言
* 此種語言的實現，應提供對於以下軟體工程要素的支援：強型別檢查、陣列維度檢查、未初始化的變數參照檢測、自動垃圾收集（Garbage Collection，指一種記憶體自動釋放技術）。軟體必須做到強大、持久，並具有較強程式開發的生產力。
* 此種語言為在分散式環境中的開發提供適用的組件開發應用。
* 為使程式設計師容易遷移到這種語言，原始碼的可移植性十分重要，尤其是對於那些已熟悉 C 和 C++ 的程式設計師而言。
* 對國際化的支援非常重要。
* C# 適合為獨立和嵌入式的系統編寫程式，從使用複雜作業系統的大型系統到特定應用的小型系統均適用。
* 雖然 C# 程式在儲存和操作能力需求方面具備經濟性，但此種語言在某些情況下並不能在效能和程式大小方面與 C 語言相抗衡。

## 歷史

原 Borland 公司的首席研發設計師安德斯・海爾斯伯格（Anders Hejlsberg）在微軟開發了 Visual J++ 1.0，很快的 Visual J++ 由 1.1 版本升級到 6.0 版。SUN 公司認為 Visual J++ 違反了 Java 開發平台的中立性，對微軟提出了訴訟。2000 年 6 月 26 日微軟在奧蘭多舉行的「職業開發人員技術大會」（PDC 2000）上，發表新的語言 C#。C# 語言取代了 Visual J++，語言本身深受 Visual Basic、Java、C 和 C++ 的影響。

### 版本

## 語言特性

相對於 C 和 C++，這個語言在許多方面進行了限制和增強：

* 指標（Pointer）只能用於不安全模式之中。大多數物件存取透過安全的參照實現，以避免無效的調用，並且有許多演算法用於檢查溢位，指標只能用於調用值類型，以及受垃圾收集控制的代管物件。
* 物件不能被顯式釋放，代替為當不存在被參照時透過垃圾回收器回收。
* 只允許單一繼承（single inheritance），但是一個類可以實現多個介面（interfaces）。
* C# 比 C++ 更加類型安全。預設的安全轉換是隱含轉換，例如由短整型轉換為長整型和從衍生類轉換為基本類。而介面布林數同整型，及枚舉型同整型不允許隱含轉換，非空指標（透過參照相似物件）同使用者定義類型的隱含轉換欄位被顯式的確定，不同於 C++ 的複製構造函式。
* 陣列宣告的語法不同（"int [] a = new int [5]" 而不是 "int a [5]"）。
* 枚舉位於其所在的命名空間中。
* C# 中沒有模版（Template），但是在 C# 2.0 中引入了泛型（Generic programming），並且支援一些 C++ 模版不支援的特性。比如泛型參數中的類型約束。另一方面，表達式不能像 C++ 模版中被用於類型參數。
* 屬性支援，使用類似存取成員的方式調用。
* 完整的反射支援。

## C# 2.0 的特性

針對於.NET SDK 2.0（相對應於 ECMA-334 標準第三版），C# 的新特性有：

### 部分類

分部類別將類別的實現分在多個檔案中。該概念於 C# 中首次出現，除了能將一個類別的成員分開存放，還使 ASP.NET 中的代碼後置得以實現。代碼後置實現了 HTML 代碼和後台互動代碼的分離。

file1.cs:

file2.cs:

分部類別這個特性允許將一個類別的編寫工作分配給多個人，一人寫一個檔案，便於版本控制。它又可以隔離自動生成的代碼和人工書寫的代碼，例如設計表單應用程式時。

### 泛型

泛型，或參數化類型，是被 C# 支援的.NET 2.0 特性。不同於 C++ 模版，.NET 參數化類型是在執行時被實例化，而不是編譯時，因此它可以跨語言，而 C++ 模版卻不行。C# 泛型類在編譯時，先生成中間代碼 IL，通用類型符號 T 只是一個預留位置；在實例化類時，根據實際資料類型代替 T 並由即時編譯器（JIT）生成本地代碼，其中使用了實際的資料類型，等同於用實際類型寫的普通的類。

它支援的一些特性並不被 C++ 模版直接支援，比如約束泛型參數實現一個介面。另一方面，C# 不支援無類型的泛型參數。不像 Java 中的泛型，在 CLI 虛擬機器中，.NET generics 使用具化生成泛型參數，它允許最佳化和儲存類型資訊。

泛型類中，可以用 where 關鍵字對參數類型實現約束。例如：

上述表示 T 和 V 必須是 Stack 類或其衍生類別，T 必須繼承了 IComparable 介面、有無參建構函式、是參照類型；V 必須是值類型。

泛型不僅能作用在類上，也可單獨用在類的方法上，稱為「泛型方法」。

泛型類的靜態成員變數在相同封閉類間共享，不同的封閉類間不共享。

泛型類中的方法多載，參數類型 T 和 V 在執行時確定，不影響這個類通過編譯。C# 的泛型是在實例的方法被呼叫時檢查多載是否產生混淆，而不是在泛型類本身編譯時檢查。特別地，當一般方法與泛型方法具有相同的簽章時，會覆蓋泛型方法。

### 靜態類別

靜態類別它不能被實例化，並且只能有靜態成員。這同很多過程語言中的模組概念相類似。

### 迭代器

一種新形式的迭代器它提供了函式語言程式設計中的 generator，使用 `yield return`

類似於 Python 中使用的 `yield`

注意事項：

* foreach 迴圈時考慮執行緒安全性，不要試圖對被遍歷的集合進行 remove 和 add 等操作
* IEnumerable 介面是 LINQ 特性的核心介面。只有實現了 IEnumerable 介面的集合，才能執行相關的 LINQ 操作，比如 select,where 等

### 匿名方法

匿名方法類似於函式語言程式設計中的閉包。匿名方法是通過使用 delegate 關鍵字建立委託實例來聲明的。例如：

### 委託的協變和逆變

委託簽章的協變和逆變，

### 屬性存取器可以被單獨設定存取級別

例子：

### 可空類型

可空類型（跟個問號，如 `int? i = null;`）允許設定 `null` 給任何類類型。

### ?? 運算子

（`??`）：如果左運算數表達式的值不為空值時回傳該值，如果為空值則返回右運算數表達式的值。

主要用作將一個可空類型賦值給不可空類型的簡便語法

## C# 3.0 的特性

C# 3.0 發布於 2007 年 10 月 17 日，是.NET Framework 3.5 的一部分，它的新特性靈感來自於函式語言程式設計語言，如：Haskell 和 ML，並廣泛地引入了 Language Integrated Query（LINQ）模式到通用語言運行庫中 e.

### Linq

語言整合查詢（英語：**L**anguage **In**tegrated **Q**uery，縮寫：**LINQ**）： 上下文相關關鍵字 "`from`, `where`, `select`" 可用於查詢 SQL、XML、集合等。這些識別碼在 LINQ 上下文中被作為關鍵字，但是它們的增加不會破壞原有的名為 `from`、`where` 或 `select` 的變數。

### 類型初始化器

可寫作：

### 集合初始化器

可寫作

假設 `MyList` 實現了 `System.Collections.IEnumerable` 且有一個 `Add` 方法 method

### 匿名類型

### 局部變數類型推斷

局部變數類型推斷：

等同於

它只是一個語法糖，這個特性被匿名類型聲明時所需要

### Lambda 表達式

Lambda 表達式（無函式名稱的物件方法在程式語言中的表達語法）:

　
: 可寫作

編譯器翻譯 Lambda 表達式為強型別委託或強型別表達式樹。

注意事項：

* 如果只有一個參數，可以省略括號 ()，例如 item=>{Console.WriteLine ("只有一個參數 {0} 的 Lambda 表達式",item); };
* 如果只有一個返回值的語句，可以省略花括號 {}、return 關鍵字、分號，例如 item => {return item % 2 == 0;}; 覆寫成：item =>item %2 == 0;
* Lambda 表達式可以分配給 Func，Action 或 Predicate 委託。

### 自動化屬性

編譯器將自動生成私有變數和適當的 getter（get 存取器）和 setter（set 存取器），如：

### 擴充方法

擴充方法能夠使現有的類型添加方法，而無需建立心的衍生類型、重新編譯或以其它方式修改原始類型。

使用拓展方法，必須在一個非巢狀、非泛型的靜態類中定義一個靜態方法，方法第一個參數必須附加 this 關鍵字作為字首，第一個參數不能有其它修飾詞（如 ref 或者 out），這個方法將被編譯器添加到該 this 的類型中。

注意事項：

* 擴充方法只會增加編譯器的工作，但不會影響程式執行效能（用繼承的方式為一個類型增加特性反而會影響效能）
* 如果原來的類中有一個方法，跟擴充方法一樣，那麼擴充方法不會被呼叫，編譯器也不會提示

### 分部方法

允許碼產生器生成方法聲明作為擴充點，如果有人在另一個部分類實現了它才會被包含於原代碼編譯。

1. 分部方法（Partial methods）必須定義在分部類（partial classes）中
2. 定義分部方法需要用 partial 做修飾詞
3. 分部方法不一定總是有執行內容的，也就是說定義的方法可以一句操作語句都沒有
4. 分部方法返回值必須是 void
5. 分部方法可以是靜態（static）方法
6. 分部方法可以包含參數，參數可以包含以下修飾詞：this,ref,params
7. 分部方法必須是私有（private）方法

例子：

## C# 4.0 的特性

### dynamic 類型

C# 4.0 新增 dynamic 關鍵字，提供動態編程（dynamic programming），把既有的靜態物件標記為動態物件，類似 javascript, Python 或 Ruby。

dynamic 關鍵字標記的實例被處理成一個特殊包裝的 object 物件，取消了 CLI 的編譯時型別檢查，編譯時被假定支援任何操作，但如果並不實際支援則執行時報錯。

### 具名參數與可選參數

呼叫 OpenFile 時，順序可以完全顛倒：

### 與 COM 組件互動

在 C# 中打開一個 Word 檔案：

在 C# 中指定 Excel 的某一格文字：

### 泛型的協變和逆變

C# 4.0 支援協變和逆變，例如在泛型介面可以加上 in、out 修饰字。

## C# 5.0 的特性

1. C# Evolution Matrix
2. Async Feature （補充： async 和 await 是一對語法糖，允許開發人員非常輕鬆的呼叫基於 TASK 的非同步編程）async-await 關鍵字並不會真的建立一個執行緒池任務，完成這個動作依賴於被呼叫方法中的函式。這一點在許多 C# 的中文教程中被忽略，導致許多學習的新手誤以為 await 關鍵字會直接建立一個新的執行緒池任務。
3. Caller Information

## C# 6.0 的特性

1. 唯讀 Auto 屬性
2. Auto 屬性初始設定式
3. 使用靜態
4. Null - 條件運算子
5. 字串插值
6. 例外狀況篩選條件
7. nameof 運算式
8. Catch 和 Finally 區塊中的 Await
9. 索引初始設定式
10. 集合初始設定式的擴充方法
11. 改進的多載解析

### 表達式主體（Expression-bodied）用於類的方法和唯讀屬性

## C# 7.0 的特性

### out 變數

能夠直接宣告一個變數在它要傳入的地方，當成一個 out 的引數

### 棄元

元組 / 物件的解構：

使用 is/switch 的模式匹配：

對具有 out 參數的方法的呼叫：

作用域內獨立使用場景：

### 表達式主體（Expression-bodied）用於類的屬性、構造器、終端子、索引器

## C# 7.1 的特性

1. async``Main 方法
2. default 常值運算式
3. 推斷的 tuple 項目名稱

## C# 7.2 的特性

1. 具備實值型別的參考語意
2. 無後置具名引數
3. 數值常值中的前置底線
4. private protected 存取修飾詞

## C# 8.0 的特性

1. 可空參照類型
2. await yield return 可非同步返回的迭代器
3. Index 索引類型和 Range 區間類型
4. 允許在聲明介面時為介面成員提供預設實現
5. 遞迴的模式匹配
6. 表達式形式的 Switch 關鍵字
7. 在編譯器可做類型推斷的情況下，允許進一步省略類型聲明

## C# 9.0 的特性

### 新的「Record」類型

記錄類型， 是一種參照類型， 預設是不可變的。 記錄類型的相等判斷可以通過參照或者結構進行判斷的。

* 優點：記錄類型是輕量級的不可變類型，可以減少大量的代碼， 可以按照結構和參照進行比較；
* 缺點：需要實例化大量的物件；

### 「init」存取子

init 存取子表示該屬性所屬類型僅能在建構函式 (Constructor) 中或是屬性初始化式子中賦予其值，如果嘗試在其他地方設定該屬性的值，在編譯時便會遭編譯器阻止。

範例如下：在這個範例中，建立了一個 `Student` 類型，並且屬性 `StudentName` 與 `StudentID` 只能在初始化時賦予其值。

如果在此時撰寫以下程式碼：

編譯器便會無法編譯並且擲回錯誤。

而如果要建立學生名稱為「Test Name」，學生 ID 為「0001」的學生，則需要寫成：

或是

### 最上層語句或稱頂級語句

在以前的版本，開發者在撰寫最上層語句 (如 Program.cs) 程式碼時，需要包含完整的 namespace 與 class 架構，因此如果要撰寫 Hello World 程式時，程式碼就會是：

但是在 C# 9.0 之後，最上層語句的程式碼不需要包含 namespace 以及 class，可將其簡化為：

注意， 一個程式中， 只能有一個檔案使用頂級語句， 並且頂級語句必須位於命名空間或類型定義之前。

### lambda 棄元參數

在 C# 9 之前，即便不使用的 Lambda 參數也需要給它命名。C# 9 支援棄元參數一方面簡化了命名，另一方面也節省了記憶體分配。更重要的是它使得編程的意圖更明確，讓人一看就知道這個參數是不用的，增強了代碼的可讀性和可維護性。

### 只能初始化的設定器

Init only setters，只能通過物件初始化進行賦值的屬性。

### 函式指標

使用 delegate\* 可以聲明函式指標。

### 跳過本地初始化

### 原生整數類型

兩個新的整數類型 nint 和 nunit ， 依賴宿主機以及編譯設定。

### 協變返回類型

協變返回類型為重寫方法的返回類型提供了靈活性。覆蓋方法可以返回從被覆蓋的基礎方法的返回類型衍生的類型。

### 模組初始化代碼

ModuleInitializerAttribute 為組件 (assembly) 定義初始化代碼， 當初始化 / 載入時執行， 可以類比類的靜態建構函式， 但是是組件級別的。

* 必須是靜態的、無參數的、無返回值的方法；
* 不能是範式方法，也不能包含在範式類中；
* 不能是私有函式，必須是公開 (public) 或者內部 (internal) 的函式；

### 靜態 lambda 表達式

static 修飾詞添加到 lambda 表達式或匿名方法 。這將無法擷取局部變數或實例狀態，從而防止意外擷取其他變數。

### 分部方法擴充

移除了分部方法的下述限制：

* 必須具有 void 返回類型。
* 不能具有 out 參數。
* 不能具有任何可存取性（隱式 private ）。

### 初始化表達式的簡化

如果建立物件的類型已知時，可以在 new 表達式中省略該類型。

### 在本地函式上添加標記

### GetEnumerator 擴充

可以為任意類型添加一個 GetEnumerator 擴充方法， 返回一個 IEnumerator 或者 IAsyncEnumerator 實例， 從而在 foreach 迴圈中使用。

### 模式匹配增強

Type patterns 類型匹配，判斷一個變數的類型

Relational patterns 關係匹配：

Conjunctive and patterns 邏輯合取匹配：

Disjunctive or patterns 邏輯析取匹配：

Negated not patterns 邏輯非匹配

Parenthesized patterns 帶括號的優先級匹配：

## C# 10.0 的特性

### record struct

解決了 record 只能給 class 而不能給 struct 用的問題：

### sealed record ToString 方法

可以把 record 里的 ToString 方法標記成 sealed

### struct 無參建構函式

無參建構函式使得 new struct () 和 default (struct) 的語意不一樣

### 用 with 建立新的匿名類型物件

這裡 y.A 將會是 3 。

### 全域的 using

可以給整個專案啟用 using，不需要每個檔案都寫一份。

### 檔案範圍的 namespace

以前寫 namespace 還得帶一層大括號。現在如果一個檔案里只有一個 namespace 的話，直接在檔案開頭寫：`namespace MyNamespace;`

### 常數字串插值

### lambda 的改進

#### lambda 可以帶 attributes

#### 指定返回值類型

此前 C# 的 lambda 返回值類型靠推導，C# 10 允許在參數列之前顯式指定 lambda 返回值類型：

```
f = int () => 4;
```

#### 支援 ref 、in 、out 等修飾

```
f = ref int (ref int x) => ref x; // 返回一个参数的引用
```

#### 頭等函式

函式可以隱式轉換到 delegate，於是函式上升為頭等函式 (first function)：

#### 自然委託類型

lambda 可自動建立自然委託類型，於是不再需要寫出類型：

#### CallerArgumentExpression

使用 CallerArgumentExpression 這個 attribute，編譯器會自動填充呼叫參數的表達式字串，例如：

當你呼叫 Foo (4 + 5) 時，會輸出 4 + 5 = 9。這對測試框架極其有用

### tuple 的混合定義和使用

於是 y 就變成 2 了，同時還建立了兩個變數 x 和 z，分別是 1 和 3 。

### 介面支援抽象靜態方法

.NET 6 中這個特性為 preview 特性。

### 泛型 attribute

### 在方法上指定 AsyncMethodBuilder

在方法上用 [AsyncMethodBuilder (...)]，來使用自己實現的 async method builder，代替內建的 Task 或者 ValueTask 的非同步方法構造器。有助於實現零開銷的非同步方法。

### line 指示器支援行列和範圍

以前 #line 只能用來指定一個檔案中的某一行，現在可以指定行列和範圍：

### 巢狀屬性模式匹配改進

以前在匹配巢狀屬性的時候需要這麼寫：

```
if (a is { X: { Y: { Z: 4 } } }) { ... }
```

現在只需要簡單的：

```
if (a is { X.Y.Z: 4 }) { ... }
```

### 改進的字串插值

實現接近零開銷的字串插值。

### Source Generator v2

包括強型別的代碼構建器，以及增量編譯的支援等

## 程式的執行

C# 通常不被編譯成為能夠直接在電腦上執行的二進位本地代碼。與 Java 類似，它被編譯成為中間代碼（Microsoft Intermediate Language），然後通過.NET Framework 的虛擬機器 —— 被稱為通用語言執行層 —— 執行。

所有的.Net 程式語言都被編譯成這種被稱為通用中間語言的中間代碼。因此雖然最終的程式在表面上仍然與傳統意義上的可執行檔都具有「.exe」的字尾名。如果電腦上沒有安裝.Net Framework，那麼這些程式會彈出對話方塊，要求使用者下載.net framework。

在程式執行時，.Net Framework 將中間代碼翻譯成為二進位機器碼，從而使它得到正確的執行。最終的二進位代碼被儲存在一個緩衝區（Buffer）中。所以一旦程式使用了相同的代碼，那麼將會呼叫緩衝區中的版本。這樣如果一個.Net 程式第二次被執行，那麼這種翻譯不需要進行第二次，速度明顯加快。

## 標準化

微軟公司已經向 ECMA 申請將 C# 作為一種標準。在 2001 年 12 月，ECMA 發布了 ECMA-334 C# 語言規範。C# 在 2003 年成為一個 ISO 標準（ISO/IEC 23270）。現在有一些獨立的實現正在進行，包括：

* 自由軟體基金會的 dotGNU Portable.NET（頁面存檔備份，存於網際網路檔案館）
* Mono（頁面存檔備份，存於網際網路檔案館）
* Baltie - C# IDE for children and young Baltie（頁面存檔備份，存於網際網路檔案館）

## 範例

### C# 的 Hello World 程式

下面是一個在命令提示字元上輸出 Hello World 的小程式，這種程式通常作為開始學習程式語言的第一個步驟：

## 實現

微軟正在引領開源參考 C# 編譯器和工具集的開發。 第一個編譯器 Roslyn 編譯成中間語言（IL），第二個編譯器 RyuJIT， 是一個 JIT（即時）編譯器，它是動態的，進行動態最佳化並編譯將 IL 轉換為 CPU 前端的本機代碼。 RyuJIT 是開源的，用 C++ 編寫。 Roslyn 完全是用 受控代碼 (C#) 編寫的，已經開放並且功能以 API 的形式出現。因此，它使開發人員能夠建立重構和診斷工具。 官方實現的兩個分支是 .NET Framework（閉源，僅限 Windows）和 .NET Core（開源，跨平台）；它們最終融合為一個開源實現：.NET 5.0。 在 .NET Framework 4.6 中，新的 JIT 編譯器取代了前者。

其他 C# 編譯器（其中一些包括公共語言基礎結構和 .NET 類別程式庫的實現）：

* 微軟的轉軸專案（Rotor Project，目前稱為 Shared Source Common Language Infrastructure），提供了通用語言執行層（_Common Language Runtime_）的實作與 C# 編譯器。但是 Shared Source Common Language Infrastructure 在 2006 年的 2.0 版後就停止了。
* 由 Microsoft 贊助的 Mono 專案提供了 C# 編譯器，它提供了一個開源 C# 編譯器、一個完整的 CLI 開源實現，同時也接近百分之百地實作了.NET Framework 類別庫。而 Mono 後來衍伸出由微軟認可的第三方套件 Xamarin。
* Dot GNU 專案（現已停產）也提供了另一個自由版本的 C# 編譯器，也提供了.NET Framework 類別庫的實作。

遊戲引擎 Unity 使用 C# 作為其主要手稿語言。由於 Microsoft 捐贈了 24,000 美元， Godot 遊戲引擎實現了一個可選的 C# 模組。

## 參考文獻

## 外部連結

* （英文）C# Language Specification (MSDN)（頁面存檔備份，存於網際網路檔案館）
* （英文）ECMA-334 C# Language Specification (.pdf)
* （英文）ISO/IEC 23270:2003 C# Language Specification（頁面存檔備份，存於網際網路檔案館）
* （英文）Visual Studio Code（頁面存檔備份，存於網際網路檔案館）
* （英文）Download .NET（頁面存檔備份，存於網際網路檔案館）
* Visual C# .NET 入門 (MSDN)（頁面存檔備份，存於網際網路檔案館）
* MCS: Mono C# 編譯器（頁面存檔備份，存於網際網路檔案館）
* Visual Studio Community（頁面存檔備份，存於網際網路檔案館）
* Visual C# Logo（頁面存檔備份，存於網際網路檔案館）
* C# 教學（頁面存檔備份，存於網際網路檔案館）
* C# 教材目錄（頁面存檔備份，存於網際網路檔案館）