【超ド基礎】例外処理の基本と王道(サンプル例:C#)
例外処理の目的
システムにはエラーが存在する。
エラーとはシステム上の欠陥が見つかった状態のことを言う。
システムを使っていてエラーが起こると、システムが落ちたり固まったりして使用に耐えない。
よってエラーが起こった場合に、システムが落ちたり固まったりしないように対策が必要。
そのために存在する考えや対策のことを「例外処理」という。
つまり例外処理の目的は
「システムが落ちないように対策をすること」と言える。
また、
「不自然なデータが入ってこないように制御すること」なども例外処理で行う。
目次
例外処理の基本サンプル
try
{
// ファイルを開く
FileStream fs = new FileStream("example.txt", FileMode.Open);
// ファイルからテキストを読み込む
StreamReader reader = new StreamReader(fs);
Console.WriteLine(reader.ReadToEnd());
// リソースを解放する
reader.Close();
fs.Close();
}
catch (FileNotFoundException ex)
{
// ファイルが見つからなかった場合のエラーメッセージ
Console.WriteLine("ファイルが見つかりませんでした: " + ex.FileName);
}
catch (Exception ex)
{
// その他の例外が発生した場合のエラーメッセージ
Console.WriteLine("予期しないエラーが発生しました: " + ex.Message);
}
finally
{
// 開放漏れリソースがあれば解放する
reader?.Close();
fs?.Close();
}
基本的にはtry catch構文を利用する。
エラーをキャッチする部分がtry{}の中。
エラーがキャッチされた場合、catch{}部分へ処理が移動する。
エラーが起きたときでもそうでないときでも、必ず行いたい処理はfinaly{}部分へ書く。
例外発生時にはオブジェクトが生成される
エラーが起きた場合、内部的にエラーオブジェクトがインスタンス化される。
(C#.Netではそういう仕様になってるらしい。)
エラーの種類によって、生成されるオブジェクトの種類が変わる。
割り算のときの数値が0のときのエラー、ファイルが存在しないエラー、文字が数値に変換できないエラー、その他のエラー、など様々なエラーの種類があり、それぞれ別のクラスのインスタンスとしてエラー時に生成される。
エラーの種類 | エラーのクラス |
---|---|
割り算のときの数値が0のときのエラー | System.DivideByZeroException |
ファイルが存在しないエラー | System.IO.FileNotFoundException |
文字が数値に変換できないエラー | System.FormatExceptionSystem.FormatException |
その他のエラー | System.Exception |
エラークラスが分かれている理由は、それぞれのエラーが発生したときに、それぞれ別のエラーメッセージを出したり、それぞれ別の処理を行う必要があるため。
例外処理の流れ
ざっくりとした流れ
- try{}の通常処理が走る
- エラーが発生した場合に、該当するエラークラスのインスタンスが生成される
- 該当するエラークラスのcatch{}部分へ移動する
- finalyへ移動する
より詳細な流れ
- try{}部分の処理が1行ずつ走る
- エラーが発生した行を通ってそこでエラーが起きた場合にエラーのインスタンス(オブジェクト)が生成される。
- (エラー発生部以降の処理は行われない)
- 該当するエラークラスのcatch{}部分へ移動する。
- finaly{}部分へ移動する。(3.によりプロセス漏れなどがある場合、ここで必ずプロセスの開放などを行うように利用することが多い。)
補足:例外処理が関数内でネストしている場合は、よりエラー箇所に近い方でキャッチされる。
例外を自分で発生させる
上記は主にプログラム側でのエラーに対応する書き方。
これ以外にも、意図的に自分で例外を発生させてエラーと判定させることができる。
ユーザーが入力した値が明らかにおかしいものである場合などに、エラーインスタンスを生成し、意図的に例外処理を発生させる。
以下サンプルコード
try
{
int width = 0;
int height = 0;
Console.Write("幅を入力してください: ");
width = int.Parse(Console.ReadLine());
Console.Write("高さを入力してください: ");
height = int.Parse(Console.ReadLine());
if (width <= 0 || height <= 0)
{
// 幅または高さが0以下の場合に例外をスローする
throw new ArgumentException("幅と高さは0より大きい数値を入力してください");
}
int area = width * height;
Console.WriteLine("面積は {0} です", area);
}
catch (FormatException ex)
{
// 数値以外の値が入力された場合のエラーメッセージ
Console.WriteLine("数値以外の値が入力されました");
}
catch (ArgumentException ex)
{
// 幅または高さが0以下の場合のエラーメッセージ
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
// その他の例外が発生した場合のエラーメッセージ
Console.WriteLine("予期しないエラーが発生しました: " + ex.Message);
}
finally
{
Console.WriteLine("処理が完了しました");
}
width <= 0 || height <= 0の条件のときに
ArgumentExceptionクラスのインスタンスを作成。
(引数に文字列でMessage("幅と高さは0より大きい数値を入力してください")を入れている。)
例外がインスタンス化されたのでcatch部分へ移動して、例外処理が行われる。
まとめ
- try catch構文を使う
- エラー時はエラーインスタンスが生成される
- 自分でエラーインスタンスを生成することも可能(throw new クラス名)
- インスタンスが生成されたら該当するcatch部へ移動
- finalyは正常時でも例外時でも必ず通る
Discussion