C# try-catchの使い方を初心者向けに解説|例外処理の基本・書き方・よくあるエラー対策

はじめに

C#でプログラムを書いていると、入力値が正しくなかったり、ファイルが存在しなかったり、ネットワーク通信に失敗したりして、処理が途中で止まってしまうことがあります。このような予期しない問題に対応するために使うのが、C#のtry-catchです。

try-catchを使うと、例外が発生したときにプログラムを異常終了させず、エラーメッセージを表示したり、別の処理に切り替えたりできます。

この記事では、C#のtry-catchの基本構文、使い方、よくあるエラー、finallythrowとの関係まで、初心者向けにわかりやすく解説します。

1. C#のtry-catchとは?例外処理の基本を初心者向けに解説

1-1. 例外処理とは何か

例外処理とは、プログラムの実行中に発生した予期しない問題に対応するための仕組みです。

たとえば、次のような状況では例外が発生することがあります。

C#
int number = int.Parse("abc");

このコードでは、文字列"abc"を整数に変換しようとしています。しかし、"abc"は数値ではないため、FormatExceptionという例外が発生します。

例外処理をしていない場合、プログラムはそこで停止してしまいます。そこでtry-catchを使い、例外が発生しても安全に処理を続けられるようにします。

1-2. try-catchを使う目的

C#でtry-catchを使う主な目的は、例外が発生したときにプログラムの異常終了を防ぐことです。

たとえば、ユーザーが入力した値を数値に変換する処理では、必ず正しい数値が入力されるとは限りません。そこでtry-catchを使うと、変換に失敗した場合でも「数値を入力してください」といったメッセージを表示できます。

C#
try
{
int number = int.Parse("abc");
}
catch
{
Console.WriteLine("数値に変換できませんでした。");
}

このように、例外が発生する可能性がある処理を安全に扱うためにtry-catchを使います。

1-3. エラーと例外の違い

初心者が混同しやすい言葉に「エラー」と「例外」があります。

エラーは、プログラムに問題がある状態全般を指す広い言葉です。一方、例外は、C#のプログラム実行中に発生する問題をオブジェクトとして扱う仕組みです。

たとえば、次のようなものは例外です。

C#
FormatException
NullReferenceException
IOException
DivideByZeroException

C#では、例外が発生するとExceptionクラスをもとにしたオブジェクトが作られます。そして、その例外をcatchで受け取って処理できます。

1-4. try-catchが必要になる代表的な場面

try-catchがよく使われる場面には、次のようなものがあります。

ファイル操作では、ファイルが存在しない、アクセス権限がない、別のアプリで使用中といった理由で例外が発生することがあります。

数値変換では、ユーザー入力が数値ではない場合にFormatExceptionが発生します。

データベース処理では、接続先が見つからない、SQLの実行に失敗する、タイムアウトするといった例外が発生する可能性があります。

API通信では、通信エラー、サーバーエラー、タイムアウトなどが起こることがあります。

このように、外部環境やユーザー入力に左右される処理では、try-catchを使って例外に備えることが大切です。

2. C# try-catchの基本構文と書き方

2-1. tryブロックの役割

tryブロックには、例外が発生する可能性のある処理を書きます。

C#
try
{
// 例外が発生する可能性のある処理
}

たとえば、ファイルを読み込む処理や、文字列を数値に変換する処理などをtryブロックの中に書きます。

tryブロック内で例外が発生すると、その時点で以降の処理はスキップされ、対応するcatchブロックに処理が移ります。

2-2. catchブロックの役割

catchブロックには、例外が発生したときの処理を書きます。

C#
catch
{
// 例外が発生したときの処理
}

たとえば、エラーメッセージを表示したり、ログを出力したり、代わりの値を設定したりします。

例外の内容を詳しく知りたい場合は、次のように例外オブジェクトを受け取ります。

C#
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

ex.Messageには、例外の内容を表すメッセージが入っています。

2-3. Exceptionクラスの基本

C#の例外は、基本的にExceptionクラスをもとにしています。

Exceptionクラスには、例外に関する情報が含まれています。よく使うプロパティは次のとおりです。

C#
ex.Message
ex.StackTrace
ex.InnerException

Messageは例外の説明です。

StackTraceは、例外がどこで発生したかを追跡するための情報です。

InnerExceptionは、例外の内側に別の例外がある場合に使います。

