新人エンジニア研修で知っておきたい例外の使い方

なぜ、例外の理解が重要なのか、その理由

この記事では、弊社の新人エンジニア研修の参考にJavaを解説します。

前回はカプセル化について解説しました。今回は例外処理について解説します。例外処理も継承を巧みに使った仕組みの一例ですので復習にもなると思います。

この短い新人エンジニア研修の中でも例外がいくつか出てきました。

等がありましたね。

例外とは、人間に向けたプログラムからのトラブル報告書です。このままでは処理を継続できないという報告です。

例えばあなたがお使いのゲームアプリが突然意味不明なメッセージを表示して落ちてしまったらどうでしょうか?その上、何時間もかけて蓄積したセーブデータが残っていないとしたら。。。

エラーが発生しても続行するか、重要なデータを保存してから適切に終了するようにプログラミングしないといけません。

そのような処理を例外処理といいます。

例外が存在しなかった時代のエラー処理は大変でした。深い階層で起こったエラーを一生懸命バケツリレーで呼び出し元の処理に伝えていたのです。メソッドが返せる値は一つだけですから、大した情報も伝えられませんでした。エラーが発生した時にだけ投げる例外という仕組みができたおかげで、エラー時の処理が画期的に改善されたのです。

こんにちは。ゆうせいです。
今回は、C#の「例外処理」について解説していきます!プログラムを書いていると、思わぬエラーや問題が発生することがありますよね。そのまま放置してしまうと、プログラムが強制終了したり、データが壊れたりしてしまうかもしれません。そんなときに活躍するのが「例外処理」です。具体的な仕組みや書き方を、一緒に学んでいきましょう!


例外処理って何?

まず、例外処理とは何なのかを簡単に説明します。

プログラムを実行中に、エラーが発生した場合、そのままだとプログラムが止まってしまいますよね。これを防ぐために、エラー(例外)が発生しても適切に対応できるようにする仕組みが「例外処理」です。

C#では、try-catch構文を使って例外をキャッチして処理するのが基本です。また、リソースを安全に解放するためのusingステートメントなど、便利な構文も用意されています。


try-catchの基本構文

例外処理の基本となる構文は以下の通りです。

try
{
    // エラーが発生するかもしれない処理
}
catch (Exception ex)
{
    // 例外が発生した場合の処理
    Console.WriteLine($"エラーが発生しました: {ex.Message}");
}

ポイント

  • tryブロックの中に、エラーが起こりそうな処理を記述します。
  • catchブロックでは、発生した例外を受け取り、適切な処理を行います。
  • Exceptionは、C#の例外の基底クラスで、どんな例外でもキャッチできます。

実際の例

たとえば、0で割り算しようとした場合の例を見てみましょう。

try
{
    int a = 10;
    int b = 0;
    int result = a / b; // ここで例外が発生
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"エラー: {ex.Message}");
}

このコードを実行すると、DivideByZeroExceptionがキャッチされて、エラーメッセージが表示されます。


リソース管理に便利なusingステートメント

C#には、リソース(ファイル、データベース接続など)の管理を簡単にするためのusingステートメントがあります。これは、IDisposableインターフェースを実装しているオブジェクトを安全に解放するための仕組みです。

例: ファイルを開いて処理する

以下の例では、ファイルを開いて内容を読み取ります。

using (StreamReader reader = new StreamReader("example.txt"))
{
    string content = reader.ReadToEnd();
    Console.WriteLine(content);
} // ここで自動的にリソースが解放される

usingを使うことで、リソースを手動で閉じるコードを書かずに済みます。


例外を呼び出し元に投げる

ときには、例外をその場で処理せず、呼び出し元に処理を任せたい場合もあります。C#ではthrowキーワードを使って、例外を呼び出し元に投げることができます。

例: メソッド内で例外をスローする

public void Divide(int a, int b)
{
    if (b == 0)
    {
        throw new DivideByZeroException("0で割ることはできません。");
    }

    Console.WriteLine($"結果: {a / b}");
}

このメソッドを呼び出す側で、例外をキャッチする構成になります。

try
{
    Divide(10, 0);
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"エラー: {ex.Message}");
}


例外処理で気をつけるべきこと

1. 例外を握りつぶさない

例外が発生した場合、何も処理をせずに無視するのはNGです。最低限、ログにエラー情報を出力する必要があります。初心者の方は、まずコンソールにエラー情報を出力する習慣をつけましょう。

2. スタックトレースを確認する

例外が発生した場合、スタックトレースを見ることで、どの部分でエラーが起きたかを把握できます。スタックトレースは、Exception.StackTraceプロパティから取得できます。

catch (Exception ex)
{
    Console.WriteLine($"エラー: {ex.Message}");
    Console.WriteLine($"スタックトレース: {ex.StackTrace}");
}


例外処理の設計

C#で例外処理を設計する際には、以下のポイントを意識しましょう。

「is-a」か「has-a」かを考える

例外の発生原因をクラスやメソッドの責務と関連付けて設計します。「例外がそのクラスで処理されるべきか?」それとも「呼び出し元に投げるべきか?」を考えます。

例外の設計を単純化する

  • 例外処理を多用しすぎると、コードが複雑になりがちです。
  • 本当に必要な箇所にだけtry-catchを使い、無駄を省きましょう。

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

try-catchで適切に例外を処理する

usingステートメントを使用することで、リソースを明示的に閉じるコードを書く必要がなくなる。

例外を呼び出し元に投げることが可能であり、throwキーワードを使用する。メソッドの呼び出し元に処理を委ねることで、例外処理の責任を適切に分担できる。

スタックトレース情報は例外発生の原因を特定する重要な手掛かりである。C#ではException.StackTraceプロパティを用いてトレース情報を確認する。情報は上から下に読むのが基本であり、自作のクラスやメソッドを重点的に調査する。

例外の処理方法は2つに大別される。

  1. try-catchでその場で処理する。特定の例外だけを処理する場合は、キャッチする例外型を指定する。
  2. 呼び出し元に例外を投げる。この場合、throwキーワードを使用し、メソッドの呼び出し側に処理を任せる。

例外発生は握りつぶしてはいけない。最低限、例外情報をログに記録するべきである。ただし、学習中や研修中は、例外情報をコンソールに出力して内容を確認することを推奨する。

まとめができたら、アウトプットとして演習問題にチャレンジしましょう。

以上、今回は「例外処理で想定外の事態に強いシステムにする」方法について見てきました。

例外処理を使うことでプログラムはトラブルの発生をユーザーに伝えることができ、途中終了を避けることができるのでした。また、継承の仕組みを上手く応用しているのも興味深いところでしたね。

実際にお客様の依頼で開発するアプリでは、詳細なエラー情報は画面に表示しないことが多いのです。特に深刻なエラーが起こった場合はなおさらです。
知識が不十分なお客様をいたずらに不安にさせることになりますし、下手をすれば責任問題に発展し、会社同士のもめ事にもなりかねないからです。

次回は、「」です。

ということで、次回を楽しみに待っていてください。

例外処理で想定外の事態に強いシステムにする 最後までお読みいただきありがとうございます。