そういえば、文法的な変更ではないのでどこにも告知は出ていないもの(サイレント修正)なんですが、トップ レベル ステートメント (C# 9.0 で追加)に変更点が2つあります。

空ステートメント禁止

以下の2つのコードを見比べてください。

1つ目:

class Program
{
    static void Main() => Console.WriteLine("Hello World!");
}

2つ目:

;
class Program
{
    static void Main() => Console.WriteLine("Hello World!");
}

C# 9.0 当初、2つ目のコードもコンパイルできていました。 そして、実行結果がどうなるかと言うと…

  • 1つ目: Program.Main が呼ばれて、Hello World! が表示される
  • 2つ目: トップ レベル ステートメント扱いされて、何も表示されない

さすがに ; 1個で挙動が変わっちゃうのはためらわれるというか、 「2つ目で何も表示されなくなるのはバグだ」と認識しちゃう人もいたので修正がかかりました

今は、2つ目のコードはコンパイル エラーになります。 空ステートメント1個だけのトップ レベル ステートメントは禁止。

でも、以下のようなコードは今 (C# 10.0) でも認められてるんですよね…

空じゃないステートメントもある:

;
Console.WriteLine();

空ブロック:

{}

; だけのものが禁止された経緯を考えると、{} だけのものも禁止されてもおかしくはないんですけど。 「Main を呼ぶかトップ レベル ステートメントを呼ぶか」の分岐条件が緩すぎるんですよね。 今後どうなるか…

トップ レベル ステートメントを使った時のクラス名

トップ レベル ステートメントを使った時、例えば以下のようなコードを書くと、

Console.WriteLine();

扱いとしては以下のようなコードに展開されていました。

using System;

internal class <Program>$
{
    private static void <Main>$(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

クラス名、メソッド名がどうなるかは仕様には明記されておらず、実装依存(変更が掛かっても文句は言えない)です。 とりあえず、「通常の C# では書けない名前」(unspeakable name というそうです)になっていました。 実装依存ですが、現在の実装では <Program>$ みたいに <>$ を入れて unspeakable にしています。

ところが、クラス名が unspeakable だと、ASP.NET の単体テストで困ったそうです。 ということで、クラス名だけは speakable な Program に変更。 C# 10.0 では上記のコードは以下のような展開結果に変更されています。

using System;

internal class Program
{
    private static void <Main>$(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

トップ レベル ステートメントだけを使っている分には特に影響のない修正のはずなんですが… 例えば以下のようなコードがコンパイル エラーを起こすようになります。

Console.WriteLine("Hello World!");

internal class Program
{
}

コンパイラーが生成する Program クラスと、コード中に手書きした Program クラスが衝突しています。

一方で、現状のこの実装を逆手に取ると、以下のようなコードはコンパイルできるようになります。

A(); // Program.A が呼ばれる。

partial class Program
{
    public static void A() => Console.WriteLine("Hello World!");
}

ただ、Program というクラス名が仕様書上に明記されているわけではないので、将来もこのコードが有効であるという保証はあんまりできません。その点はご注意ください。