初心者のうちは、まずex.Messageで例外内容を確認するところから始めるとよいでしょう。

2-4. 最小構成のtry-catchサンプルコード

C#のtry-catchは、次のように書きます。

C#
try
{
int number = int.Parse("abc");
Console.WriteLine(number);
}
catch (Exception ex)
{
Console.WriteLine("例外が発生しました。");
Console.WriteLine(ex.Message);
}

このコードでは、"abc"を整数に変換しようとして例外が発生します。そのため、catchブロックが実行されます。

実行結果の例は次のようになります。

例外が発生しました。
The input string 'abc' was not in a correct format.

環境によって例外メッセージの表示内容は異なる場合がありますが、基本的な流れは同じです。

3. C# try-catchの使い方をサンプルコードで理解する

3-1. 0除算の例外をcatchする例

整数を0で割ると、DivideByZeroExceptionが発生します。

C#
try
{
int x = 10;
int y = 0;
int result = x / y;

Console.WriteLine(result);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0で割ることはできません。");
Console.WriteLine(ex.Message);
}

この例では、x / yの部分で例外が発生します。そのため、Console.WriteLine(result);は実行されず、catchブロックに処理が移ります。

3-2. ファイル読み込み時の例外をcatchする例

存在しないファイルを読み込もうとすると、例外が発生します。

C#
try
{
string text = File.ReadAllText("sample.txt");
Console.WriteLine(text);
}
catch (FileNotFoundException ex)
{
Console.WriteLine("ファイルが見つかりません。");
Console.WriteLine(ex.Message);
}
catch (IOException ex)
{
Console.WriteLine("ファイルの読み込み中にエラーが発生しました。");
Console.WriteLine(ex.Message);
}

ファイル操作では、ファイルが存在しない場合だけでなく、アクセス権限がない場合や、別のプロセスが使用している場合にも例外が発生します。

そのため、ファイル操作ではtry-catchを使って安全に処理することが重要です。

3-3. 数値変換時の例外をcatchする例

ユーザー入力を数値に変換する処理では、FormatExceptionがよく発生します。

C#
try
{
Console.WriteLine("年齢を入力してください。");
string input = Console.ReadLine();

int age = int.Parse(input);

Console.WriteLine($"あなたの年齢は{age}歳です。");
}
catch (FormatException ex)
{
Console.WriteLine("数値を入力してください。");
Console.WriteLine(ex.Message);
}

int.Parseは、変換できない文字列が渡されると例外を発生させます。

ただし、ユーザー入力の数値変換では、try-catchよりもint.TryParseを使う方が適している場合もあります。

C#
Console.WriteLine("年齢を入力してください。");
string input = Console.ReadLine();

if (int.TryParse(input, out int age))
{
Console.WriteLine($"あなたの年齢は{age}歳です。");
}
else
{
Console.WriteLine("数値を入力してください。");
}

予測できる入力ミスは、例外処理ではなく事前チェックで対応するのが基本です。

3-4. 例外メッセージを表示する方法

例外メッセージを表示するには、catchで例外オブジェクトを受け取り、Messageプロパティを使います。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

開発中はex.Messageex.StackTraceを確認すると、原因の特定に役立ちます。

C#
catch (Exception ex)
{
Console.WriteLine("エラー内容:");
Console.WriteLine(ex.Message);

Console.WriteLine("発生場所:");
Console.WriteLine(ex.StackTrace);
}

ただし、実際のアプリケーションでは、ユーザーにStackTraceをそのまま表示するのは避けましょう。内部構造やファイルパスなど、見せるべきでない情報が含まれることがあるためです。

4. 複数のcatchを使って例外ごとに処理を分ける方法

4-1. 複数catchの基本構文

C#では、複数のcatchを書くことで、例外の種類ごとに処理を分けられます。

C#
try
{
// 例外が発生する可能性のある処理
}
catch (FormatException ex)
{
// 数値変換などの形式エラー
}
catch (IOException ex)
{
// ファイル入出力エラー
}
catch (Exception ex)
{
// その他の例外
}

複数のcatchを書く場合は、具体的な例外クラスを先に書き、最後にExceptionを書くのが基本です。

4-2. FormatExceptionをcatchする例

FormatExceptionは、文字列の形式が正しくないときに発生します。

