C#でプログラムを終了する方法|Application.Exit・Environment.Exit・returnの違いも解説

はじめに

C#でプログラムを終了する方法には、returnEnvironment.ExitApplication.Exit などがあります。どれも「終了」に関係しますが、使う場面や終了のされ方は同じではありません。

たとえば、コンソールアプリで処理を終えて自然に終了したいなら return が基本です。一方、エラー発生時にプロセス全体を確実に終了したい場合は Environment.Exit を使います。Windowsフォームアプリで画面操作からアプリを閉じたい場合は Application.Exit がよく使われます。

この記事では、C#でプログラムを終了する代表的な方法を、コード例つきでわかりやすく解説します。Application.ExitEnvironment.Exitreturn の違いや、終了処理を書くときの注意点も整理しているため、「C# 終了」で調べている方は、まずこの記事の使い分けを押さえてください。

1. C#でプログラムを終了する方法は主に3つ

C#でプログラムを終了する代表的な方法は、主に次の3つです。

方法主な用途対象
return処理を終えて自然に終了するコンソールアプリ、メソッド、Mainメソッド
Environment.Exitプロセス全体を即終了するコンソールアプリ、バッチ処理、エラー時
Application.ExitWindowsフォームアプリのメッセージループを終了するWindows Forms

それぞれ似ているように見えますが、実際には「現在のメソッドを抜けるだけ」なのか、「アプリケーション全体を終了する」のか、「Windowsフォームの画面を閉じる」のかが異なります。

1-1. まず結論:用途別に使う終了方法を選ぶ

C#で終了処理を書くときは、次のように考えると迷いにくくなります。

やりたいことおすすめの方法
コンソールアプリを通常終了したいreturn
正常終了・異常終了の終了コードを返したいreturn 0; または return 1;
エラー時にプロセス全体を即終了したいEnvironment.Exit(1);
Windowsフォームアプリを終了したいApplication.Exit();
WPFアプリを終了したいApplication.Current.Shutdown();
後始末処理を確実に行いたいreturnusingDispose を使う

特に重要なのは、通常の終了ではできるだけ return を使い、Environment.Exit は必要な場面に限定することです。Microsoftのドキュメントでも、Environment.Exit はプロセスを終了して終了コードをOSへ返すメソッドとして説明されており、return とは動作が異なります。Microsoft Learn

1-2. Application.Exit・Environment.Exit・returnの違い早見表

Application.ExitEnvironment.Exitreturn の違いを整理すると、次のようになります。

比較項目returnEnvironment.ExitApplication.Exit
主な用途メソッドを抜ける、Mainを終了するプロセス全体を即終了するWindowsフォームアプリを終了する
対象C#全般.NETアプリ全般Windows Forms
終了コードMainint の場合に返せる引数で指定できる基本的に指定しない
後続処理通常は実行される実行されない場合があるFormClosingなどのイベントが関係する
強制力低い高い中程度
通常終了向き
エラー終了向き

return はあくまで現在のメソッドから戻る命令です。ただし、Main メソッドで return すれば、C#プログラム全体の終了につながります。Mainint または Task<int> を返す場合、終了コードを外部へ返せます。Microsoft Learn

1-3. 「安全に終了」と「強制的に終了」の違いを理解する

C#の終了処理では、「安全に終了する」のか「強制的に終了する」のかを意識することが大切です。

安全な終了とは、処理を最後まで進め、ファイルの保存、DB接続の切断、ログ出力、Dispose などの後始末を行ってから終了することです。通常は returnusingtry-finally、フォームの CloseApplication.Exit などを使います。

一方、強制的な終了とは、現在の処理を途中で打ち切り、プロセス全体を終了させる方法です。代表例が Environment.Exit です。Environment.Exit はプロセスを終了するため強力ですが、finally ブロックが実行されない場合がある点に注意が必要です。Microsoft Learn

2. returnでC#プログラムを終了する方法

C#で最も基本的な終了方法は return です。特にコンソールアプリでは、処理を終えて自然に終了する場合、return を使うのがシンプルで安全です。

2-1. Mainメソッドでreturnするとプログラムが終了する

C#のコンソールアプリでは、Main メソッドがアプリケーションの入口です。そのため、Main メソッド内で return すると、Main の処理が終了し、プログラムも終了します。

C#
using System;

class Program
{
static void Main()
{
Console.WriteLine("処理を開始します。");

if (DateTime.Now.Hour < 9)
{
Console.WriteLine("まだ実行時間ではありません。終了します。");
return;
}

Console.WriteLine("処理を実行します。");
}
}

