1. 程式人生 > >C# 9.0 新特性預覽 - 頂級語句

C# 9.0 新特性預覽 - 頂級語句

# C# 9.0 新特性預覽 - 頂級語句 ### 前言 隨著 .NET 5 釋出日期的日益臨近,其對應的 C# 新版本已確定為 C# 9.0,其中新增加的特性(或語法糖)也已基本鎖定,本系列文章將向大家展示它們。
### 目錄 [[C# 9.0 新特性預覽 - 型別推導的 new](https://www.cnblogs.com/Rwing/p/csharp-9-0-preview-target-typed-new.html)]
[[C# 9.0 新特性預覽 - 空引數校驗](https://www.cnblogs.com/Rwing/p/csharp-9-0-preview-null-arg-checking.html)]
[C# 9.0 新特性預覽 - 頂級語句]
[C# 9.0 新特性預覽 - Record 型別]
[C# 9.0 新特性預覽 - 模式匹配的改善]
[C# 9.0 新特性預覽 - 原始碼生成器]
[C# 9.0 新特性預覽 - 其他小的變化]
### 頂級語句 (Top-level statements) 頂級語句這個名字看起來不是那麼直觀,或許它的曾用名更好一些:Simple Programs,簡單程式。 #### 目的 想必大家都知道,即使是最簡單的 C# 程式,也會有一定量的繁文縟節,因為最少也需要一個 Main 方法。這似乎妨礙了語言的學習和程式的清晰度。因此,這個特性的最主要目的就是為了初學者和程式碼的清晰度,讓書寫 C# 程式可以變得更輕鬆。 #### 語法 語法 Spec 如下,允許在名稱空間的宣告前面,新增一組語句,且只允許有一個編譯單元(可以認為是一個原始檔)擁有這種語句: ```csharp compilation_unit : extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration* ; ``` Spec 比較難懂,我們直接來看示例:簡單來說,就是允許在原始檔中直接書寫程式碼語句而不用寫 Main 方法: ```csharp System.Console.WriteLine("Hi!"); ``` 以上程式碼會被翻譯為: ```csharp static class $Program { static void $Main(string[] args) { System.Console.WriteLine("Hi!"); } } ``` 可以看到,WriteLine語句被自動的包在了一個類和 Main 方法裡面。
自動生成的 Main 方法的返回值也會根據是否非同步以及是否有返回值來變化,例如: ```csharp await System.Threading.Tasks.Task.Delay(1000); System.Console.WriteLine("Hi!"); return 0; ``` 會被翻譯為: ```csharp static class $Program { static async Task $Main(string[] args) { await System.Threading.Tasks.Task.Delay(1000); System.Console.WriteLine("Hi!"); return 0; } } ``` #### 各種場景 - 支援在 using 語句後面: ```csharp using System; Console.Write("Hi!"); ``` 會被翻譯為: ```csharp using System; static class $Program { static void $Main(string[] args) { Console.Write("Hi!"); } } ``` - 也可以加上本地函式: ```csharp local(); void local() => System.Console.WriteLine(2); ``` - 可以與其它程式碼共存,例如類的宣告: ```csharp Type.M(); static class Type { public static void M() { System.Console.WriteLine("Hi!"); } } ``` 稍微複雜一點的: ```csharp await using (var x = new C()) { System.Console.Write("body "); } class C : System.IAsyncDisposable, System.IDisposable { public System.Threading.Tasks.ValueTask DisposeAsync() { System.Console.Write("DisposeAsync"); return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); } public void Dispose() { System.Console.Write("IGNORED"); } } ``` - 同時相容了using alias的語法 ```csharp using alias1 = Test; string Test() => ""1""; System.Console.WriteLine(Test()); class Test {} delegate Test D(alias1 x); namespace N1 { using alias2 = Test; delegate Test D(alias2 x); } ``` - 也可以同時與顯示的 Main 方法宣告在一起,只不過顯示的Main方法會被忽略掉並提示一個警告 ```csharp using System; using System.Threading.Tasks; System.Console.Write("Hi!"); class Program { static void Main() // warning CS7022: The entry point of the program is global code; ignoring 'Program.Main()' entry point { Console.Write("hello"); } } ``` #### 限制 - 不支援在多個編譯單元下擁有頂級語句: ```csharp // file1.cs System.Console.WriteLine("1"); // error CS9001: Only one compilation unit can have top-level statements. // file2.cs System.Console.WriteLine("2"); // error CS9001: Only one compilation unit can have top-level statements. ``` - 不能放在類的內部 ```csharp class Test { System.Console.WriteLine("Hi!"); // ERROR } ``` - 不能放在名稱空間的內部 ```csharp namespace Test { System.Console.WriteLine("Hi!"); // ERROR } ``` - 要麼所有分支都有返回值,要麼都沒有 ```csharp System.Console.WriteLine(); if (args.Length == 0) { return 10; // error CS0161: 不是所有程式碼分支都有返回值 } ``` - 雖然可以可以與類宣告一起寫,但是在類中是無法呼叫到 Main 方法 args 入參的,因為編譯時會編譯為兩個類 ```csharp System.Console.WriteLine(args); class Test { void M() { System.Console.WriteLine(args); // ERROR } } ``` - 自然,你也不能用 args 來命名本地函式 ```csharp args(1); void args(int x) // ERROR {} ``` ### 參考 [[Proposal: Simplified Null Argument Checking](https://github.com/dotnet/csharplang/blob/master/proposals/null-arg-checking.md)]
[[Unit test: NullCheckedParameterTests.cs](https://github.com/dotnet/roslyn/blob/features%2Fparam-nullchecking/src/Compilers/CSharp/Test/Semantic/Semantics/NullCheckedParameterTests.cs)]
[[LDM-2019-07-10.md](https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-07-10.md#param)]