Visual Studio 2022のConsoleアプリをたった1行にしたカラクリ


2022年 04月 08日

Visual Studio 2022 が昨年11月にリリースされ、C#も10.0になりました。
毎日レガシーコード満載のプロジェクトと戦っている勇者さんたちも、「もう春だし、そろそろ開発環境更新してもいいかな」と考えられているのではないでしょうか。
しかし、日々の業務に追われ気味のプログラマーがVisual Studio 2022を触って一番最初に面食らうのが、あのConsoleアプリケーションのテンプレートでしょう。

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

「ナニコレ?1行しかないんだけど?Mainどこ行ったの?」
「なんでこれ動くの?」
「レガシーコードと格闘してるうちにC#の仕様ってこんなに大きく変わっちゃったの?」

と、いろんな「?」と不安が頭をグルグル駆け巡ったのではないでしょうか。

言語仕様が大きく変わったわけではない。追加された新機能を組み合わせて使っているだけ。

ご安心を。実はこの1行、C#9と10の二つの新機能、そして.NET6 SDKを使って実現しているのです。

C#9と10の二つの新機能とは、

  • 最上位レベルのステートメント(C#9)
  • global using ディレクティブ(C#10)

のことです。

最上位レベルのステートメント(C#9)

https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/program-structure/top-level-statements

↑こちらの公式説明で十分ですが、要約すると

  • プロジェクトに1個だけ、ステートメント(クラスでない)で始まるファイルを配置することができる。
  • ステートメント(クラスでない)で始まるファイルは、コンパイラの頑張りによってMainメソッドに展開されてからコンパイルされる。

たとえば、最初のテンプレートの1行だと、下記になります。

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

おお、これなら見たことありますね、いつものやつですね。安心ですね。
ごく限定的な条件でのみ自動展開してくれるコンパイラーの機能なので、言語仕様というよりはサクッと書きたい時用のチョイ便利機能みたいな感じですね。
便利であるに越したことはないですけどね。

余談ですが、ステートメントで始まるファイルはビルドアクションを「C#コンパイル」にしないと展開してくれません。(当たり前ですが)
また、ビルドアクションが「C#コンパイル」になっていれば、拡張子はtxtでもhtmlでもなんでも良いです。(誰得な知識ですが)

global using ディレクティブ(C#10)

「Mainの自動展開はわかったけど、usingはどこ行ったん?」
「using無いとSystem.Console.WriteLine()て書かなきゃいかんでしょ」
という疑問を持った方、優秀です。
これはC#10の新機能 global using ディレクティブを使っています。

https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/using-directive

↑詳しい説明は公式を読んでください。
要はプロジェクト全体に適用されるusingを定義できますよ、という機能です。

ここで、

global using system;

なんてどこにも定義されていないのに、適用されているのはなぜだ?
と思ったあなた、さらに優秀です。

実はこれは.NET6 SDKの機能で「暗黙のusingディレクティブ」というやつでして、
プロジェクトを作成したときに、プロジェクトディレクトリ内に

obj\Debug\net6.0{プロジェクト名}.GlobalUsings.g.cs

というファイルが自動生成され、下記が記述されています。

global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

なので、わざわざ書かなくてもよかったんですね。

ちなみにこの暗黙のusingディレクティブはプロジェクトテンプレートによっても変わりますし、プログラマーが制御することもできますが、それは次回としましょう。

まとめ

  • Visual Studio 2022ではConsoleアプリが1行になっていてビビるけど、言語仕様が大きく変わったわけではなく、追加された新機能を使っているだけ。
  • C#9の最上位レベルのステートメント、C#10のglobal usingディレクティブ、.NET6 SDKの暗黙のusingディレクティブ機能を合わせて使用している。

以上です。
さ、レガシーコードと戦ってこよ。