この例では、9時前であれば return; によって Main メソッドを抜けます。Main の後に実行する処理がなければ、そのままプログラムは終了します。

return は自然な終了方法なので、通常の分岐や入力チェックでプログラムを終了したい場合に向いています。

2-2. returnで終了コードを返す方法

C#では、Main メソッドの戻り値を int にすると、終了コードを返せます。

C#
using System;
using System.IO;

class Program
{
static int Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("ファイルパスを指定してください。");
return 1;
}

if (!File.Exists(args[0]))
{
Console.WriteLine("指定されたファイルが見つかりません。");
return 2;
}

Console.WriteLine("正常に処理が完了しました。");
return 0;
}
}

一般的に、終了コード 0 は正常終了、0 以外はエラーや異常終了を表します。MicrosoftのC#ドキュメントでも、int または Task<int> を返す Main によって、他のプログラムやスクリプトへ状態情報を渡せると説明されています。Microsoft Learn

バッチファイルやPowerShellからC#プログラムを実行する場合、この終了コードを利用して後続処理を分岐できます。

PowerShell
dotnet run
echo $LastExitCode

Windowsのバッチファイルでは、次のように %ERRORLEVEL% を確認できます。

bat
MyApp.exe
echo %ERRORLEVEL%

2-3. メソッド内のreturnではアプリ全体は終了しない点に注意

return は、書かれた場所によって意味が変わります。Main メソッドで return すればアプリ全体の終了につながりますが、通常のメソッド内で return しても、そのメソッドを抜けるだけです。

C#
using System;

class Program
{
static void Main()
{
Console.WriteLine("Main開始");

Check();

Console.WriteLine("Mainの処理は続きます。");
}

static void Check()
{
Console.WriteLine("Check開始");

return;

// ここには到達しない
}
}

このコードでは、Check メソッド内で return していますが、アプリ全体が終了するわけではありません。Check から Main に戻ったあと、Console.WriteLine("Mainの処理は続きます。"); が実行されます。

つまり、「アプリを終了したい」のか「現在のメソッドを抜けたい」のかを明確にすることが重要です。

2-4. コンソールアプリではreturnが基本的におすすめ

コンソールアプリで通常終了する場合は、基本的に return を使うのがおすすめです。

理由は、コードの流れが自然で、後始末処理も行いやすいからです。using を使っている場合はスコープを抜けると Dispose が呼ばれ、try-finally を使っている場合も通常の return であれば finally が実行されます。

C#
using System;
using System.IO;

class Program
{
static int Main()
{
try
{
using var writer = new StreamWriter("log.txt", append: true);
writer.WriteLine("処理を開始しました。");

// 何らかの処理
writer.WriteLine("処理が完了しました。");

return 0;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return 1;
}
}
}

このように、正常終了なら return 0;、エラーなら return 1; のように書くと、終了コードも明確になります。

3. Environment.ExitでC#プログラムを終了する方法

Environment.Exit は、C#でアプリケーション全体、つまり現在のプロセスを終了したいときに使うメソッドです。通常の return よりも強い終了方法です。

3-1. Environment.Exit(0)の基本的な使い方

Environment.Exit は、引数に終了コードを指定して呼び出します。

C#
using System;

class Program
{
static void Main()
{
Console.WriteLine("プログラムを終了します。");

Environment.Exit(0);

// ここは実行されない
Console.WriteLine("この行は表示されません。");
}
}

Environment.Exit(0); は、終了コード 0 をOSに返してプロセスを終了します。Microsoftのドキュメントでは、Environment.Exit(Int32) はプロセスを終了し、終了コードをオペレーティングシステムに返すメソッドとして説明されています。Microsoft Learn

3-2. 終了コード0と0以外の意味

終了コードは、プログラムの実行結果を外部へ伝えるための数値です。

終了コード一般的な意味
0正常終了
1一般的なエラー
2入力値エラー、ファイル形式エラーなど
その他アプリ独自のエラー

たとえば、ファイルが存在しない場合は 1、設定ファイルの形式が不正な場合は 2 のように、アプリ独自のルールを決められます。

C#
using System;
using System.IO;

class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("引数が指定されていません。");
Environment.Exit(1);
}

if (!File.Exists(args[0]))
{
Console.WriteLine("ファイルが存在しません。");
Environment.Exit(2);
}

Console.WriteLine("正常終了です。");
Environment.Exit(0);
}
}

Environment.Exit の引数では、正常終了には 0、エラーには 0 以外の値を使うのが一般的です。Microsoftのドキュメントでも、0 は正常完了、0 以外はエラーを示す値として使う説明があります。Microsoft Learn

3-3. Environment.Exitはアプリケーション全体を即終了する

