例外処理(エラー処理)
- エラーはtry catchで捕捉する
- try finallyで必ず実行する処理を記載できる
- try catchの際、throwで再度エラーを発生させることができる
例外処理(エラー処理)try catch
プログラムは正しい処理を実行できるように作るのは当然ですが、処理に失敗した場合にどう動くかも重要になります。
なぜ動かないのかがわからないと何の対応もできません。
プログラムにバグがあるのか、プログラムの使い方の問題なのかもわからない事態になります。
では、C#ではどのようにエラーを捕まえて、例外(エラー)時の処理を行うかを紹介します。
例外時の処理は、下記のように記載します。
try
{
// 例外が発生する可能性のある処理
}
catch (例外の型 例外変数名)
{
// 例外発生時の処理
}
using System;
class Program
{
static void Main()
{
try
{
// 文字を数値に変換してわざとエラー発生
int i = int.Parse("あいうえお");
}
catch (Exception ex)
{
// エラーの内容を表示
Console.WriteLine(ex.ToString());
}
Console.ReadKey(); // キー入力待ち
}
}
System.FormatException: 入力文字列の形式が正しくありません。
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.Main()
サンプルプログラムの実行方法はトップページを参照してください。
エラー内容の取得
catchの際、指定している例外変数を使用してエラーの内容を取得することができます。
エラーの内容を取得する方法はいくつかありますが、表示するなら一番詳細を出せるToString()がお勧めです。
他のエラー取得方法だと次のような出力になります。
using System;
class Program
{
static void Main()
{
try
{
// 文字を数値に変換してわざとエラー発生
int i = int.Parse("あいうえお");
}
catch (Exception ex)
{
// エラーの内容を表示
Console.WriteLine("[Message]\n"+ex.Message);
Console.WriteLine("\n[Source]\n"+ex.Source);
Console.WriteLine("\n[StackTrace]\n"+ex.StackTrace);
Console.WriteLine("\n[TargetSite]\n"+ex.TargetSite);
Console.WriteLine("\n[ToString()]\n"+ex.ToString());
}
Console.ReadKey(); // キー入力待ち
}
}
[Message]
入力文字列の形式が正しくありません。
[Source]
mscorlib
[StackTrace]
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.Main()
[TargetSite]
Void StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)
[ToString()]
System.FormatException: 入力文字列の形式が正しくありません。
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.Main()
デバッグオプションでより詳細なエラー取得
コンパイルの際にデバッグオプションを付けるとどのファイルの何行目でエラーが発生するかまで出力することができます。
Compile.batの最後に"/debug+"を追記します。
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Program.cs /debug+
pause
デバッグオプションを付けて前述のプログラムをコンパイル、実行すると次の結果になります。
[Message]
入力文字列の形式が正しくありません。
[Source]
mscorlib
[StackTrace]
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.Main() 場所 c:\Windwosですぐに始めるC#プログラミング\Program.cs:行 10
[TargetSite]
Void StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)
[ToString()]
System.FormatException: 入力文字列の形式が正しくありません。
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.Main() 場所 c:\Windwosですぐに始めるC#プログラミング\Program.cs:行 10
StackTraceとToString()の出力で最後にファイル「Program.cs」の10行目でエラーになっていることがわかります。
デバッグオプションを付けてコンパイルした場合、Program.pdbというファイルがexeと一緒に作成されます。
このファイルにデバッグ情報が含まれています。
例外の種類
catchのときに例外を指定することで、例外の内容により処理を分けることができます。
複数の例外の種類を処理する場合は、catchを続けて記載します。
using System;
class Program
{
static void Main()
{
try
{
DateTime dt = DateTime.ParseExact("2024/01/01 00:00:00", "yyyy/MM/dd", null);
}
catch (FormatException ex)
{
Console.WriteLine("書式が不正です");
Console.WriteLine(ex.ToString());
}
catch (OverflowException ex)
{
Console.WriteLine("桁あふれが発生しました");
Console.WriteLine(ex.ToString());
}
Console.ReadKey(); // キー入力待ち
}
}
書式が不正です
System.FormatException: 文字列は有効な DateTime ではありませんでした。
場所 Program.Main()
主な例外の種類を紹介します。
| 型名 | 内容 |
|---|---|
| System.Exception | すべてのエラー |
| System.FormatException | 書式が不正のエラー |
| System.OutOfMemoryException | メモリ不足のエラー |
| System.NullReferenceException | nullの変数を参照したエラー |
| System.InvalidCastException | 型の変換に失敗 |
| System.IndexOutOfRangeException | 配列参照時にインデックスの範囲が外れている |
| System.DivideByZeroException | 0で割ったときのエラー |
| System.OverflowException | 桁あふれ |
| System.IO.DirectoryNotFoundException | ディレクトリがないエラー |
| System.IO.FileNotFoundException | ファイルがないエラー |
必ず実行する処理 try finally
finallyを使うと例外の有無にかかわらず必ず実行する処理を記載できます。
try
{
// 例外が発生する可能性のある処理
}
catch (例外の型 例外変数名)
{
// 例外発生時の処理
}
finally
{
// 例外の有無にかかわらずに実行する処理
}
using System;
class Program
{
static void Main()
{
try
{
int i = int.Parse("あいうえお");
Console.WriteLine("ここだと実行されない");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
// エラーが発生したら終了する
return;
}
finally
{
Console.WriteLine("例外が出ても、returnで処理を抜けてもfinallyは実行する");
Console.ReadKey(); // キー入力待ち
}
}
}
System.FormatException: 入力文字列の形式が正しくありません。
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.Main()
例外が出ても、returnで処理を抜けてもfinallyは実行する
catchを使用せずにfinallyだけで使用するこもできます。
try→catch→finallyの順に処理が実行されるため、catchの前に処理をしたい場合はtry-finallyをtry-catchで括ったりする場合もあります。
using System;
class Program
{
static void Main()
{
try
{
Console.WriteLine("サンプルプログラム");
return;
}
finally
{
Console.WriteLine("returnで処理を抜けてもfinallyは実行する");
Console.ReadKey(); // キー入力待ち
}
}
}
サンプルプログラム
returnで処理を抜けてもfinallyは実行する
エラーの再発生 throw
throwを使うと例外をキャッチした後に例外を再発生させることができます。
エラー時の処理を行いながら、例外を呼び出し元にも伝えることができます。
try
{
// 例外が発生する可能性のある処理
}
catch (例外の型 e)
{
throw;
}
using System;
class Program
{
static void Main()
{
try
{
StringToInt("あいうえお");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey(); // キー入力待ち
}
static int StringToInt(string s)
{
try
{
int i = int.Parse(s);
return i;
}
catch (FormatException)
{
Console.WriteLine("「"+s+"」は数字に変換できません。");
throw;
}
}
}
「あいうえお」は数字に変換できません。
System.FormatException: 入力文字列の形式が正しくありません。
場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
場所 Program.StringToInt(String s)
場所 Program.Main()
このとき、throw ex;のように例外のクラスを指定することもできますが、これだとStackTraceの内容がクリアされるため注意が必要です。
using System;
class Program
{
static void Main()
{
try
{
StringToInt("あいうえお");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey(); // キー入力待ち
}
static int StringToInt(string s)
{
try
{
int i = int.Parse(s);
return i;
}
catch (FormatException ex)
{
Console.WriteLine("「"+s+"」は数字に変換できません。");
throw ex;
}
}
}
「あいうえお」は数字に変換できません。
System.FormatException: 入力文字列の形式が正しくありません。
場所 Program.StringToInt(String s)
場所 Program.Main()
hrow;をthrow ex;にするだけで、ParseInt32でエラーになっていることがわからなくなってしまいます。