プログラミングを試してみたい人向けのC#プログラミングチュートリアル

例外処理(エラー処理)

POINT
  • エラーは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+"を追記します。

ファイル名:Compile.bat
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でエラーになっていることがわからなくなってしまいます。