OUCC

BLOG

記事一覧 タグ一覧

最近のC#のMainの書き方バリエーション

投稿日:

執筆者: しおかい

これは OUCC Advent Calendar 2022の7日目の記事です。 空いてたので即興で書きました。

最近の(と言っても.NET6とかまでの話ですが)C#にはいろいろ機能が追加されていまして、Hello Worldもいろいろな書き方が出来るようになっています。 dotnet new consoleして出来たProgram.csに

Console.WriteLine("Hello World");

の1行しかなく、にもかかわらず実行できるのを見て戸惑った人も居るのではないでしょうか。

という事で、今回はHello Worldを短く書くための3つの機能を紹介し、最終的に上記の1行コードにしていきます。 なお以下では特記無き限りMainは返り値void、引数string[]としました。

0.プレーンな書き方

最も基本的な書き方はおそらく以下のようになると思います。


using System;

namespace Plane
{
    public class Sample
    {
        public static void Main(string[] arg)
        {
            Console.WriteLine("Hello World");
        }
    }
}

SystemusingConsole.WriteLineでHello Worldしています。 特に言う事は無いでしょう。

1.ファイルスコープ名前空間

上記ではnamespaceでスコープを括っているが、ファイルスコープ名前空間という機能を使えばこの括りを外せ、インデントを1段減らすことが出来ます。

using System;

namespace Plane;
public class Sample
{
    public static void Main(string[] arg)
    {
        Console.WriteLine("Hello World");
    }
}

これは、ファイル全体が名前空間で括られているのと同じ扱いになります。

一応デメリットとして、ファイルスコープ名前空間を使うと1ファイルに1つしか名前空間を(通常の名前空間もファイルスコープ名前空間も)指定できませんが、普通1ファイルに複数名前空間を指定することは無いので問題ないでしょう。

2.ImplicitUsings

C#10からglobal usingという機能が追加されました。 これは例えば

global using System;

とすると、同じプロジェクトにある全てのファイルでusing Systemしているのと同じことになります。 .NET6ではこれを利用してImplicitUsingsという機能が提供されています。 これは読んで字のごとく「暗黙的なusing」であり、いくつかの名前空間を.NETが勝手にglobal usingしてくれるというものです。

コンソールアプリケーションでは

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

が自動的に行われた扱いになります。 内容はプロジェクトの種類によって異なる様です。 これを利用すると、Hello Worldは以下のようになります。

namespace Implicit;
public class Sample
{
    public static void Main(string[] arg)
    {
        Console.WriteLine("Hello World");
    }
}

using Systemが無くなりました。 幾分すっきりしましたね。

3.トップレベルステートメント

C#9から、トップレベルステートメントと言って、名前空間やクラスや関数の外、トップレベルの場所に直接式(ステートメント)を書けるようになりました。 すなわち、先ほどのHello Worldは、Mainの中身だけ抜き取って以下のように書けます。

Console.WriteLine("Hello World");

これが始めに出てきた1行コードの正体です。 実際にはコンパイル時にclassとMainが自動生成されている様です。 注意点として、トップレベルステートメントより上に名前空間やクラスを定義するとエラーになります。

// これはエラー
namespace TopLevel;
Console.WriteLine("Hello World");
// これもエラー
namespace TopLevel
{

}
Console.WriteLine("Hello World");
// これもエラー
namespace TopLevel
{
    Console.WriteLine("Hello World");
}
// これはセーフ
Console.WriteLine("Hello World");
namespace TopLevel
{

}

また、トップレベルステートメントを書けるのは1プロジェクトに1ファイルまでです。 なお、関数やusingは上に定義できます。

// これはセーフ
void Hello() => Console.WriteLine("Hello World");
Hello();

この関数はプロジェクト全体から見ることが出来ますが、アクセスは出来ないようになっています。

ちなみにこのトップレベルステートメント、awaitを使うと自動生成Mainは勝手にasyncになってくれます。 なので

Console.WriteLine("Hello");
await Task.Delay(5000);
Console.WriteLine("World.");

のようにTaskも使用できます。

ただし、refは直接使用できません。

var hoge = new[]{0, 1, 2};
// エラー
ref var e = ref hoge[^1];
e++;
Console.WriteLine(hoge[^1]);
// これはセーフ
void Hoge()
{
    var hoge = new[]{0, 1, 2};
    ref var e = ref hoge[^1];
    e++;
    Console.WriteLine(hoge[^1]);
}
Hoge();

終わりに

というわけで、C#でHello Worldを簡潔に書けるようになる機能3つを紹介しました。 決まり切ったことを省略できるのは便利で嬉しいことです。

大きく見た目の変わるトップレベルステートメントはともかく、ファイルスコープ名前空間とImplicitUsingsは便利なので使っていきたいと思います。

参考