Try   HackMD

版本整理摘要

下载 .NET

下载 .NET 版本

版本整理圖表

C#版本 釋出時間 .NET 版本 VS版本
C#1.0 2002-02-13 .NET Framework 1.0 VS.NET 2002
C#1.1 C#1.2 2003-04-24 .NET Framework 1.1 VS.NET 2003
C#2.0 C#3.0 2005-11-07 .NET Framework 2.0 VS2005
C#3.0 2006-11 .NET Framework 3.0 VS2008
C#3.0 (導入LinQ) 2007-11-19 .NET Framework 3.5 VS2008
C#4.0 2010-4-12 .NET Framework 4.0 VS2010
C#5.0 2012-02-20 .NET Framework 4.5 VS2012
C#5.0 2013-10-17 .NET Framework 4.5.1 VS2013
C#5.0 2014-05-05 .NET Framework 4.5.2 VS2013
C#6.0 2015-07-26 .NET Framework 4.6 VS2015(v14)
C#6.0 2015-11-30 .NET Framework 4.6.1 VS2015(v14)
C#7.0 2016-08-02 .NET Framework 4.6.2 VS2017(v15)
C#7.1 2017-04-05 .NET Framework 4.7 VS2017(v15.3)
C#7.2 2017-10-17 .NET Framework 4.7.1 VS2017(v15.5)
C#7.3 2018-04-30 .NET Framework 4.7.2 (對應 .Net Core 2.X) VS2017(v15.7)
C#8 2019-04-18 .NET Framework 4.8 (對應 .Net Core 3.X) VS2019(v16)
C#9 2020-11 .NET 5.0 VS2019(16.8)
C#10 2021年11-08 .NET 6.0 ~ 6.0.36 VS2022(17.0)
C#11 2022-11-8 .Net 7.0.0 ~7.0.20 VS2022(17.4)
C#12 2023年11月14日 .Net 8.0.0
C#13 2024年12月3日 .Net 9.0.0

C# 1.0 版

僅提供最基本的功能 , 此版本連泛型都沒有了 , 就更不用說 LinQ 了

C# 2.0 版

開始支援泛型(Generic)

  • Generics(泛型)
  • Partial Classes
    • 將 class、struct、interface,分割到兩個以上的來源檔案。 每一個來源檔案都包含型別或方法定義的一個區段,而當編譯應用程式時,就會將所有區段結合起來。

  • Delegate Operator
    • 使用 delegate 關鍵字宣告匿名方法.
    • 匿名方法的用法已經被 C# 3.0 的 Lambda 給取代了.
  • Nullable Value Types
    • 實質型別 可以使用 Nullable Type (可以被指派一個 null)
      • e.g. int? i = null;
  • Null Coalescing Operator ??
    • e.g.
      ​​​​​​​​int? i = null;
      ​​​​​​​​// 當 i 是 null 時 , 
      ​​​​​​​​// 則繼續查找 ?? 右邊的字符是否為 null , 若否 , 則回傳該值. 因此 j=3
      ​​​​​​​​int j = i ?? 3; 
      
  • Iterators
  • Covariance & Contravariance
  • Accessor Accessibility
    • 此版本開始可以設定 Accessor 的存取權限
    • ​​​​​​​​public class MyClass
      ​​​​​​​​{
      ​​​​​​​​    private int _myField = 5;
      ​​​​​​​​    public int MyProperty
      ​​​​​​​​    {   // get set 皆是 accessor
      ​​​​​​​​        // 設定 set 的存取權限為 protected
      ​​​​​​​​        get { return _myField; } 
      ​​​​​​​​        protected set { _myField = value; }
      ​​​​​​​​    }
      ​​​​​​​​}
      
  • Static Class
  • is Operation
    • is 運算子檢查運算式的執行階段型別是否與給定型別相容 (可檢查 enum、字符和類別)
    • ​​​​​​var o = new object();
      ​​​​​​bool isA = o is A(ClassName); // 回傳 True or False`
      
    • C# 7.0 模式比對, 會再進一步擴充
  • as Operation
    • as 運算子將運算式的結果明確地轉換成給定參考或可為 Null 的實值型別。 如果無法轉換,則 as 運算子會傳回 null

    • object as ClassName // 回傳 ClassName 型別的物件 or null
  • ThreadPool
    Queues a method for execution. The method executes when a thread pool thread becomes available.
    • QueueUserWorkItem(WaitCallback) 將方法排入佇列,以等候執行。 可以使用執行緒集區執行緒時,即可執行這個方法。
    • QueueUserWorkItem(WaitCallback, Object) 將方法排入佇列,以等候執行,並指定包含這個方法所要使用之資料的物件。 可以使用執行緒集區執行緒時,即可執行這個方法。
    • QueueUserWorkItem<TState>(Action<TState>, TState, Boolean) 將 Action<T> 委派指定的方法排入佇列以便執行,並提供將由方法使用的資料。 可以使用執行緒集區執行緒時,即可執行這個方法。