Environment.Exit は、現在のメソッドだけではなく、アプリケーション全体を終了します。たとえば、深い階層のメソッドから呼び出しても、呼び出し元に戻らずにプロセスが終了します。

C#
using System;

class Program
{
static void Main()
{
Console.WriteLine("Main開始");

MethodA();

Console.WriteLine("この行は実行されません。");
}

static void MethodA()
{
MethodB();
}

static void MethodB()
{
Console.WriteLine("ここで終了します。");
Environment.Exit(1);
}
}

このように、Environment.Exit はアプリ全体を即終了させたい場合には便利です。ただし、通常の制御フローを飛ばしてしまうため、多用するとコードの見通しが悪くなります。

3-4. finallyや後続処理が実行されない場合がある

Environment.Exit を使うときに特に注意したいのが、finally ブロックや後続処理が実行されない場合があることです。

C#
using System;

class Program
{
static void Main()
{
try
{
Console.WriteLine("tryブロック");
Environment.Exit(1);
}
finally
{
Console.WriteLine("finallyブロック");
}
}
}

通常、try-finally では finally が実行されます。しかし、Environment.Exittry または catch ブロックで呼び出した場合、finally ブロックのコードが実行されないとMicrosoftのドキュメントで説明されています。Microsoft Learn

そのため、ファイルのクローズ、ログ出力、DB接続の切断などを確実に行いたい場合は、Environment.Exit を呼ぶ前に明示的に後始末を済ませる必要があります。

C#
try
{
// 処理
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);

// 必要な後始末をここで行う
SaveErrorLog(ex);

Environment.Exit(1);
}

3-5. 異常終了やエラー時に使うケース

Environment.Exit が向いているのは、主に次のようなケースです。

ケース
致命的なエラーが発生した必須設定ファイルがない
これ以上処理を続けられないDB接続に失敗した
バッチ処理で明確な終了コードを返したいCI/CDやスケジューラに失敗を伝える
どの階層からでも即終了したい初期化処理の途中で失敗した

ただし、通常のエラー処理であれば、例外を上位に投げる、Main で捕捉して return 1; を返す、という書き方の方が安全です。

C#
using System;

class Program
{
static int Main()
{
try
{
Run();
return 0;
}
catch (Exception ex)
{
Console.WriteLine($"エラー: {ex.Message}");
return 1;
}
}

static void Run()
{
// アプリのメイン処理
}
}

このように書けば、終了コードを返しつつ、finallyusing による後始末も行いやすくなります。

4. Application.ExitでC#アプリを終了する方法

Application.Exit は、Windowsフォームアプリを終了するときによく使うメソッドです。コンソールアプリではなく、System.Windows.Forms を使うアプリで利用します。

4-1. Application.Exitの基本的な使い方

Windowsフォームアプリでは、次のように Application.Exit(); を呼び出します。

C#
using System;
using System.Windows.Forms;

public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}

private void menuExit_Click(object sender, EventArgs e)
{
Application.Exit();
}
}

Application.Exit は、実行中のメッセージループを停止し、アプリケーションのウィンドウを閉じるためのメソッドです。Microsoftのドキュメントでは、すべての実行中のメッセージループを停止し、アプリケーションのすべてのウィンドウを閉じると説明されています。Microsoft Learn

4-2. Windowsフォームアプリでボタンから終了する例

ボタンを押したらアプリを終了する場合は、ボタンのクリックイベントで Application.Exit(); を呼び出します。

C#
using System;
using System.Windows.Forms;

public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}

private void buttonExit_Click(object sender, EventArgs e)
{
Application.Exit();
}
}

確認ダイアログを出してから終了したい場合は、次のように書けます。

C#
private void buttonExit_Click(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show(
"アプリケーションを終了しますか?",
"終了確認",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);

if (result == DialogResult.Yes)
{
Application.Exit();
}
}

このように、Windowsフォームアプリでは「終了ボタン」「終了メニュー」「閉じる処理」などから Application.Exit を呼ぶのが一般的です。

4-3. Application.Exitはメッセージループを終了するメソッド

Windowsフォームアプリは、通常 Application.Run(new MainForm()); によってメッセージループを開始します。

C#
using System;
using System.Windows.Forms;

internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}

Application.Exit は、このメッセージループを終了させ、Application.Run が戻るようにします。つまり、単に現在のメソッドを抜ける return とは役割が違います。

ただし、Application.Exit は必ずプロセスを強制終了するメソッドではありません。Microsoftのドキュメントでも、Application.Exit はアプリケーションを必ず強制終了するとは限らないと説明されています。Microsoft Learn

