ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

C# - 例外処理、throw文、try-catch-finally文

初めに

 プログラミングでは、例外が起こったときでもプログラムが異常な動作をしないよう、 しっかりと例外処理を行う必要があります。本記事では、C#における例外処理の仕方として、throw 文、try-catch-finally 文についてまとめます。

例外処理とは

 プログラミングにおいて、例外(エラー)はさまざまな場面で発生することがあります。

(例)

  • ゼロ除算エラー(ZeroDivisionError)
    0で割り算をしようとした場合に発生する

    int x = 10;
    int y = 0;
    int result = x / y;  // DivideByZeroExceptionが発生する
    

  • ファイルオープンエラー(FileNotFoundException)
    アクセスしようとしたファイルが存在せず、ファイルが開けない場合に発生する

    string filePath = "path/to/nonexistent_file.txt";
    using (StreamReader reader = new StreamReader(filePath))
    {
        // ファイルを読み込む処理
    }
    // FileNotFoundExceptionが発生する
    

 このように例外が生じたときにプログラムが止まってしまうようでは、問題があるので例外に対応できるように処理を行う必要があります。

throw 文、try-catch-finally文

 C# における例外処理の書き方の一つとして、throw 文try-catch-finally文があります。

(例:0で割る場合)

using System;

public class Program
{
    public static void Main()
    {
        //関数利用側の記述
        try
        {
            int result1 = DivideNumbers(10, 2);
            Console.WriteLine("結果: " + result1);

            int result2 = DivideNumbers(10, 0);  //0で割っているので例外発生
            Console.WriteLine("結果: " + result2);
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static int DivideNumbers(int x, int y)
    {
        if (y == 0)
        {
             //例外処理の定義
            throw new DivideByZeroException("ゼロ除算エラーが発生しました");
        }
        return x / y;
    }
}

(実行結果)

※上の実行結果は Unity で実行したものになります。そのため、名前空間に UnityEngine を追加し、Main 関数を Start 関数に、Console.WriteLine を Debug.Log に変更したものを実行しています。

throw 文

 関数を定義する際に、throw 分を使って例外処理をすることができます。

 throw 文は、例えば次のように記述されます。

throw new FormatException();  //関数を呼び出すときに渡した引数の書式が使用に一致していない場合に投げる
throw new throw new DivideByZeroException("ゼロ除算エラーが発生しました");  //0で割ったときに投げる

 throw 文で投げられる例外は System.Exception クラスの派生クラスのインスタンスです。引数がnullだった、ファイルがないなどさまざまな例外を投げることができます。

 下のリンク先に throw 文で投げられる例外がまとめられています。

learn.microsoft.com

 throw 文は例外が起こった場所に挿入します。

public static int DivideNumbers(int x, int y)
{
    if (y == 0)  //0で割ったことが分かった
    {
         //例外処理を挿入
        throw new DivideByZeroException("ゼロ除算エラーが発生しました");
    }
    return x / y;
}
try-catch-finally 文

 try-catch-finally 文は関数を利用する側の記述の仕方です。

try
{
    // 例外が発生する可能性のあるコードを記述する
}
catch (Exception ex)
{
    // 例外が発生した場合の処理を記述する
    Console.WriteLine("例外が発生しました: " + ex.Message);
}
finally
{
    // 例外発生の有無にかかわらず実行したいコード
    //リソースの破棄などを行う
    Console.WriteLine("処理が終了しました。");
}

(try-catch-finally 文の書き方)

  • try 文内に例外が発生する可能性のあるコードを記述
  • catch 文内に例外が発生したときの処理を記述
  • finally 文内に例外発生の有無にかかわらず実行したいコードを記述
  • try 文は必須
  • catch 文、finally 文はどちらか片方だけでも ok
  • catch 文は複数並べて書ける
  • 同じ型の catch 文を複数並べるとエラーになる

(例)

using System;
using System.IO;  //StreamReader, IOExceptionを使うために追加

public class Program
{
    public static void Main()
    {
        try
        {
            ReadFile("path/to/nonexistent_file.txt");
        }
        catch (FileNotFoundException ex)  //ファイルが見つからない場合の例外処理
        {
            Console.WriteLine("ファイルが見つかりませんでした: " + ex.FileName);
        }
        catch (IOException ex)  //ディレクトリやファイルにアクセスしているときに起きた例外に関する処理
        {
            Console.WriteLine("ファイルの読み込み中にエラーが発生しました: " + ex.Message);
        }
        finally
        {
            Console.WriteLine("プログラムの実行が終了しました。");
        }
    }

    public static void ReadFile(string filePath)  //ファイルを読み込む関数
    {
        StreamReader reader = null;
        try  //読み込もうとする
        {
            reader = new StreamReader(filePath);
            string contents = reader.ReadToEnd();
            Console.WriteLine("ファイルの内容: " + contents);
        }
        finally  //例外が起ころうがreaderは閉じる
        {
            if (reader != null)
            {
                reader.Close();
            }
        }
    }
}
補足(関数内に throw 文がない場合)

 関数内に throw 文がなくても例外処理ができるようです。

(例:0で割る場合その2)
上記のものとの変更点は throw 文を削除しただけ

using System;

public class Program
{
    public static void Main()
    {
        //関数利用側の記述
        try
        {
            int result1 = DivideNumbers(10, 2);
            Console.WriteLine("結果: " + result1);

            int result2 = DivideNumbers(10, 0);  //0で割っているので例外発生
            Console.WriteLine("結果: " + result2);
        }
        catch (DivideByZeroException ex)  //0で割る場合の例外処理
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static int DivideNumbers(int x, int y)
    {
        //throw文を削除
        return x / y;
    }
}

(実行結果)