C# 3.0 版

引用函數式語言設計的概念 , LINQ 在此版被導入 , 不嫌棄 , 請參考我的筆記

  • Property Auto Implemented
    • 編譯器自動生成私有變數給屬性使用(精確說法是給 Getter or Setter 使用)
    • ​​​​​​​​// 舊的寫法
      ​​​​​​​​private string _property = string.Empty;
      ​​​​​​​​public string Property
      ​​​​​​​​{
      ​​​​​​​​    get { return _property; }
      ​​​​​​​​    set { _property = value; }
      ​​​​​​​​}
      ​​​​​​​​// 新的寫法
      ​​​​​​​​public string Property { get; set; }
      
  • Anonymous Types
    • 使用 new 關鍵字 , 可以簡單建立匿名型別. 此型別的物件不需要事先明確定義類別名稱 , 類別名稱是由編譯器產生 , 並且每個屬性的型別會由編譯器推斷.
    • ​​​​​​// 建立匿名型別物件 , 其具有屬性為 Name , Age
      ​​​​​​var value = new { Name = "王曉明", Age = 5 };
      ​​​​​​// 建立匿名型別陣列 , 其每一個成員(物件)都具有屬性為 Name , Age
      ​​​​​​var values = new[] { new { Name = "周論發", Age = 15 }, 
      ​​​​​​                     new { Name = "小龍女", Age = 65 } 
      ​​​​​​                   };
      
  • Query Expression
    • LinQ 的 Query 語法
  • Expression Trees
    • 解析 LinQ 方法的東西. 我不會XD
  • Object And Collection Initializers
    • ​​​​​​​​// 物件初始舊的寫法
      ​​​​​​​​Person p = new Person();
      ​​​​​​​​p.Name = "City";
      ​​​​​​​​// 新的寫法
      ​​​​​​​​Person p = new Person{ Name = "City" }
      
    • ​​​​​​// 集合初始舊的寫法
      ​​​​​​List<int> list = new List<int>();
      ​​​​​​list.Add(1);
      ​​​​​​list.Add(2);
      ​​​​​​// 新的寫法
      ​​​​​​var list = new List<int>() { 1 , 2 };
      
  • Var 變數
    • 變數型別推斷
    • var s = "小名" // 自動推斷變數 s 為 string
  • Lambda
  • Extension Method
    • 一個靜態類別內的方法 , 若此方法的第一個參數使用 this 關鍵字作為前綴詞時 , 該方法將會被視為是 this 關鍵字後所指的類別之中的方法.
  • Partial Method
    • 允許在 Partial Class 內僅定義方法簽章 , 然後在另外的相同名稱的 Partial Class 內實作.
    • 類似於 C++ 的 .h 檔案以及 .cpp 檔案的感覺.

C# 4.0 版

