[Tips] ファイル操作における例外処理

はじめに

アプリケーションは、エラーが発生したときに適切な対処をしなければ、フリーズしたり想定もしない動作をしてしまいます。

エラーは「例外」とも呼び、この例外を対処することを例外処理と呼びます。

今回は、C# における例外処理の基礎と、ファイル操作における例外の対処方法について説明します。

目次

C#における例外処理の基礎

通常、C#において例外が発生すると以下のように、「ハンドルされていない例外」というメッセージが表示されます。

「ハンドルされていない」というのは、「対処されていない」ということです。「よって対処されていない想定外のエラーが発生した」ということを意味します。

try 〜 catch 〜 finallyを理解しよう

C# で例外処理を行うには、try 〜 catch 〜 finally を使用します。構文は以下の通りです。

try {
  // 例外が発生する可能性のある処理
} 
catch(例外の種類) {
  // 例外発生時の処理
}
finally {
  // 例外発生の有無に関わらず実施したい処理
}

try {} には例外が発生する可能性のあるコードを書きます。例えば、ファイルを開いてデータの読み込みをする際、指定したファイルが存在しなければエラーになりますので、このような処理は try 句に記述します。

catch(例外の種類) {} には、try 句で例外が発生した場合に実行する処理を記述します。例外の種類については後述します。例えば、エラーメッセージを表示して、ユーザーに再捜査を促すような処理書いたり、エラーログを出力するなどの操作を行います。

finally {} には、例外の発生の有無に関わらず実施したい処理を書きます。例えば、try 句でファイルを開く操作をしている場合は、必ずファイルを閉じる操作が必要となります。このような処理は finally 句に記述します。

catch や finally はどちらか一方だけ書いてもよいことになっています。

よって try 〜 catch としたり、try 〜 finally とすることもできます。

例外の種類

catch (例外の種類) に書くことができる「例外の種類」には、catch 句がどの例外を対処するかの種類を指定することができます。この例外の種類は省略することができ、その場合はすべての種類の例外に対応することになります。

例外の種類を限定することで、きめ細やかに対処することができます。

例えば、ファイル操作に対する例外処理を行いたければ、例外の種類に IOException を指定すると、ファイル操作における例外をキャッチすることができます。より細かく例外の対処をしたければ IOException の派生クラスである FileNotFoundException(ファイルが見つからない時に発生する例外をキャッチ)や、FileLoadException(ファイルの読み込みに失敗した時に発生する例外をキャッチ)を指定すると良いでしょう。

例外の種類については https://docs.microsoft.com/ja-jp/dotnet/api/system.exception?view=netframework-4.8 を参照ください

例外を投げるには

自作のメソッドで、あえて例外を発生させることが可能です。例外を発生させることを、「例外を投げる」や「例外をスローする」といいます。

では、例外をスローさせると何がよいのか?

これは作成したメソッドを呼び出し元で try 〜 catch 〜 finally を使用して、適切な例外対処をできることを意味します。

例外をスローさせるには以下の書式を使用します。

throw 例外クラスのインスタンス

例えば、自作メソッド FileOpen() の中で FileLoadException の例外をスローする場合は以下のように記述します。

void FileOpen() {
  throw new FileLoadException();
}

ファイル操作における例外処理の例

それでは、ファイル操作における例外処理の例を見てみましょう。

以下は、ファイル読み込み時の例外対処の例です。

catch では、例外の種類として (FileNotFoundException fnfex) を指定しています。

例外 FileNotFoundException はファイルが見つからなかった例外を表し、fnfex は発生した例外の情報を受け取る変数です。

try
{
    using (var sr = new StreamReader(@"C:\Work\test.jsonxx", System.Text.Encoding.UTF8))
    {
        var jsonData = sr.ReadToEnd();

    }
}
catch (FileNotFoundException fnfex)
{
    Console.WriteLine(fnfex.FileName);
    Console.WriteLine(); // みやすいように空白行を出力
    Console.WriteLine(fnfex.Message);
    Console.WriteLine();  // みやすいように空白行を出力
    Console.WriteLine(fnfex.StackTrace);
}

上記の catch では、発生した例外の情報(fnfexの各プロパティ値)をコンソールに表示しています。表示している FileName は開こうとしたファイルの名前、Message は、エラーメッセージ、StackTrace は例外発生時の場所(行番号)などの情報を表示しています。

実際に例外を発生させた場合は、以下のようになります。

例外を発生時の例

if 文によるエラーの対処じゃダメなのか?

先ほどの例外処理ので例では、ファイルが存在しない場合の例外を対処するものでした。

何も try 〜 catch 〜 finally を使わなくても if文でファイルの存在を調べてからファイルを開けばいいのでは?と思われるかもしれません。

以下の例をみてください。

if ( !File.Exists(@"C:\Work\test.jsonxx"))
{
    return; // ファイルがないので何もしないで処理を抜ける
}

// このタイミングでファイルがなくなればエラーになってしまう
using (var sr = new StreamReader(@"C:\Work\test.jsonxx", System.Text.Encoding.UTF8))
{
    var jsonData = sr.ReadToEnd();

}

はじめに if文でファイルの存在をチェックして、ファイルがなければ return で処理を抜けています。これにより以降のコードは実行することがないのでファイルがない場合の対処としては十分そうに見えます。

しかし、if 文をすり抜けた後のタイミングでファイルが削除されたらどうでしょうか?

7行目の実行時にエラーが発生してしまいますね。

よって、ケースバイケースではありますが、if 文ではエラーの対処が不十分になることがあります。

よって 例外が発生する可能性があるのであれば、 try 〜 catch 〜 finally を使用するようにしましょう。

カテゴリーC#

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください