目次

キーワード

概要

C# では通常、1つのプログラムは複数の C# ソースコードからなり、そのソースコード中には複数の関数が含まれています。 その、多数ある関数の中で、プログラム起動時に最初に呼ばれるものをエントリーポイント(entry point: 入場地点)と呼びます。

C# のプログラムの基本構造」で例を出したように、 C# では、Mainという名前の関数が自動的にエントリーポイントになります。

(「関数」内でも補足していますが、 正確にいうと、Mainという名前のメソッドがエントリーポイントになります。)

[補足] C# スクリプト

スクリプト実行の場合は関数で囲わなくてもどこにでも処理を書けます。 Main関数も不要です。

Main の引数、戻り値

Mainの引数と戻り値は、以下のいずれかである必要があります。 これ以外のオーバーロードはエントリーポイントになりません。

static int Main()
static int Main(string[] args)
static void Main()
static void Main(string[] args)

(ただし、後述しますが、C# 7.1 からは戻り値としてTaskクラスが使えるようになりました。)

引数を持っている場合、引数にはコマンドライン引数が渡ってきます。 (引数なし版は、コマンドライン引数を受け取る必要がない時に使います。)

また、戻り値はプログラムの終了コードを返します。 Windows の場合は0が正常終了、1が部分的な成功、…などの意味があるようです。 戻り値なし版の場合は常に0(正常終了)扱いです。

Main がないタイプのプロジェクト

GUI アプリや Web アプリでは、Main関数を書かない場合があります。 この場合、以下のいずれかです。

  • 他のプログラムから呼び出される。どの関数から呼び出すかは、呼び出し元次第
  • 開発者に見えないところで自動的にMainが作られている

例えば、ASP.NETの場合は前者、WPF アプリの場合は後者になります。

エントリーポイントの指定

1つのプログラムの中に複数のクラスがあって、 複数のクラスの中にMain関数がある場合、そのままではエントリーポイントを決定できず、コンパイル エラーになります。

この場合、どのMain関数を使うかをオプション指定できます。

参考:

非同期 Main

Ver. 7.1

C# 7.1で、以下のように、Main関数の戻り値にTaskクラス(System.Threading.Tasks名前空間)を使えるようになりました。

static Task<int> Main()
static Task<int> Main(string[] args)
static Task Main()
static Task Main(string[] args)

もちろん、非同期メソッドを使えるようにするためです。 例えば以下のようなMain関数が、ちゃんとエントリーポイントとして認識されます。

static async Task Main()
{
    for (int i = 10; i > 0; i--)
    {
        Console.WriteLine(i);
        await Task.Delay(1000);
    }

    Console.WriteLine("done.");
}

非同期 Main の仕組み

ちなみに、この機能は、コンパイラーが通常の(void/int戻り値の)エントリーポイントを別途自動生成することで実現しています。 例えば、先ほどの例のように、Task Main()を書くと、追加で以下のような関数が作られ、これが実際のエントリーポイントとして機能します。

// 実際には <Main> というような、C# で本来使えない名前で生成される
static void _Main_(string[] args)
{
    Main().GetAwaiter().GetResult();
}

中身はGetAwaiter().GetResult()を呼んでいるだけです。

通常の Main がすでにある場合

非同期 Main の仕様は C# 7.1 で追加されたものです。 そのため、これまでに書いたコードの中にすでに、エントリーポイントにするつもりがない Task Main() が含まれている場合に対する考慮が必要です。

C# 7.1 では、通常の(void/int戻り値の)Main関数がある場合、そちらだけをエントリーポイント扱いします。

static void Main(string[] args)
{
    Console.WriteLine("こちらがエントリーポイント扱い");
}

static async Task Main()
{
    Console.WriteLine("void Main(string[]) がある限り、こちらは呼ばれない");
}

更新履歴

ブログ