4-4. FormClosing・FormClosedイベントとの関係

Application.Exit を呼び出すと、開いているフォームに対して FormClosing イベントが発生します。終了がキャンセルされなければ、その後 FormClosed イベントが発生し、フォームが閉じられます。Microsoftのドキュメントでも、Application.Exit は各フォームの FormClosing を発生させ、キャンセルされなければ FormClosed を発生させると説明されています。Microsoft Learn

C#
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult result = MessageBox.Show(
"終了してもよろしいですか?",
"確認",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);

if (result == DialogResult.No)
{
e.Cancel = true;
}
}

FormClosing イベントでは、e.Cancel = true; を設定すると終了をキャンセルできます。未保存のデータがある場合に確認ダイアログを出す処理などでよく使います。

4-5. キャンセル処理があると終了しない場合がある

Application.Exit を呼んでもアプリが終了しない場合、FormClosing イベント内で終了がキャンセルされている可能性があります。

C#
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (HasUnsavedChanges())
{
MessageBox.Show("未保存のデータがあります。");
e.Cancel = true;
}
}

このように e.Cancel = true; が実行されると、フォームは閉じられず、アプリも終了しません。Microsoftのドキュメントでも、FormClosingEventArgsCanceltrue に設定されると、Application.Exit はそれ以上の処理を行わず戻ると説明されています。Microsoft Learn

「終了ボタンを押しても終了しない」というトラブルでは、まず FormClosing イベントを確認しましょう。

5. アプリの種類別に見るC#の終了方法

C#といっても、コンソールアプリ、Windowsフォームアプリ、WPFアプリ、バッチ処理、常駐アプリなど、アプリの種類によって適切な終了方法は異なります。

5-1. コンソールアプリを終了する場合

コンソールアプリでは、基本的に return を使うのがおすすめです。

C#
static int Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("引数が不足しています。");
return 1;
}

Console.WriteLine("正常終了しました。");
return 0;
}

通常終了なら return 0;、エラーなら return 1; のように書くと、終了コードも明確になります。

どうしても処理の途中でプロセス全体を終了したい場合は、Environment.Exit を使います。

C#
if (CriticalErrorOccurred())
{
Environment.Exit(1);
}

ただし、後始末処理を重視するなら、例外を上位へ投げて Main で捕捉し、return で終了コードを返す形の方が扱いやすいです。

5-2. Windowsフォームアプリを終了する場合

Windowsフォームアプリでは、Application.Exit(); を使います。

C#
private void buttonExit_Click(object sender, EventArgs e)
{
Application.Exit();
}

現在のフォームだけを閉じたい場合は、this.Close(); を使います。

C#
private void buttonClose_Click(object sender, EventArgs e)
{
this.Close();
}

this.Close() はそのフォームを閉じる処理であり、Application.Exit() はアプリケーション全体の終了を意図した処理です。メインフォームを閉じれば結果的にアプリが終了することもありますが、複数フォームがある場合は動作の違いに注意しましょう。

5-3. WPFアプリを終了する場合

WPFアプリでは、Windows Formsの Application.Exit ではなく、通常は Application.Current.Shutdown(); を使います。

C#
private void Exit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}

終了コードを返したい場合は、次のように指定できます。

C#
Application.Current.Shutdown(0);

WPFでは ShutdownMode によって、最後のウィンドウが閉じたときに終了するのか、メインウィンドウが閉じたときに終了するのか、明示的に Shutdown を呼ぶまで終了しないのかが変わります。MicrosoftのWPFドキュメントでは、ShutdownMode の既定値は OnLastWindowClose で、最後のウィンドウが閉じられると自動的にシャットダウンされると説明されています。Microsoft Learn

XML
<Application
x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShutdownMode="OnMainWindowClose">
</Application>

ShutdownMode="OnExplicitShutdown" にしている場合は、ウィンドウを閉じてもアプリが残ることがあります。その場合は Application.Current.Shutdown(); を明示的に呼び出します。Microsoft Learn

5-4. バックグラウンド処理や別スレッドがある場合

C#アプリで別スレッドやタスクを動かしている場合、終了処理には注意が必要です。

.NETでは、フォアグラウンドスレッドが残っているとプロセスが終了しない場合があります。一方、バックグラウンドスレッドはプロセスの終了を妨げず、すべてのフォアグラウンドスレッドが終了すると残りのバックグラウンドスレッドは停止されます。Microsoft Learn

C#
using System;
using System.Threading;

class Program
{
static void Main()
{
Thread thread = new Thread(() =>
{
while (true)
{
Console.WriteLine("処理中...");
Thread.Sleep(1000);
}
});

thread.IsBackground = true;
thread.Start();

Console.WriteLine("Enterで終了します。");
Console.ReadLine();
}
}

