C# 歷代新功能 整理

整理常用的

C# 2.0 版 - 泛型 , partial class , delegate (Func) , 可為 null 的C#實數值型別 (int?) , yield return 
C# 3.0 版 - LINQ , 擴充方法  , 自動實作的屬性 , 匿名類型 var v = new { Amount = 108, Message = "Hello" };  , 物件和集合初始設定式
C# 4.0 版 - 動態型別 (dynamic) , 具名和選擇性引數 , 
C# 5.0 版 - async await , 
C# 6.0 版 - nameof , 唯讀 Auto 屬性 , Null 條件運算子 first = person?.FirstName ?? "Unspecified";$"{FirstName} {LastName}" , 例外狀況篩選條件
使用索引子初始化關聯集合
private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};
C# 7.0 版 -  out 變數 if (int.TryParse(input, out var answer))
Tuples 
(string Alpha, string Beta) namedLetters = ("a", "b");

public class Point
{
       public Point(double x, double y) 
           => (X, Y) = (x, y);

       public double X { get; }
       public double Y { get; }

       public void Deconstruct(out double x, out double y) =>
           (x, y) = (X, Y);
}

Discard - 暫存虛擬變數 (_, _, area) = city.GetCityInformation(cityName);
模式比對 -  is 運算式
if (input is int count)  sum += count;
模式比對 -  switch 比對運算式

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}

C# 8.0 版 (C# .Net Core 3.x 和 .NET Standard 2.1支援8.0)

Nullable Reference Types   Huan-Lin 學習筆記
編譯時幫你檢查null reference 的可能性有的話跳警告 
定義一個屬性string Title 會警告  可能為null
如果就是可能為null時要string?  這樣編譯器就知道這是可能為null 不會跳警告      
class 給預設初始值  
post.title!.Length   只是跟編譯器說這裡不會有null 的時候讓編譯器不要跳警告   

Pattern matching 進一步強化,其中包含:
1. Switch 運算式 (Switch Expressions)
2. 屬性模式 (Property Pattern)
3. Tuple 模式 (Tuple Pattern)
4. 位置模式 (Positional Pattern)

C# 8 索引(Indices)與範圍(Ranges)
兩個新的類型(Index, Range)與兩個新的運算子(^, ..)
System.Index 序列索引
^  序列結尾開始
System.Range 序列子範圍。
.. 指定範圍 開始結束

C# 9.0  initial only property
public string Name { get; init; }
init就是 property setter,僅能在物件初始化設定式中設定此屬性值

C# 9.0  record
 record 是一個糖分非常高的語法糖
(1) implement IEquatable<T> interface
(2) override void Equals(Object other) 方法,內容則為呼叫  this.Equals(other as Person)。
(3) overload == 和 != 運算子,讓這兩個運算子的行為和 Equals 方法一致。
(4) override int GetHashCode() 方法。
(5) 如果沒有手動寫建構式的話,編譯器會產生兩個建構式,一個是無參數建構式 (這是原本編譯器處理類別的行為,和 record 無關);另外一個是具有自身型別為參數的建構式,若為編譯器產出的這個建構式存取修飾會被宣告為 protected。
(6) 產生一個 public virtual Person <Clone>$() 方法,這個方法的內部會呼叫 new Person(this) 回傳一個新的執行個體,這個方法不能直接在 C# 使用程式碼直接呼叫,必須透過新的 with expression 來使用。
(7) 產生一個唯讀的屬性 protected virtual Type EqualityContract,內容很簡單,就是  return typeof(Person); 。
(8) 加入一個 protected virtual bool PrintMembers(StringBuilder builder) 方法,這個方法的內容是串聯欄位與屬性值字串,基本上是為了給 ToString() 方法呼叫。
(9) override ToString() 方法。
var p1 = new Person { id = 10, Age = 28, Name = "Bill" };
var p2 = p1 with { id = 11, Name = "Tom" };
p2 會指向一個新的執行個體,這個執行個體的 id 欄位會被改變為 11,Name 屬性則會變更成 "Tom";

預設生成的是屬性是 {get;init;} 不是 {get;set;},這代表設定值時間點在 constructor(建構式),延伸產生immutable(不可變)特性,也代表 record 預設為thread-safe(線程安全)
注意不能把 record 當作一定是 immutable(不可變),原因在微軟沒有限制以下寫法
准許修改 {get;init;} 為 {get;set},將會導致 immutable 跟 thread-safe 特性消失

參考資料
C# 的歷史

如果內容有誤請多鞭策謝謝