C#
try
{
string input = "abc";
int number = int.Parse(input);

Console.WriteLine(number);
}
catch (FormatException ex)
{
Console.WriteLine("入力形式が正しくありません。");
Console.WriteLine(ex.Message);
}

この例では、"abc"を整数に変換できないため、FormatExceptionが発生します。

4-3. IOExceptionをcatchする例

IOExceptionは、ファイルやストリームなどの入出力処理で問題が発生したときに使われる例外です。

C#
try
{
string text = File.ReadAllText("data.txt");
Console.WriteLine(text);
}
catch (IOException ex)
{
Console.WriteLine("ファイル処理中にエラーが発生しました。");
Console.WriteLine(ex.Message);
}

ファイル操作では、IOExceptionのほかにFileNotFoundExceptionUnauthorizedAccessExceptionなどが発生することもあります。

必要に応じて、より具体的な例外を個別にcatchすると、原因に応じた処理を書きやすくなります。

4-4. Exceptionを最後に書く理由

Exceptionは、多くの例外クラスの基底クラスです。そのため、catch (Exception ex)は非常に広い範囲の例外を受け取ります。

次のように、Exceptionを先に書くと問題になります。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine("例外が発生しました。");
}
catch (FormatException ex)
{
Console.WriteLine("形式が正しくありません。");
}

この場合、FormatExceptionExceptionとして先に捕まえられてしまうため、後ろのcatch (FormatException ex)には到達できません。

そのため、catchは具体的な例外から順番に書き、最後に汎用的なExceptionを書く必要があります。

4-5. catchの順番で起きやすいエラー

複数のcatchを書くときに順番を間違えると、コンパイルエラーになることがあります。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine("例外が発生しました。");
}
catch (FormatException ex)
{
Console.WriteLine("形式エラーです。");
}

このコードでは、FormatExceptionExceptionより後に書かれているため、FormatExceptioncatchが実行される可能性がありません。

正しくは次のように書きます。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException ex)
{
Console.WriteLine("形式エラーです。");
}
catch (Exception ex)
{
Console.WriteLine("その他の例外が発生しました。");
}

catchの順番は、具体的な例外から抽象的な例外へ並べると覚えておきましょう。

5. finallyの使い方|例外の有無に関係なく処理を実行する

5-1. finallyとは何か

finallyは、例外が発生してもしなくても必ず実行されるブロックです。

C#
try
{
// 例外が発生する可能性のある処理
}
catch (Exception ex)
{
// 例外発生時の処理
}
finally
{
// 必ず実行したい処理
}

たとえば、ファイルを閉じる処理、データベース接続を閉じる処理、一時的に確保したリソースを解放する処理などに使います。

5-2. finallyが実行されるタイミング

finallyは、次のような場合に実行されます。

例外が発生しなかった場合でも実行されます。

例外が発生してcatchされた場合でも実行されます。

trycatchの中でreturnした場合でも、通常はfinallyが実行されます。

C#
try
{
Console.WriteLine("tryを実行します。");
}
catch
{
Console.WriteLine("catchを実行します。");
}
finally
{
Console.WriteLine("finallyを実行します。");
}

例外が発生しない場合でも、最後にfinallyの処理が実行されます。

5-3. ファイルやリソースを閉じる処理の例

ファイルを扱う場合、処理が終わったらファイルを閉じる必要があります。

C#
StreamReader reader = null;

try
{
reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();

Console.WriteLine(text);
}
catch (IOException ex)
{
Console.WriteLine("ファイルの読み込みに失敗しました。");
Console.WriteLine(ex.Message);
}
finally
{
if (reader != null)
{
reader.Close();
Console.WriteLine("ファイルを閉じました。");
}
}

この例では、ファイル読み込み中に例外が発生しても、finallyでファイルを閉じる処理が実行されます。

ただし、現在のC#では、リソース解放にはusing文を使う方が一般的です。

5-4. finallyとusing文の使い分け

using文は、使い終わったリソースを自動的に解放するための構文です。

C#
try
{
using (StreamReader reader = new StreamReader("sample.txt"))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
}
catch (IOException ex)
{
Console.WriteLine("ファイルの読み込みに失敗しました。");
Console.WriteLine(ex.Message);
}

usingを使うと、CloseDisposeを明示的に書かなくても、処理が終わったタイミングでリソースが解放されます。

ファイル、データベース接続、ネットワーク接続など、IDisposableを実装しているオブジェクトにはusingを使うのが基本です。

一方で、リソース解放以外にも必ず実行したい処理がある場合は、finallyを使うとよいでしょう。

6. try-catchでよくあるエラーと対策

6-1. catchに到達しない原因

catchに到達しない原因として多いのは、そもそもtryブロック内で例外が発生していないケースです。

C#
try
{
int number = 10;
Console.WriteLine(number);
}
catch
{
Console.WriteLine("例外が発生しました。");
}

このコードでは例外が発生しないため、catchは実行されません。

また、try-catchの外側で例外が発生している場合も、対象のcatchには入りません。

C#
int number = int.Parse("abc");

try
{
Console.WriteLine(number);
}
catch
{
Console.WriteLine("例外が発生しました。");
}

この場合、例外が発生しているのはtryの外なので、catchでは受け取れません。

6-2. 例外の型が合っていない場合

catchで指定した例外の型が、実際に発生した例外と合っていない場合、そのcatchでは処理されません。

C#
try
{
int number = int.Parse("abc");
}
catch (IOException ex)
{
Console.WriteLine("入出力エラーです。");
}

このコードで発生するのはFormatExceptionです。しかし、catchしているのはIOExceptionなので、このcatchでは処理されません。

正しくは次のように書きます。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException ex)
{
Console.WriteLine("数値形式が正しくありません。");
}

どの例外が発生する可能性があるのかを理解して、適切な例外クラスをcatchすることが大切です。

6-3. catchの順番が原因でコンパイルエラーになる場合

複数のcatchを書く場合、親クラスの例外を先に書くと、子クラスの例外に到達できなくなります。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine("例外です。");
}
catch (FormatException ex)
{
Console.WriteLine("形式エラーです。");
}

このようなコードは、FormatExceptioncatchが実行される可能性がないため、コンパイルエラーになります。

正しくは、具体的な例外を先に書きます。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException ex)
{
Console.WriteLine("形式エラーです。");
}
catch (Exception ex)
{
Console.WriteLine("その他の例外です。");
}

6-4. 例外を握りつぶして原因が分からなくなる場合

初心者がやりがちな失敗に、catchの中を空にしてしまうケースがあります。

C#
try
{
int number = int.Parse("abc");
}
catch
{
}

このように書くと、例外が発生しても何も表示されず、何が起きたのか分からなくなります。

少なくとも、ログやメッセージを残すようにしましょう。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine("例外が発生しました。");
Console.WriteLine(ex.Message);
}

実務では、コンソール表示ではなくログファイルや監視システムに記録することが一般的です。

6-5. NullReferenceExceptionへの対策

NullReferenceExceptionは、nullのオブジェクトに対してプロパティやメソッドを呼び出したときに発生します。

C#
string name = null;
Console.WriteLine(name.Length);

このコードでは、namenullなのにLengthを参照しているため、例外が発生します。

対策として、事前にnullチェックを行います。

C#
string name = null;

if (name != null)
{
Console.WriteLine(name.Length);
}
else
{
Console.WriteLine("nameはnullです。");
}

C#では、null条件演算子を使うこともできます。

C#
string name = null;

Console.WriteLine(name?.Length);

NullReferenceExceptiontry-catchで受け取ることもできますが、基本的には事前チェックで防ぐ方が望ましいです。

7. try-catchを使うときの注意点とベストプラクティス

7-1. 何でもtry-catchで囲まない

try-catchは便利ですが、何でも囲めばよいわけではありません。

C#
try
{
int age = 20;
Console.WriteLine(age);
}
catch
{
Console.WriteLine("エラーです。");
}

このように、例外が発生する可能性が低い処理までtry-catchで囲むと、コードが読みにくくなります。

try-catchは、例外が発生する可能性があり、かつ発生したときに適切な対応が必要な場所で使いましょう。

7-2. catchした例外はログに残す

例外をcatchしたら、原因を後から調査できるようにログを残すことが大切です。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine($"エラー: {ex.Message}");
}

開発中はConsole.WriteLineでもよいですが、実務ではログライブラリを使って記録することが多いです。

ログには、例外メッセージ、発生場所、発生時刻、入力値などを残すと調査しやすくなります。

7-3. 必要に応じて例外を再スローする

catchした例外を、その場で完全に処理できない場合は、再スローすることがあります。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException ex)
{
Console.WriteLine("ログに記録します。");
throw;
}

throw;を使うと、現在の例外をそのまま上位の呼び出し元に伝えられます。

「ここではログだけ残して、最終的な処理は上位に任せたい」という場合に使います。

7-4. ユーザーに表示するメッセージと開発者向けログを分ける

例外メッセージをそのままユーザーに表示すると、内容が分かりにくかったり、内部情報が漏れたりする可能性があります。

よくない例は次のとおりです。

C#
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}

ユーザーには分かりやすいメッセージを表示し、詳細情報はログに残すのが基本です。

C#
catch (Exception ex)
{
Console.WriteLine("処理中に問題が発生しました。時間をおいて再度お試しください。");

// 実務ではログ出力ライブラリなどに記録する
Console.WriteLine(ex.ToString());
}

ユーザー向けメッセージと開発者向けログは、目的を分けて設計しましょう。

7-5. 予測できるエラーは事前チェックで防ぐ

すべての問題をtry-catchで処理するのではなく、予測できるものは事前チェックで防ぐことが大切です。

たとえば、数値変換ではint.TryParseを使うと、例外を発生させずに変換できるか確認できます。

C#
string input = "abc";

if (int.TryParse(input, out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値を入力してください。");
}

ファイルの存在確認では、File.Existsを使えます。

C#
string path = "sample.txt";

if (File.Exists(path))
{
string text = File.ReadAllText(path);
Console.WriteLine(text);
}
else
{
Console.WriteLine("ファイルが存在しません。");
}

ただし、ファイルは存在確認後に削除される可能性もあります。そのため、ファイル操作では事前チェックとtry-catchを組み合わせることが重要です。

8. try-catchとthrowの関係

8-1. throwとは何か

throwは、例外を発生させるためのキーワードです。

C#では、システムが自動的に例外を発生させるだけでなく、自分で例外を投げることもできます。

C#
throw new Exception("例外を発生させました。");

たとえば、メソッドに不正な引数が渡された場合に、明示的に例外を発生させることがあります。

C#
static void SetAge(int age)
{
if (age < 0)
{
throw new ArgumentException("年齢に負の値は指定できません。");
}

Console.WriteLine($"年齢は{age}歳です。");
}

8-2. 例外を再スローする書き方

catchで受け取った例外を再び投げることを、再スローといいます。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException)
{
Console.WriteLine("形式エラーを検出しました。");
throw;
}

throw;を使うと、元の例外情報を保ったまま上位に伝えられます。

メソッド内でログを残しつつ、呼び出し元にも例外を知らせたい場合に使います。

8-3. throwとthrow exの違い

C#では、再スローするときにthrow;throw ex;の違いに注意が必要です。

推奨される書き方は次のとおりです。

C#
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}

一方、次の書き方は避けた方がよいです。

C#
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}

throw ex;を使うと、例外のスタックトレースがリセットされ、元の発生場所が分かりにくくなる場合があります。

再スローする場合は、基本的にthrow;を使いましょう。

8-4. 独自例外を投げる基本例

必要に応じて、自分で独自の例外クラスを作ることもできます。

C#
public class InvalidUserNameException : Exception
{
public InvalidUserNameException(string message) : base(message)
{
}
}

独自例外は、次のように投げられます。

C#
static void RegisterUser(string userName)
{
if (string.IsNullOrWhiteSpace(userName))
{
throw new InvalidUserNameException("ユーザー名が入力されていません。");
}

Console.WriteLine("ユーザー登録が完了しました。");
}

呼び出し側では、独自例外をcatchできます。

C#
try
{
RegisterUser("");
}
catch (InvalidUserNameException ex)
{
Console.WriteLine(ex.Message);
}

独自例外は、業務ルールに特化したエラーを分かりやすく表現したいときに役立ちます。

9. C#の例外処理でよく使う例外クラス一覧

9-1. Exception

Exceptionは、多くの例外クラスの基底となるクラスです。

C#
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

どの例外が発生するか分からない場合に使えますが、何でもExceptionだけで処理すると、具体的な原因に応じた対応がしにくくなります。

基本的には、具体的な例外を先にcatchし、最後にExceptionを置く使い方が適しています。

9-2. NullReferenceException

NullReferenceExceptionは、nullのオブジェクトを参照したときに発生します。

C#
string text = null;
Console.WriteLine(text.Length);

対策としては、nullチェックを行います。