主要 Feature 為 Dynamic 關鍵字的導入.

  • Dynamic Type
  • Tuple
    • 此版本的元祖名稱只能是 Item1 , Item2 非常之不好用. 需要等到 C#7.0 才解禁.
  • Method Support Named Arguments
    • 具名引數僅需指名方法參數名稱 , 則可無視方法簽章的參數順序 , 傳入參數
    • ​​​​​​​​public static void CityMethod(string name, int age)
      ​​​​​​​​{
      ​​​​​​​​    Console.WriteLine($"{name}的年紀是 {age} 歲");
      ​​​​​​​​}
      
      ​​​​​​​​public static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    // 即使傳入參數位置順序相反 , 但只要有指名參數名稱 , 仍可正確傳入參數
      ​​​​​​​​    CityMethod(age: 29, name: "楊過");
      ​​​​​​​​    Console.ReadKey();
      ​​​​​​​​}
      
  • Method Support Optional Arguments
    • 選擇性引數 : 允許參數設定預設值
    • ​​​​​​​​public static void CityMethod(string name, int age = 15)
      ​​​​​​​​{
      ​​​​​​​​    Console.WriteLine($"{name}的年紀是 {age} 歲");
      ​​​​​​​​}
      
      ​​​​​​​​public static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    // 若未傳入參數 age , 則使用預設的 15
      ​​​​​​​​    CityMethod("楊過");
      ​​​​​​​​    Console.ReadKey();
      ​​​​​​​​}
      
  • Support COM Element
    • 支援與 COM 組件互動 , e.g. 打開 Word 檔案
  • Covariance and Contravariance of Generics
    • 在泛型介面或類別可以加上 in & out 修饰字
  • Task
    • Task.Start
      ​​​​var task = new Task(Action);
      ​​​​task.Start();  // 開始執行 Action
      
    • Task.Wait
      ​​​​var task = new Task(Action);
      ​​​​task.Start(); 
      ​​​​task.Wait();  // Block UI 等待 task 完成
      
    • Task.TaskFactory
      ​​​​// 回傳 "不用 call Start , 就會執行 Action" 的 task
      ​​​​var task = Task.Factory.StartNew(Action); 
      
    • Task.ContinueWith
      • 在能用 await 的地方盡量用await , 別再使用 Task.ContinueWith() <- 過時了
      ​​​​private void Button1_Click(object sender, EventArgs e)  
      ​​​​{  
      ​​​​   var backgroundScheduler = TaskScheduler.Default;  
      ​​​​   var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();  
      ​​​​   Task.Factory.StartNew(delegate { DoBackgroundComputation(); },  
      ​​​​                         backgroundScheduler).  
      ​​​​   ContinueWith(delegate { UpdateUI(); }, uiScheduler).  
      ​​​​                ContinueWith(delegate { DoAnotherBackgroundComputation(); },  
      ​​​​                             backgroundScheduler).  
      ​​​​                ContinueWith(delegate { UpdateUIAgain(); }, uiScheduler);  
      ​​​​}                                                         
      
  • Task<Result>
    使用上 , 原則上跟 Task 一樣 , 只是多了回傳值
    • Task<TResult>.Result
      ​​​​var task = Task.Factory.StartNew(()=>"result data");
      ​​​​var result = task.Result;
      

C# 5.0 版

參考基於 Task 的非同步設計的概念 , async 和 await 這兩個關鍵字被導入. 印象中 C# 是最早使用這兩個關鍵字的語言.

  • Task.Run()
  • async & await
    • async 關鍵字
      • 保證此方法的回傳值必定是 Task 型態
      • 保證此方法必定會使用 await 關鍵字
      • 使用 async 不代表程式必定為非同步執行或是新 thread 的產生 , 需要看方法內部的實作. 例如 : 該方法可能使用 Task.FromResult(TResult result) 將結果轉成 Task. 則此實作為同步執行 , 所以也不會產生新 Thread.
    • await 關鍵字
      • await 代表程式會在此處紀錄 , 並承諾當非同步的結果完成時 , 會從記錄點開始繼續執行.
        • 再執行到 await 處時 , 當前 Thread 資源可能會被釋放 , 但通常是會跳回母涵式處繼續往下執行. 這由 OS 決定
        • 當非同步結果完成時 , 不一定由原本的 Thread 繼續往下執行 , 由 OS 依據當時 Thread Pool 內的資源決定.
      • await 並不一定會新增一個 thread . 此依據被呼叫方法內部的實作是否有新增一個 thread 去執行.
  • Caller Information
    • 屬性 描述 類型
      CallerFilePathAttribute 包含呼叫端的原始程式檔完整路徑。 完整路徑是在編譯時期的路徑。 String
      CallerLineNumberAttribute 原始程式檔中呼叫方法的行號。 Integer
      CallerMemberNameAttribute 呼叫端的方法名稱或屬性名稱。 String
      CallerArgumentExpressionAttribute 引數運算式的字串表示。 String
    • 使用 Caller Info 屬性,您就可以取得有關方法之呼叫端的資訊。 您可以取得原始程式碼的檔案路徑原始程式碼中的行號,以及呼叫端的成員名稱

    • ​​​​​​​​public abstract class NotifyPropertyBase : INotifyPropertyChanged
      ​​​​​​​​{
      ​​​​​​​​    public event PropertyChangedEventHandler PropertyChanged;
      ​​​​​​​​    protected void OnPropertyChanged(string propertyName)=> 
      ​​​​​​​​                            PropertyChanged?.Invoke(
      ​​​​​​​​                                this, 
      ​​​​​​​​                                new PropertyChangedEventArgs(propertyName)
      ​​​​​​​​                            );
      
      ​​​​​​​​    // 透過 CallerMemberName 去取得呼叫端的名稱 (呼叫端可以是方法或者是屬性)
      ​​​​​​​​    protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
      ​​​​​​​​    {
      ​​​​​​​​        if (EqualityComparer<T>.Default.Equals(storage, value)) return;
      ​​​​​​​​        storage = value;
      ​​​​​​​​        OnPropertyChanged(propertyName);
      ​​​​​​​​    }
      ​​​​​​​​}
      

C# 6.0 版

主要是語法使用上的改進.

  • Read-only Automatically Implemented Properties
    • 唯讀屬性
    • ​​​​​​​​// C# 2.0
      ​​​​​​​​private string _property = string.Empty;
      ​​​​​​​​public string Property
      ​​​​​​​​{
      ​​​​​​​​    get { return _property; }
      ​​​​​​​​    private set { _property = value; }
      ​​​​​​​​}
      ​​​​​​​​// C# 3.0
      ​​​​​​​​public string Property { get;private set; }
      
      ​​​​​​​​// C# 6.0
      ​​​​​​​​public string Property { get; }
      
  • Auto Property Initializers
    • ​​​​​​​​public string Property { get; set; } = "自動實作的屬性可以設定初始值了"
      
  • Expression Bodied Function/Members
    • 方法以及唯獨屬性回傳值為單一運算式 , 可將回傳結果放在 => 符號右側表示
    • ​​​​​​​​public string GetFullName(string name) => $"我的名字是 {name}";
      ​​​​​​​​
      ​​​​​​​​// 等同唯讀屬性 , 因為沒有設定 set.
      ​​​​​​​​public string FullName => GetFullName("王大力");
      
  • Using Static
    • 匯入類別所擁有的 static method
    • ​​​​​​​​// 匯入 Math 類別所有的靜態方法 , 實體方法會忽略之
      ​​​​​​​​using static System.Math;
      
  • Null Conditional Operators
    • 語法糖 , 檢查成員是否為 null , 若為 null 則回傳 null
    • ​​​​​​​​class Person
      ​​​​​​​​{
      ​​​​​​​​    public string Name { get; set; } = "小王";
      ​​​​​​​​}
      
      ​​​​​​​​public static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    List<Person> people = null;
      ​​​​​​​​    people = new List<Person>() { null, new Person() };
      ​​​​​​​​    // 由左到右 , 第一個 ? 檢查 people 是否為 null , 若為 null 則回傳 null
      ​​​​​​​​    // 第二個 ? 檢查 people[1] 是否為 null , 若為 null 則回傳 null
      ​​​​​​​​    var firstPersonName = people?[1]?.Name;
      
      ​​​​​​​​    Console.WriteLine(firstPersonName);
      ​​​​​​​​    Console.ReadKey();
      ​​​​​​​​}
      
  • String Interpolation
    • 使用 $ 語法糖 取代 String.Format() 方法
    • ​​​​​​​​public string LastName { get; set; } = "小名";
      ​​​​​​​​public string FirstName { get; set; } = "王";
      ​​​​​​​​public string FullName => $"{FirstName} {LastName}";
      
  • Exception Filters
    • when () 內放入回傳值為 bool 的內容物 , 幫助塞選是否進入此例外類型. (不用再例外區間內加入邏輯判斷)
    • try 區塊內的東西 , 無法放入 when () 內.
    • ​​​​​​​​abstract class Base { }
      ​​​​​​​​class Circle : Base { }
      ​​​​​​​​class Rectangle : Base { }
      ​​​​​​​​public static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    var graph = new Rectangle();
      ​​​​​​​​    try { 
      ​​​​​​​​        throw new Exception(); 
      ​​​​​​​​    }
      ​​​​​​​​    catch (Exception e) when (e.Message != null && graph is Circle ) { 
      ​​​​​​​​        Console.WriteLine($"Circle Exception"); 
      ​​​​​​​​    }
      ​​​​​​​​    catch (Exception e) when (e.Message != null && graph is Rectangle) { 
      ​​​​​​​​        Console.WriteLine($"Rectangle Exception"); 
      ​​​​​​​​    }
      ​​​​​​​​    Console.ReadKey();
      ​​​​​​​​}
      
  • nameof Expression
    • nameof 運算式將變數、屬性或成員欄位的名稱甚至是方法轉成字串回傳
    • 使用快捷鍵 Ctrl+R+R 更改名稱時 , 會連 nameof()內的項目名稱一起更改
    • ​​​​​​​​public string Property { get; set; }
      ​​​​​​​​public int _field;
      ​​​​​​​​public void Method() { }
      ​​​​​​​​public static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    Console.WriteLine(nameof(Property));
      ​​​​​​​​    Console.WriteLine(nameof(_field));
      ​​​​​​​​    Console.WriteLine(nameof(Method));
      ​​​​​​​​}
      
  • 解除在 Catch 和 Finally 區塊中使用 Await 的限制
    • 在 catch / finally 區塊可以開始使用 await 語法糖
    • ​​​​​​​​try{
      ​​​​​​​​    // doSomeThing
      ​​​​​​​​}
      ​​​​​​​​catch (Exception)
      ​​​​​​​​{
      ​​​​​​​​     await Task.Delay(1000);
      ​​​​​​​​}
      ​​​​​​​​finally
      ​​​​​​​​{
      ​​​​​​​​     await Task.Delay(1000);
      ​​​​​​​​}
      
  • Initialize Associative Collections Using Indexers
    • 我不知道這可以幹嘛 = =" , 感覺也沒有比較好寫呀XDDDD
    • ​​​​​​​​// 舊的寫法
      ​​​​​​​​Dictionary<int, string> webErrors = new Dictionary<int, string>
      ​​​​​​​​{
      ​​​​​​​​    { 404, "Page not Found"},
      ​​​​​​​​    { 302, "Page moved, but left a forwarding address."},
      ​​​​​​​​    // 404 索引子重複 , 會跳 Exception
      ​​​​​​​​    { 404, "The web server can't come out to play today."} 
      ​​​​​​​​};
      ​​​​​​​​// 新的寫法
      ​​​​​​​​Dictionary<int, string> webErrors = new Dictionary<int, string>
      ​​​​​​​​{
      ​​​​​​​​    [404] = "Page not Found",
      ​​​​​​​​    [302] = "Page moved, but left a forwarding address.",
      ​​​​​​​​    // 發現索引子已存在 , 會蓋過原本索引子的值 , 不會跳 Exception
      ​​​​​​​​    [404] = "The web server can't come out to play today." 
      ​​​​​​​​};
      
  • Extension Add Methods In Collection Initializers
    • 自定義類別也可以透過實作 Add 方法 , 而使用集合初始設定式
    • ​​​​​​​​public class Person
      ​​​​​​​​{
      ​​​​​​​​    public string Name { get; set; }
      ​​​​​​​​    public int Age { get; set; }
      ​​​​​​​​}
      
      ​​​​​​​​public static void Add(this ICollection<Person> source, string name, int age)
      ​​​​​​​​{
      ​​​​​​​​    source.Add(new Person { Name = name, Age = age });
      ​​​​​​​​}
      ​​​​​​​​
      ​​​​​​​​public static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    // new Person 的動作 , 交給 Add 方法做掉了.
      ​​​​​​​​    // 自定義類別的集合初始設定式
      ​​​​​​​​    var people = new List<Person>()
      ​​​​​​​​    {
      ​​​​​​​​        {"小王",15 },
      ​​​​​​​​        {"大王",64 },
      ​​​​​​​​    };
      ​​​​​​​​    Console.ReadKey();
      ​​​​​​​​}
      
  • Improved Overload Resolution
    • 舊版的編譯器無法正確分辨 Task.Run(Action) 與 Task.Run(Func<Task>())
    • 需要寫成Task.Run(() => DoThings());

