概要
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[]) がある限り、こちらは呼ばれない");
}