thread.IsBackground = true; にすると、そのスレッドはプロセスの終了を妨げません。ただし、処理が途中で止まる可能性があるため、必要であればキャンセルトークンなどを使って安全に停止させましょう。

C#
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
using CancellationTokenSource cts = new CancellationTokenSource();

Task task = Task.Run(async () =>
{
while (!cts.Token.IsCancellationRequested)
{
Console.WriteLine("バックグラウンド処理中...");
await Task.Delay(1000);
}
});

Console.WriteLine("Enterで終了します。");
Console.ReadLine();

cts.Cancel();
await task;
}
}

5-5. 単体テストやバッチ処理で終了コードを返す場合

単体テストやバッチ処理、CI/CDで実行するC#プログラムでは、終了コードが重要になることがあります。

C#
static int Main()
{
bool success = RunBatch();

if (success)
{
return 0;
}

return 1;
}

このようにしておくと、呼び出し元のスクリプトが成功・失敗を判定できます。

bat
MyBatchApp.exe

if %ERRORLEVEL% neq 0 (
echo バッチ処理に失敗しました。
exit /b 1
)

echo バッチ処理に成功しました。

バッチ処理では Environment.Exit でも終了コードを返せますが、通常は Main から return する方がテストしやすく、後始末も行いやすいです。

6. Application.Exit・Environment.Exit・returnの使い分け

ここまでの内容を踏まえて、Application.ExitEnvironment.Exitreturn の使い分けを整理します。

6-1. 通常終了ならreturnを使う

通常終了であれば、まず return を使いましょう。

C#
static int Main()
{
Console.WriteLine("処理完了");
return 0;
}

return は処理の流れが自然で、usingfinally とも相性が良い方法です。コンソールアプリやバッチ処理では、最も基本的な終了方法と考えて問題ありません。

また、メソッド内で処理を打ち切りたいだけの場合も return が適しています。

C#
void Save(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return;
}

// 保存処理
}

この場合、アプリ全体を終了するのではなく、Save メソッドを抜けるだけです。

6-2. WindowsフォームならApplication.Exitを使う

Windowsフォームアプリを終了する場合は、Application.Exit を使います。

C#
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}

Application.Exit はフォームアプリのメッセージループや開いているフォームと関係する終了方法です。Windowsフォームでは、終了前に FormClosing イベントで確認処理を行うこともできます。

C#
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("終了しますか?", "確認",
MessageBoxButtons.YesNo) == DialogResult.No)
{
e.Cancel = true;
}
}

6-3. プロセスを確実に終了したいならEnvironment.Exitを使う

プロセスを確実に終了したい場合は、Environment.Exit を使います。

C#
Environment.Exit(1);

Environment.Exit は、他のスレッドが実行中でもアプリケーションを即終了させる動作をします。Microsoftのドキュメントでも、Environment.Exit は常にアプリケーションを終了し、return はアプリケーションのエントリポイントで使われた場合にのみ終了につながると説明されています。Microsoft Learn

ただし、強制力が高い分、後始末処理がスキップされる可能性があるため、通常終了には向きません。

6-4. 後始末処理を重視するなら強制終了は避ける

ファイル、DB接続、ネットワーク接続、ログ、排他ロックなどを扱っている場合は、強制終了を避けた方が安全です。

C#
using var connection = CreateConnection();

try
{
// DB処理
return 0;
}
catch (Exception ex)
{
// ログ出力
Console.WriteLine(ex.Message);
return 1;
}

using を使っておけば、スコープを抜けるときにリソースを解放できます。Environment.Exit を途中で呼ぶと、通常の制御フローを飛ばすため、このような後始末が期待どおりに動かない場合があります。

後始末を重視するなら、次の順番で考えるのがおすすめです。

優先度方法
1return で自然に終了する
2例外を上位で捕捉し、Main で終了コードを返す
3UIアプリでは適切な終了メソッドを使う
4最後の手段として Environment.Exit を使う

6-5. 迷ったときの判断フロー

C#でどの終了方法を使うか迷ったら、次の流れで判断してください。

コンソールアプリですか?
├─ はい
│ ├─ 通常終了ですか? → return
│ ├─ 終了コードを返したいですか? → int Main で return 0 / return 1
│ └─ 今すぐプロセスを終了したいですか? → Environment.Exit

└─ いいえ
├─ Windowsフォームですか? → Application.Exit
├─ WPFですか? → Application.Current.Shutdown
└─ サービス・常駐処理ですか? → キャンセル通知で安全に停止