C# 7.0 版

主要是語法使用上的改進. 改進幅度比 6.0 更大. 新的語法與語法之間可以交互使用.

  • out Variables

    • out 的用途是使用傳址呼叫將某變數丟入方法後 , 等待其修改. 是一種得到回傳值的方式.
    • 7.0 後 out 變數出現後 , 我們能夠直接宣告一個變數在它要傳入的地方. 也就是說 , 變數不必在丟入方法前將變數型態先宣告好.
    • ​​​​​​​​public static void SetValue(out int v) => v = 3;
      ​​​​​​​​static void Main()
      ​​​​​​​​{
      ​​​​​​​​    // 舊的寫法
      ​​​​​​​​    int v;
      ​​​​​​​​    SetValue(out v);
      
      ​​​​​​​​    // 新的寫法 , 支援變數型別推斷 , 不想使用 , var 可以改 int
      ​​​​​​​​    SetValue(out var v2);
      ​​​​​​​​}
      
  • Tuples

    • C# 7.0 加入了 Tuple 的語言支援,讓 Tuple 欄位的語意名稱能使用全新且更具效率的 Tuple 型別。

    • ​​​​​​​​public static (string name, int age, bool successs) GetData(
      ​​​​​​​​                                            (string name, int age) data
      ​​​​​​​​                                       )
      ​​​​​​​​{
      ​​​​​​​​    var (name, age) = data; // deconstruct data into two variable name & age
      ​​​​​​​​    return (name, age, true);
      ​​​​​​​​}
      ​​​​​​​​static void Main()
      ​​​​​​​​{
      ​​​​​​​​    var valueTuple = GetData(("王大明", 15));
      ​​​​​​​​}
      
  • Discards

    • 通常在您解構 Tuple 或以 out 參數呼叫方法時,會強制您要定義變數,而您沒有使用該值的打算。 C# 新增了對捨棄的支援,來應付這種狀況。 捨棄是僅限寫入的變數,其名稱為 _ (底線字元);您可將所有想要捨棄的值指派到單一變數。 捨棄類似於未經指派的變數;和指派陳述式一樣,都不能用於程式碼中。

    • ​​​​​​​​public static (string name, int age, bool successs) GetData(
      ​​​​​​​​                                            (string name, int age) data
      ​​​​​​​​                                        )
      ​​​​​​​​{
      ​​​​​​​​    var (name, age) = data;
      ​​​​​​​​    return (name, age, true);
      ​​​​​​​​}
      ​​​​​​​​static void Main()
      ​​​​​​​​{
      ​​​​​​​​    // just record success by variable isSuccess
      ​​​​​​​​    var (_, _, isSuccess) = GetData(("王大明", 15));
      ​​​​​​​​    Console.WriteLine($"get Data success is {isSuccess}");
      ​​​​​​​​}
      
  • Pattern Matching

    • 模式比對支援 is 運算式和 switch 運算式。 每個都讓您能檢查物件和其屬性,以判斷該物件是否符合搜尋的模式 , 並在一個指令後得到結果。 同時亦可以使用 when 關鍵字在 switch 運算式上

    • ​​​​​​​​if (input is int count)
      ​​​​​​​​    sum += count;
      
    • ​​​​​​​​public class MyClass { public string Name { get; set; } }
      ​​​​​​​​static string Show(MyClass instance)
      ​​​​​​​​{
      ​​​​​​​​    switch (instance)
      ​​​​​​​​    {
      ​​​​​​​​        case null:
      ​​​​​​​​            throw new NullReferenceException();
      ​​​​​​​​        case MyClass c1 when c1.Name != null:
      ​​​​​​​​            return $"MyClass 's Name is {c1.Name}";
      ​​​​​​​​        case MyClass _:
      ​​​​​​​​            return "MyClass 's Name is Empty";
      ​​​​​​​​        default:
      ​​​​​​​​            throw new InvalidOperationException("Unrecognized type");
      ​​​​​​​​    }
      ​​​​​​​​}
      
  • ref locals and returns

    • 想成是 C 的 Point .
    • ​​​​​​​​static void Before()
      ​​​​​​​​{
      ​​​​​​​​    // unsafe 需要去 專案 => 專案屬性 => 建置 => 不安全程式碼打勾
      ​​​​​​​​    int number = 100;
      ​​​​​​​​    unsafe
      ​​​​​​​​    {
      ​​​​​​​​        int* p = &number;
      ​​​​​​​​        *p = 999;
      ​​​​​​​​    }
      ​​​​​​​​}
      ​​​​​​​​static void After()
      ​​​​​​​​{
      ​​​​​​​​    int number = 100;
      ​​​​​​​​    ref int p = ref number;
      ​​​​​​​​    p = 999;
      ​​​​​​​​}
      
  • Local Functions

    • 支援在方法、屬性或是建構式中 , 宣告以及實作別的方法.
    • local functions
      1. 依照方法內部是否有使用到物件成員 , 來決定 local 方法會被編譯成實體方法還是靜態方法 (此物件成員若是透過local方法的參數傳入 , 則還是會編譯成靜態方法)
      2. 若local方法內部使用到暫存變數(未透過local方法傳入) , 則 local 方法會自動長一個 struct 參數(將使用到的暫存變數裝入).
    • ​​​​​​​​static string OutputDayString(string freq = "weekend")
      ​​​​​​​​{
      ​​​​​​​​    var dayStr = IsEveryDay(freq) ?? IsWeekday(freq) ?? IsWeekend(freq) ?? "has error";
      ​​​​​​​​    return dayStr;
      
      ​​​​​​​​    // local functions
      ​​​​​​​​    string IsEveryDay(string f) => f == "everyDay" ? "is everyDay ~" : null;
      ​​​​​​​​    string IsWeekday(string f) => f == "weekday" ? "is weekday ~" : null;
      ​​​​​​​​    string IsWeekend(string f) => f == "weekend" ? "is weekend ~" : null;
      ​​​​​​​​}
      ​​​​​​​​static void Main()
      ​​​​​​​​{
      ​​​​​​​​    var dayString = OutputDayString();
      ​​​​​​​​    Console.WriteLine(dayString);
      ​​​​​​​​}
      
  • New Expression Bodied Members

    • 在 C# 7.0 , 可以在 「建構子」、「解構子」,以及「屬性」上的 get 和 set 存取子使用 => 運算子
    • ​​​​​​​​public class MyClass
      ​​​​​​​​{
      ​​​​​​​​    private string _name = string.Empty;
      ​​​​​​​​    
      ​​​​​​​​    public MyClass(string name) => this._name = name;
      ​​​​​​​​    
      ​​​​​​​​    ~MyClass() => Console.WriteLine("do some thing");
      
      ​​​​​​​​    public string Name
      ​​​​​​​​    {
      ​​​​​​​​        get => _name;
      ​​​​​​​​        set => _name = value;
      ​​​​​​​​    }
      ​​​​​​​​}
      
  • Throw Expression

    • throw 原本只能做為陳述式使用 , 但現在也可以當作運算式使用.

    • ​​​​​​​​void ThrowException() => throw new Exception(); //能使用 => 運算子
      
      ​​​​​​​​// 能使用三元運算子
      ​​​​​​​​string result = value != string.Empty ? 
      ​​​​​​​​                "correct" : throw new Exception("value is empty");
      ​​​​​​​​
      ​​​​​​​​// 能使用 null 聯合運算子
      ​​​​​​​​return value ?? throw new Exception("value is null");
      
  • ValueTask

    • 從非同步方法傳回 Task 物件可能會造成效能瓶頸。 Task 是參考型別,因此使用它表示要配置物件。 當使用 async 修飾詞宣告的方法傳回結果時,或是以同步方式完成,額外配置記憶體給物件可能會效能關鍵的程式中變成一項重要的時間成本。 例如 : 如果這些配置發生在緊密迴圈中,它可能會變得成本很高。

    • async 方法傳回型別除了 Task、Task<T> 和 void 外 , 新增一個 ValueTask<T>

    • 需要再 NuGet 安裝 System.Threading.Tasks.Extensions 才能使用該 ValueTask<TResult>

    • [C#]7.0為async新增的ValueTask的作用
  • Numeric Literal Syntax Improvements

    • 數字分隔符號 _ 是幫助我們閱讀數字使用 , 其在程式執行時 , 會被忽略.
    • ​​​​​​​​public const int Eight = 0b00001_000;
      ​​​​​​​​public const int Sixteen = 0x1_0;
      ​​​​​​​​public const double AvogadroConstant = 6.022_140_857_747_474e23;
      ​​​​​​​​public const long BillionsAndBillions = 100_000_000_000;
      

C# 7.1 版

  • Async Main
    • Main 方法可以使用 async 修飾.
  • default literal expressions
    • 除了原本的 default 運算子功能外 , 強化 default 的功能 - 預設常值(型別推斷後回傳該型別之預設值)
    • ​​​​​​​​public static void MyMethod<TSource>()
      ​​​​​​​​{
      ​​​​​​​​    // old 
      ​​​​​​​​    var result = default(TSource);
      ​​​​​​​​    // new 
      ​​​​​​​​    TSource result2 = default;
      ​​​​​​​​}
      
  • Inferred Tuple Element Names
    • 使用變數名稱自動推斷 tuple 項目名稱
    • ​​​​​​​​// old 寫法
      ​​​​​​​​string s = "";
      ​​​​​​​​int i = 5;
      ​​​​​​​​var t = (s: s, i: i);
      
      ​​​​​​​​// new 寫法
      ​​​​​​​​// 之後可使用 t.s or t.i 去使用 tuple 項目 , 
      ​​​​​​​​// C#7.1之前 , 若未替 tuple 項目命名 , 則只能使用 .item1 , .item2 ...
      ​​​​​​​​string s = "";
      ​​​​​​​​int i = 5;
      ​​​​​​​​var t = (s, i);  
      
  • Pattern Matching On Generic Type Parameters
    • 泛型變數可以使用模式比對
    • ​​​​​​​​public class MyClass { public string Name { get; set; } }
      ​​​​​​​​public class MyClass2 { public string Name { get; set; } }
      ​​​​​​​​static string Show<T>(T instance)
      ​​​​​​​​{
      ​​​​​​​​    switch (instance)
      ​​​​​​​​    {
      ​​​​​​​​        case MyClass c1 when c1.Name != null:
      ​​​​​​​​            return $"MyClass 's Name is {c1.Name}";
      ​​​​​​​​        case MyClass _:
      ​​​​​​​​            return "MyClass 's Name is Empty";
      ​​​​​​​​        case MyClass2 c2 when c2.Name != null:
      ​​​​​​​​            return $"MyClass2 's Name is {c2.Name}";
      ​​​​​​​​        case MyClass2 _:
      ​​​​​​​​            return "MyClass2 's Name is Empty";
      ​​​​​​​​        default:
      ​​​​​​​​            return "甚麼玩意";
      ​​​​​​​​    }
      ​​​​​​​​}
      

C# 7.2 版

  • Safe Efficient Code Enhancements

    • ref readonly 區域變數
    • readonly structure
    • ref structure
  • Non Trailing Named Arguments

    • 具名引數本來只能放在位置引數後方 , 現在只要位置正確 , 也可以放在前方
    • ​​​​​​​​// 通常會跟 Optional 參數混用.
      ​​​​​​​​static void Main(string[] args)
      ​​​​​​​​{
      ​​​​​​​​    Test("小名", phone: "0989782266"); // 不用把所有的 Optional 參數都打完.
      ​​​​​​​​    Console.ReadKey();
      ​​​​​​​​}
      ​​​​​​​​public static void Test(string name, int age = 30, string phone = null, [CallerMemberName] string caller = null)
      ​​​​​​​​{
      ​​​​​​​​    Console.WriteLine($"{name} {age} {phone} {caller}");
      ​​​​​​​​}
      
  • Leading Underscores In Numeric Literals

    • 數值常數的任何位置都可以使用 _ , 即使是 0b 後也可以加上 _ .
    • ​​​​​​​​int binaryValue = 0b_0101_0101;
      
  • Private Protected Access Modifier

    • 新增 private protected 存取修飾詞
  • Conditional Ref Expressions

    • ref 條件運算式
    • ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

C# 7.3 版

  • ref 變數可重新指派
    • ​​​​​​​​var obj = new object();
      ​​​​​​​​var obj2 = new object();
      ​​​​​​​​ref var i = ref obj;
      ​​​​​​​​i = ref obj2; // i 重新指派給 obj2
      
  • Equality And Tuples
    • Tuple 可使用 == 或是 != 運算子進行相等或不等的運算判斷
  • 泛型約束
    • 允許 Enum、Delegate 和 MulticastDelegate 作為基底類別條件約束

  • 將 attributes 附加至自動實作屬性的支援欄位

C# 8.0

  • Default interface members
    • 修改介面時不會影響現有已實作此介面的衍生型別 e.g. 已經 Release 很久的類別庫
  • using scope C# 8.0
  • 靜態區域函式 C# 8.0
  • 可為 null 的參考型別 C# 8.0
  • 非同步迭代 C# 8.0
  • Index & Range C# 8.0
  • ★switch expression C# 8.0
  • recursive pattern matching C# 8.0
  • tuple pattern matching C# 8.0
  • position pattern matching C# 8.0
  • ★property pattern matching C# 8.0
  • 使用 {} C# 8.0

C# 9.0

C# 10.0

C# 11.0

Thank you!

You can find me on

若有謬誤 , 煩請告知 , 新手發帖請多包涵

:100: :muscle: :tada: :sheep: