C#の例外の再スロー(throw;)と新しい例外のスローの違いを徹底解説!初心者でもわかる入門講座
生徒
「先生、C#でエラーが出たときにthrowを使うって聞いたんですけど、再スローと新しい例外のスローって違うんですか?」
先生
「とても大事なところですね。C#では、同じthrowでも『そのまま投げ直す』場合と『新しいエラーを投げる』場合があるんです。」
生徒
「えっ、同じように見えるのに意味が違うんですか?混乱しそうです。」
先生
「では、実際のコード例を見ながら違いをしっかり理解していきましょう。」
1. 例外処理とは?
C#における例外処理とは、プログラムの実行中に発生するエラーを安全に処理する仕組みのことです。例えば、存在しないファイルを開こうとしたり、0で割り算をした場合にエラーが発生します。このような予期しないエラーを放置するとプログラムが強制終了してしまいます。そこでtry-catchを使ってエラーを捕まえ、安全に処理を続けることができるようになります。
2. 再スロー(throw;)とは?
再スローとは、捕まえた例外をそのまま上位に投げ直すことをいいます。元の例外情報(エラーメッセージや発生した場所)を失わずに保持できるのが特徴です。
コードで見てみましょう。
try
{
int x = 10;
int y = 0;
int result = x / y;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("ゼロ除算エラーを捕まえました。再スローします。");
throw; // ここで再スロー
}
この場合、エラーが再び外側に投げ直されるため、上位の処理でもエラーを扱うことができます。そして、例外が発生した元の場所(ゼロ除算の行番号など)も保持されます。
3. 新しい例外をスローする場合
一方でthrow new Exception("メッセージ");のように新しい例外を投げる方法もあります。こちらは元のエラー情報を破棄して、新しいエラーとして扱います。
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // 範囲外アクセス
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("配列の範囲外エラーを検出しました。新しい例外に変換します。");
throw new Exception("配列アクセスエラーが発生しました。");
}
このコードでは、元々はIndexOutOfRangeExceptionという例外ですが、新しくExceptionとして投げ直しています。そのため、元のエラーがどこで起きたかといった情報は失われてしまいます。
4. 再スローと新しいスローの違いをイメージで理解
初心者の方にも分かりやすいように例え話をしてみましょう。
- 再スローは「拾ったボールをそのまま上の人に投げ返す」イメージ。ボールについた泥(エラーの詳細)もそのまま渡ります。
- 新しいスローは「拾ったボールを捨てて、新しいボールを作って投げる」イメージ。元の泥の情報は消えてしまいます。
エラーの原因をしっかり調べたいときは再スロー、ユーザーにわかりやすいメッセージだけを伝えたいときは新しいスローを使う、と考えると理解しやすいです。
5. 実行結果の違いを確認しよう
再スローと新しいスローで、どのように実行結果が変わるのか見てみましょう。
再スローの場合:
ゼロ除算エラーを捕まえました。再スローします。
System.DivideByZeroException: Attempted to divide by zero.
at Program.Main()
新しい例外の場合:
配列の範囲外エラーを検出しました。新しい例外に変換します。
System.Exception: 配列アクセスエラーが発生しました。
at Program.Main()
再スローは元のエラー情報が保持されますが、新しいスローはメッセージだけに置き換えられることがわかります。
6. どちらを使うべきか?
実際の開発では、状況によって使い分けます。
- 再スロー:デバッグやログ出力で正確なエラー情報を残したいとき。
- 新しいスロー:ユーザーに「専門用語ではなく、わかりやすいエラーメッセージ」を伝えたいとき。
特に業務システムでは、開発者用のログには再スローで詳細を残し、ユーザーには新しい例外で「処理に失敗しました」と伝えるなど、両方を組み合わせるのが一般的です。
まとめ
ここまで、C#における例外処理のしくみや、再スローと新しい例外スローのちがいを丁寧にふりかえりながら学んできました。あらためて整理すると、再スローは元の例外情報をそのまま上位へ伝えるため、デバッグやログ解析にとても有効であり、エラーの発生箇所を正確に把握するための大切な手段になります。一方で、新しい例外のスローは、利用者に対してよりわかりやすい独自メッセージを提示したい場面で役立ち、システム利用者の視点で扱いやすいエラー表示を実現します。この二つは見た目がにていても目的が根本的にちがうため、適切に使い分けることが安定したプログラム構築につながります。とくに企業向けシステムや大規模アプリケーションでは、例外の再スローで詳細な技術情報をログへ残しつつ、画面上では新しい例外スローによるやさしいメッセージを提示し、開発者とユーザーの双方にとって扱いやすい環境を整えることが重要になります。 さらに、例外処理を正しく理解することは、プログラムの安定性を高めるだけでなく、障害発生時の原因特定や保守作業にも大きく貢献します。そのため、この章で触れた例外の再スロー、例外の再構築、例外の伝播、例外チェーン、エラーハンドリングの設計などは、基礎レベルにとどまらず応用的な場面でも非常に価値のある考え方となります。とくに再スローが保持するスタックトレースの意味や、新しい例外スローで見失われる元の例外情報の取り扱いを具体的なコードを通じて理解しておくことは、のちの開発工程で大きな差を生みます。ソフトウェア開発において例外処理は裏方のようにみえるかもしれませんが、実際にはアプリケーションの品質と信頼性を支える重要な柱のひとつであり、丁寧に向き合うほど実力がつく分野です。ここで学んだ知識を基礎として、例外フィルタ、カスタム例外クラス、ログの最適化などさらに深い世界へ進む道も開かれていきます。
再スローと新しい例外スローを確認できるサンプル
// 再スローで元のスタックトレースを保持する例
try
{
int a = 1;
int b = 0;
int r = a / b;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("再スローで元の例外を保持します");
throw; // 再スロー
}
// 新しい例外を投げる例
try
{
string[] items = { "A", "B" };
var x = items[5];
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("新しい例外として投げ直します");
throw new Exception("配列の範囲外アクセスが発生しました");
}
生徒
「先生、今日の学習で再スローと新しい例外スローのちがいがだいぶ理解できました。意外と奥が深いんですね。」
先生
「そうですね。例外処理はプログラム全体の信頼性に直結する重要な要素なので、表面的な使い方ではなく、どういう目的で使い分けるかを理解することが大切なんです。」
生徒
「再スローは元のエラー情報を残すからデバッグに便利で、新しい例外はユーザー向けに見やすくするためなんですね。」
先生
「そのとおりです。実務では両方を状況に応じて使い分けます。とくにログを分析するときには再スローの情報がとても役に立ちますよ。」
生徒
「なるほど、これで例外処理の理解が一段階深まりました。もっと練習して慣れていきたいです!」
先生
「良い心がけです。今後はカスタム例外や例外フィルタも学ぶと、さらに高度なエラーハンドリングができるようになりますよ。」