基本的には、通常終了は return、Windowsフォームは Application.Exit、強制終了は Environment.Exit と覚えておくとよいでしょう。

7. C#で終了処理を書くときの注意点

終了処理は、単にプログラムを閉じるだけではありません。安全に終了するためには、リソース解放や非同期処理、確認ダイアログなども考える必要があります。

7-1. ファイル・DB接続・ログの後始末を忘れない

C#アプリを終了する前に、次のような後始末が必要になることがあります。

対象必要な後始末
ファイル書き込み完了、クローズ
DB接続トランザクション完了、接続解除
ログ最後のログ出力、フラッシュ
ネットワーク接続終了、送信完了
一時ファイル削除
ロックファイル解除

特に Environment.Exit を使う場合、これらの処理が実行されないまま終了する可能性があります。終了前に必要な処理を明示的に行う設計にしましょう。

C#
try
{
// メイン処理
}
finally
{
// 終了前に必ず行いたい処理
Console.WriteLine("終了処理を実行します。");
}

通常の return であれば finally が実行されますが、Environment.Exit を呼び出すと finally が実行されない場合があります。Microsoft Learn

7-2. usingやDisposeでリソースを解放する

ファイルやDB接続など、解放が必要なオブジェクトは using を使うのがおすすめです。

C#
using System.IO;

class Program
{
static void Main()
{
using StreamWriter writer = new StreamWriter("log.txt", append: true);

writer.WriteLine("ログを書き込みます。");

// スコープを抜けるとDisposeされる
}
}

using を使うことで、処理が終わったときに自動的に Dispose が呼ばれます。終了処理を安全に書きたい場合は、Environment.Exit で途中終了するより、スコープを抜けて自然に終了する設計にした方がよいです。

7-3. 非同期処理中に終了する場合の注意点

非同期処理中にアプリを終了すると、処理が途中で止まったり、データが保存されなかったりする可能性があります。

C#
static async Task Main()
{
await SaveAsync();
}

C#の Mainasync Task Mainasync Task<int> Main として定義できます。Microsoftのドキュメントでも、非同期メソッドを呼び出す必要がある場合は Task または Task<int> を戻り値に使うと説明されています。Microsoft Learn

終了時に非同期処理を安全に止めたい場合は、CancellationToken を使いましょう。

C#
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
using CancellationTokenSource cts = new CancellationTokenSource();

Task work = DoWorkAsync(cts.Token);

Console.WriteLine("Enterで終了します。");
Console.ReadLine();

cts.Cancel();

try
{
await work;
}
catch (OperationCanceledException)
{
Console.WriteLine("処理をキャンセルしました。");
}
}

static async Task DoWorkAsync(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(1000, token);
Console.WriteLine("処理中...");
}
}
}

このように、いきなり終了するのではなく、キャンセル要求を出してから処理を終える方が安全です。

7-4. 終了前に確認ダイアログを表示する方法

Windowsフォームアプリでは、終了前に確認ダイアログを表示できます。

C#
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult result = MessageBox.Show(
"終了してもよろしいですか?",
"終了確認",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);

if (result == DialogResult.No)
{
e.Cancel = true;
}
}

WPFアプリの場合は、Closing イベントで確認できます。

C#
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var result = MessageBox.Show(
"終了してもよろしいですか?",
"終了確認",
MessageBoxButton.YesNo,
MessageBoxImage.Question);

if (result == MessageBoxResult.No)
{
e.Cancel = true;
}
}

ただし、WPFで Application.Current.Shutdown() を明示的に呼び出すと、ShutdownMode の設定に関係なく終了処理が進みます。Shutdown を呼ぶと、開いているウィンドウの Closing イベントがキャンセルされていてもアプリケーションがシャットダウンされるとMicrosoftのドキュメントで説明されています。Microsoft Learn

7-5. Environment.Exitを多用しない方がよい理由

Environment.Exit は便利ですが、多用しない方がよいです。

主な理由は次のとおりです。

理由内容
後始末が飛ぶ可能性があるfinally や後続処理が実行されない場合がある
テストしづらいメソッドを呼ぶとテストプロセスまで終了することがある
コードの流れが追いにくいどこからでも終了できるため可読性が下がる
再利用しづらいライブラリ内で呼ぶと呼び出し元を巻き込む

特にライブラリや共通クラスの中で Environment.Exit を呼ぶのは避けましょう。呼び出し元のアプリケーション全体を終了させてしまうためです。

悪い例は次のようなコードです。

C#
public class FileValidator
{
public void Validate(string path)
{
if (!File.Exists(path))
{
Environment.Exit(1);
}
}
}

この場合は、例外を投げるか、結果を返す形にする方が安全です。

C#
public class FileValidator
{
public bool Validate(string path)
{
return File.Exists(path);
}
}

または、例外で呼び出し元へ伝えます。

C#
public class FileValidator
{
public void Validate(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException("ファイルが見つかりません。", path);
}
}
}

8. C#でプログラムが終了しない原因と対処法

returnApplication.ExitEnvironment.Exit を使っているのに、C#プログラムが終了しないことがあります。原因はアプリの種類によって異なります。

8-1. FormClosingイベントで終了がキャンセルされている

Windowsフォームアプリで Application.Exit を呼んでも終了しない場合、FormClosing イベントで e.Cancel = true; が設定されている可能性があります。

C#
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}

このコードがあると、フォームの終了は常にキャンセルされます。確認ダイアログの条件分岐が正しいか、未保存データの判定が常に真になっていないかを確認しましょう。

C#
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (!HasUnsavedChanges())
{
return;
}

var result = MessageBox.Show(
"未保存の変更があります。終了しますか?",
"確認",
MessageBoxButtons.YesNo);

if (result == DialogResult.No)
{
e.Cancel = true;
}
}

8-2. 別スレッドやタスクが残っている

コンソールアプリやデスクトップアプリで、別スレッドが残っていると終了しないことがあります。

C#
Thread thread = new Thread(() =>
{
while (true)
{
Thread.Sleep(1000);
}
});

thread.Start();

Thread クラスで作成したスレッドは、既定ではフォアグラウンドスレッドです。フォアグラウンドスレッドが残っていると、アプリケーションの終了を妨げる場合があります。Microsoftのドキュメントでは、Thread コンストラクターで作成したスレッドは既定でフォアグラウンドスレッドとして実行されると説明されています。Microsoft Learn

対処法としては、次のような方法があります。

C#
thread.IsBackground = true;

または、キャンセルトークンなどでスレッドの処理を安全に停止します。

C#
cts.Cancel();

8-3. バックグラウンドではないスレッドが動作している

.NETでは、バックグラウンドスレッドはプロセスの終了を妨げませんが、フォアグラウンドスレッドは終了を妨げることがあります。

C#
Thread worker = new Thread(Work);
worker.IsBackground = false;
worker.Start();

このようなスレッドが無限ループしていると、Main メソッドが終了してもプロセスが残ることがあります。

終了時には、次のように停止フラグを使う方法があります。

C#
private static bool _running = true;

static void Work()
{
while (_running)
{
Thread.Sleep(1000);
}
}

static void Main()
{
Thread worker = new Thread(Work);
worker.Start();

Console.ReadLine();

_running = false;
worker.Join();
}

ただし、実務では単純な bool よりも、CancellationToken やスレッドセーフな制御を使う方が安全です。

8-4. Application.Exitを呼んでもプロセスが残る場合

Application.Exit はWindowsフォームのメッセージループを終了させ、フォームを閉じるためのメソッドですが、プロセスを必ず強制終了するメソッドではありません。Microsoftのドキュメントでも、Application.Exit は必ずしもアプリケーションを強制終了しないと説明されています。Microsoft Learn

プロセスが残る場合は、次の点を確認しましょう。

確認項目内容
FormClosing でキャンセルしていないかe.Cancel = true が実行されていないか
別スレッドが残っていないかフォアグラウンドスレッドが動いていないか
タイマーが動いていないかSystem.Threading.Timer など
常駐処理が残っていないか通知アイコン、バックグラウンド処理
外部プロセスを待っていないかWaitForExit など

どうしてもプロセスを終了させる必要がある場合は、最後の手段として Environment.Exit を使うこともあります。

C#
Application.Exit();

// 必要な後始末を済ませたあと、最終手段として使う
// Environment.Exit(0);

ただし、Environment.Exit を安易に追加する前に、なぜ終了しないのかを調べることが重要です。

8-5. デバッグ実行時と通常実行時の違い

Visual Studioでデバッグ実行している場合、通常実行とは見え方が異なることがあります。

たとえば、コンソールアプリでは処理が終了しても、デバッグコンソールが残っているように見える場合があります。また、Visual Studioの出力ウィンドウにログが表示されるため、プロセスがまだ動いているように感じることもあります。

確認する場合は、次の点を見てください。

確認方法内容
タスクマネージャープロセスが残っているか確認
Visual Studioのデバッグ停止実行中かどうか確認
ログ出力終了処理まで到達しているか確認
終了コードreturnEnvironment.Exit の値を確認

デバッグ中だけ終了しない場合は、ブレークポイント、待機処理、入力待ち、スレッドの残存などを確認しましょう。

9. C#の終了処理でよくある質問

ここでは、C#の終了処理でよくある質問に答えます。

9-1. C#でアプリを強制終了するには?

C#でアプリを強制終了したい場合は、Environment.Exit を使います。

C#
Environment.Exit(0);

エラー終了として扱いたい場合は、0 以外の終了コードを指定します。

C#
Environment.Exit(1);

ただし、Environment.Exit は強制力が高く、finally が実行されない場合があります。後始末が必要な処理では、できるだけ return や例外処理を使って自然に終了する方が安全です。Microsoft Learn

9-2. Application.ExitとCloseの違いは?

Application.Exit は、Windowsフォームアプリ全体を終了したいときに使います。一方、Close は特定のフォームを閉じるメソッドです。

C#
// アプリ全体の終了を意図する
Application.Exit();
C#
// 現在のフォームを閉じる
this.Close();

メインフォームで this.Close(); を呼ぶと結果的にアプリが終了する場合もありますが、複数フォームがある場合や、メッセージループの構成によっては期待どおりにならないことがあります。

アプリ全体を終了したいなら Application.Exit、現在の画面だけ閉じたいなら this.Close() と考えるとよいでしょう。

9-3. Environment.Exitとreturnはどちらを使うべき?

通常は return を使うべきです。

C#
static int Main()
{
return 0;
}

Environment.Exit は、プロセス全体を即終了させたい特別な場面で使います。

C#
Environment.Exit(1);

return は自然な制御フローで、後始末処理も行いやすいです。一方、Environment.Exit はアプリケーションを即終了させるため、finally や後続処理が実行されない場合があります。Microsoftのドキュメントでも、Environment.Exitreturn の違いとして、Exit は即時終了し、return はエントリポイントで使われた場合に終了につながると説明されています。Microsoft Learn

9-4. 終了コードは何を指定すればよい?

基本的には、正常終了なら 0、エラーなら 0 以外を指定します。

C#
return 0; // 正常終了
return 1; // エラー

または、Environment.Exit を使う場合は次のようにします。

C#
Environment.Exit(0); // 正常終了
Environment.Exit(1); // エラー

エラーの種類ごとに終了コードを分けたい場合は、定数や列挙型を用意するとわかりやすくなります。

C#
enum ExitCode
{
Success = 0,
GeneralError = 1,
FileNotFound = 2,
InvalidFormat = 3
}
C#
return (int)ExitCode.FileNotFound;

9-5. ボタンを押したらアプリを終了するには?

Windowsフォームアプリでボタンを押して終了するには、クリックイベントで Application.Exit(); を呼び出します。

C#
private void buttonExit_Click(object sender, EventArgs e)
{
Application.Exit();
}

確認ダイアログを表示する場合は、次のようにします。

C#
private void buttonExit_Click(object sender, EventArgs e)
{
var result = MessageBox.Show(
"終了しますか?",
"確認",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);

if (result == DialogResult.Yes)
{
Application.Exit();
}
}

WPFアプリの場合は、次のように Application.Current.Shutdown(); を呼び出します。

C#
private void ButtonExit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}

Windowsフォームでは Application.Exit、WPFでは Application.Current.Shutdown と覚えておきましょう。

まとめ

C#でプログラムを終了する方法には、主に returnEnvironment.ExitApplication.Exit の3つがあります。

方法使う場面
returnコンソールアプリの通常終了、メソッドの処理中断
Environment.Exitプロセス全体を即終了したい場合、異常終了時
Application.ExitWindowsフォームアプリを終了したい場合

コンソールアプリでは、基本的に return を使うのがおすすめです。Main メソッドを int にすれば、return 0;return 1; で終了コードを返せます。

Environment.Exit は、アプリケーション全体を即終了させる強力な方法です。ただし、finally や後続処理が実行されない場合があるため、ファイル保存やDB切断などの後始末が必要な処理では注意が必要です。

Windowsフォームアプリでは、Application.Exit(); を使います。終了時には FormClosing イベントで確認ダイアログを出したり、未保存データがある場合に終了をキャンセルしたりできます。

C#の終了処理で迷った場合は、次のように判断しましょう。

通常終了 → return
終了コードを返したい → int Main で return
Windowsフォームを終了したい → Application.Exit
WPFを終了したい → Application.Current.Shutdown
強制的にプロセスを終了したい → Environment.Exit

終了処理は、アプリの安定性に関わる重要な部分です。単に「終了できればよい」と考えるのではなく、後始末処理、終了コード、スレッド、非同期処理、UIイベントまで含めて、安全に終了できる設計にしましょう。