去年公司的 NAS 商城和行動網頁服務的專案翻新至 .NET Core 2.0、2.1,翻新期間 Microsofr .NET Core 3.0 和 C#8 也隨之正式發佈,這才沒過多久,緊接著 C# 9.0 的規格已經進入規劃設計的階段了!不免讓人感嘆新的技術和版本的迭代速度之快。
雖然許多細項還沒定案,我們可以在 Github dotnet csharlang 中的 Milestone 看到許多新規格及功能的提案,今天我們來介紹其中幾項 Design Review 清單中的候選功能,或許還會持續變化,但偶爾關注這些功能的策畫演變也是很有趣的。
1. 簡化參數的 Null 驗證(Simplified Parameter NULL Validation Code)
原本傳入的參數要做 null 驗證,不像 JavaScript 可用兩個驚嘆號去做 Logic Not 反轉判定,必須再傳入後 if-else 加以判斷,C# 9 提案了在變數後方加上一個驚嘆號,可直接判定目標變數是否為空值;此 feature 並不是一個型別,而是只能在 runtime value 上進行檢驗的便利語法糖。範例如下:
1. // Before
2. void Insert(string s) {
3. if (s is null)
4. throw new ArgumentNullException(nameof(s));
5. ...
6. }
7.
8. // After
9. void Insert(string s!) {
10. ...
11. }
2. 可將 Switch 的表達式視為陳述式(Switch expression as a statement expression)
原本在 C# 7 中使用 Swtich 需如以下程式碼撰寫,也是大多數人所熟悉的 Switch 寫法:
1. public static RGBColor FromRainbowClassic GetArea(Color colorBand)
2. {
3. switch (shape)
4. {
5. case Color.Red:
6. return new RGBColor(0xFF, 0x00, 0x00);
7. case Color.Orange:
8. return new RGBColor(0xFF, 0x7F, 0x00);
9. case Color.Yellow:
10. return new RGBColor(0xFF, 0xFF, 0x00);
11. default:
12. throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
13. }
14. }
而 C# 8 中允許了 Switch 使用運算式精簡語法,能使程式碼更加精煉:
1. public static RGBColor FromRainbow(Rainbow colorBand) =>
2. colorBand switch
3. {
4. Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),
5. Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
6. Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
7. _ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
8. };
而這次新版本的提案是開放可在 Switch 的表達式中使用陳述式,C# 9 的 Milestone 中這項提案,原本被放在 C# 8.X 的目標中,後來被轉移至 9.0。目前這項功能仍處於還沒定案的討論階段:
1. void M(bool c, ref int x, ref string s)
2. {
3. c switch { true => x = 1, false => s = null };
4. }
3.Primary Constructors
在 C# 9 中提出了類似 TypeScript 語言中的簡化 Constructors,原本的 Constructor 需如以下撰寫:
1. public class Car{
2. private readonly int _wheels;
3. public Car(int wheels){
4. _wheels = wheels;
5. }
6. ...
7. }
在提案中,新的寫法可使用更精簡的方式宣告:
1. public class Car(int _wheels){
2. ...
3. }
然而此種宣告方式可能會帶來語義與原宣告方式不甚一致的狀況,這方面的疑慮或許可以經由其他擴展方式消除,但勢必連帶出其他需求,且此種精簡宣告方式僅限於當只有單一 Constructor 存在時才能使用。在提案文件中詳細列出了可能的備案,有興趣進一步了解的人可參考 primary-constructors.md。
4. Record Classes
Record Classes 和 Primary Constructor 概念類似,目標為大量減少在創建新類別時出現的模板代碼,目前看提案內容,和 Primary Constructor 兩者應是只能擇一存在。
1. public class Person
2. {
3. public string Name { get; }
4. public DateTime DateOfBirth { get; }
5.
6. public Person(string Name, DateTime DateOfBirth)
7. {
8. this.Name = Name;
9. this.DateOfBirth = DateOfBirth;
10. }
11. }
12. //簡化之後
13. public class Person(string Name, DateTime DateOfBirth);
5. 經由 Enum 類別做差別聯集(Discriminated Unions via Enum Classes )
簡單來說,這項提議的目的與上述幾項類似,利用 enum 特性簡化 Class 的宣告方式,例如:
1. enum class Shape
2. {
3. Rectangle(float Width, float Length),
4. Circle(float Radius)
5. }
此種 enum class 的宣告方式等同於以下程式碼:
1. partial abstract class Shape
2. {
3. public data class Rectangle(float Width, float Length) : Shape,
4. public data class Circle(float Radius) : Shape
5. }
Enum 類別為以上五項提議中爭議最大的一項,之後再產生變數的可能性不低,而此篇文章截選了討論度較高的五項提案,Github milestone有更多的proposal內容,大家可以再持續關注 C#9 的規格進展。
Reference