C#
if (text != null)
{
Console.WriteLine(text.Length);
}

または、null条件演算子を使います。

C#
Console.WriteLine(text?.Length);

9-3. ArgumentException

ArgumentExceptionは、メソッドに渡された引数が不正な場合に使われる例外です。

C#
static void PrintName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前が正しくありません。");
}

Console.WriteLine(name);
}

引数の値が範囲外の場合は、ArgumentOutOfRangeExceptionが使われることもあります。

9-4. FormatException

FormatExceptionは、文字列の形式が正しくない場合に発生します。

代表的な例は、数値変換です。

C#
int number = int.Parse("abc");

対策として、int.TryParseを使う方法があります。

C#
if (int.TryParse("abc", out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値に変換できません。");
}

9-5. IOException

IOExceptionは、ファイルやストリームなどの入出力処理で問題が発生した場合に使われます。

C#
try
{
string text = File.ReadAllText("sample.txt");
}
catch (IOException ex)
{
Console.WriteLine("入出力エラーが発生しました。");
Console.WriteLine(ex.Message);
}

ファイル操作やネットワークストリームなど、外部リソースを扱う場合によく見かける例外です。

9-6. InvalidOperationException

InvalidOperationExceptionは、オブジェクトの現在の状態では操作を実行できない場合に発生します。

たとえば、コレクションの状態や処理の順序が正しくない場合などに使われます。

C#
throw new InvalidOperationException("現在の状態ではこの操作を実行できません。");

自分でメソッドを作るときにも、「この状態では呼び出してはいけない」という場面で使えます。

10. 初心者が理解しておきたいtry-catchの実践例

10-1. 入力値チェックとtry-catch

ユーザー入力では、想定外の値が入力されることがよくあります。

C#
try
{
Console.WriteLine("数値を入力してください。");
string input = Console.ReadLine();

int number = int.Parse(input);

Console.WriteLine($"入力された数値は{number}です。");
}
catch (FormatException)
{
Console.WriteLine("数値以外が入力されました。");
}

ただし、入力値チェックではTryParseの方が自然な場合も多いです。

C#
Console.WriteLine("数値を入力してください。");
string input = Console.ReadLine();

if (int.TryParse(input, out int number))
{
Console.WriteLine($"入力された数値は{number}です。");
}
else
{
Console.WriteLine("正しい数値を入力してください。");
}

例外が起きることを前提にするのではなく、事前に防げるものは防ぐという考え方が大切です。

10-2. ファイル操作とtry-catch

ファイル操作では、try-catchが特に重要です。

C#
try
{
string path = "data.txt";
string text = File.ReadAllText(path);

Console.WriteLine(text);
}
catch (FileNotFoundException)
{
Console.WriteLine("指定されたファイルが見つかりません。");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("ファイルにアクセスする権限がありません。");
}
catch (IOException ex)
{
Console.WriteLine("ファイル処理中にエラーが発生しました。");
Console.WriteLine(ex.Message);
}

ファイルがあるかどうか、アクセスできるかどうかは、実行環境によって変わります。そのため、ファイル操作では例外処理を用意しておくと安全です。

10-3. データベース処理とtry-catch

データベース処理では、接続エラーやSQL実行エラーが発生することがあります。

C#
try
{
// データベース接続やSQL実行処理を書く
Console.WriteLine("データベース処理を実行します。");
}
catch (Exception ex)
{
Console.WriteLine("データベース処理中にエラーが発生しました。");
Console.WriteLine(ex.Message);
}

実際の開発では、使用するデータベースやライブラリに応じて、より具体的な例外クラスをcatchします。

また、データベース接続などのリソースは、usingを使って確実に解放することが重要です。

C#
try
{
using var connection = new SqlConnection("接続文字列");

connection.Open();

Console.WriteLine("接続に成功しました。");
}
catch (Exception ex)
{
Console.WriteLine("データベース接続に失敗しました。");
Console.WriteLine(ex.Message);
}

10-4. API通信とtry-catch

API通信では、ネットワークエラーやタイムアウト、サーバー側のエラーなどが発生する可能性があります。

C#
try
{
using HttpClient client = new HttpClient();

string result = await client.GetStringAsync("https://example.com/api/data");

Console.WriteLine(result);
}
catch (HttpRequestException ex)
{
Console.WriteLine("API通信に失敗しました。");
Console.WriteLine(ex.Message);
}
catch (TaskCanceledException ex)
{
Console.WriteLine("API通信がタイムアウトした可能性があります。");
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("予期しないエラーが発生しました。");
Console.WriteLine(ex.Message);
}

API通信では、失敗した場合に再試行する、ユーザーにエラーメッセージを表示する、ログに記録するなどの対応が必要になります。

11. C# try-catchに関するよくある質問

11-1. try-catchは必ず書く必要がある?

すべての処理にtry-catchを書く必要はありません。

try-catchは、例外が発生する可能性があり、その例外に対して何らかの対応をしたい場合に書きます。

たとえば、ユーザー入力、ファイル操作、データベース処理、API通信などではtry-catchがよく使われます。

一方で、通常の計算処理や単純な変数代入までtry-catchで囲む必要はありません。

11-2. catchには何を書けばいい?

catchには、例外が発生したときに行いたい処理を書きます。

代表的な処理は次のようなものです。

エラーメッセージを表示する。

ログを記録する。

代わりの処理を実行する。

必要に応じて例外を再スローする。

たとえば、次のように書きます。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException ex)
{
Console.WriteLine("数値の形式が正しくありません。");
Console.WriteLine(ex.Message);
}

catchの中を空にすると、原因が分からなくなるため避けましょう。

11-3. finallyは省略できる?

finallyは省略できます。

try-catchの最小構成は次のように書けます。

C#
try
{
int number = int.Parse("123");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

必ず実行したい後片付け処理がある場合は、finallyを使います。

C#
try
{
Console.WriteLine("処理を開始します。");
}
catch
{
Console.WriteLine("例外が発生しました。");
}
finally
{
Console.WriteLine("後片付け処理を実行します。");
}

ファイルやデータベース接続などのリソース解放では、finallyよりusingを使う方がシンプルな場合もあります。

11-4. Exceptionだけcatchしても問題ない?

Exceptionだけをcatchすることはできます。

C#
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

ただし、Exceptionは多くの例外をまとめて受け取るため、具体的な原因に応じた処理がしにくくなります。

たとえば、数値変換エラーとファイル読み込みエラーでは、ユーザーに表示すべきメッセージが異なります。

そのため、可能であれば具体的な例外を先にcatchし、最後にExceptionを置くのがよい書き方です。

C#
try
{
int number = int.Parse("abc");
}
catch (FormatException)
{
Console.WriteLine("数値の形式が正しくありません。");
}
catch (Exception ex)
{
Console.WriteLine("予期しないエラーが発生しました。");
Console.WriteLine(ex.Message);
}

11-5. try-catchを使うと処理速度は遅くなる?

try-catchを書くだけで大きく処理速度が遅くなるわけではありません。

ただし、例外が実際に発生すると、通常の処理に比べてコストが高くなります。そのため、頻繁に発生することが分かっている処理を例外で制御するのは避けるべきです。

たとえば、数値変換できるかどうかを確認するために毎回try-catchを使うより、int.TryParseを使う方が適しています。

C#
if (int.TryParse("123", out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("数値に変換できません。");
}

try-catchは、通常の分岐処理の代わりではなく、予期しない問題に対応するための仕組みとして使いましょう。

まとめ

C#のtry-catchは、プログラム実行中に発生する例外を安全に処理するための基本的な構文です。

tryブロックには例外が発生する可能性のある処理を書き、catchブロックには例外が発生したときの処理を書きます。

基本構文は次のとおりです。

C#
try
{
// 例外が発生する可能性のある処理
}
catch (Exception ex)
{
// 例外が発生したときの処理
}

複数のcatchを使えば、FormatExceptionIOExceptionなど、例外の種類ごとに処理を分けられます。その場合は、具体的な例外を先に書き、最後にExceptionを書くのが基本です。

また、例外の有無に関係なく実行したい処理がある場合はfinallyを使います。ファイルやデータベース接続などのリソース解放では、using文もよく使われます。

try-catchを使うときは、何でも囲むのではなく、例外が発生する可能性がある処理に絞って使うことが大切です。さらに、catchした例外はログに残し、必要に応じてthrowで再スローすることで、原因調査や保守がしやすいコードになります。

C#の例外処理は、初心者にとって最初は難しく感じるかもしれません。しかし、trycatchfinallythrowの役割を理解すれば、安全で読みやすいプログラムを書けるようになります。