C#のthrow,try,catch,finallyステートメントの動作を確認する

C# コンピュータ
C#

面倒なので例外処理を書いてい来なかった筆者ですが、例外処理を学習したいと思います。

try-finallyステートメント

try{}を離れるとfinally{}が実行されるとのことですので、tryブロック内でreturnしてみます。

void TestFunc()
{
    try
    {
        Console.WriteLine("Hello, World!");
        return;
    }
    finally
    {
        Console.WriteLine("Finally");
    }
}

TestFunc();

結果

Hello, World!
Finally

例外をcatchの有無に関係なく実行されるブロックという、ふんわりとした理解でしたが、試してみると他にはないプログラムの流れを作り出すことが出来ます。コンソールにFinallyが出力されていることからtryブロックのreturnの実行が完了する前にfinallyブロックが実行されています。

関数(メソッド)を抜ける前に、ストリームを閉じたり、オブジェクトをDispose()したりするクリーンアップ処理に使えそうですが、そちらはusingを使えば良いのでで、他の使い道として関数の先頭でStopwatchを仕込んでfinallyブロックで計測し関数の処理時間の計測に使えそうです。

throwとtry-catchステートメント

throwで例外オブジェクトを投げます。try-catchステートメントで例外オブジェクトを捕捉します。

void TestSubFunc()
{
    throw new Exception("catch me");
}


void TestFunc()
{
    try
    {
        TestSubFunc();
    }
    catch (Exception e)
    {
        Console.WriteLine("{0}", e.Message);
        throw;
    }
}

TestFunc();

結果

catch me

例外オブジェクトとが補足されたので例外オブジェクトのメッセージ”catch me”がコンソールに出力されます。
その後再度throwで例外を投げています。こちらはソースコード内ではcatchしていないので、ユーザーにまで例外が届きます。

VSCODEのデバッグ実行の例

この際例外が発生した場所(のスタックトレース?)は、catchブロック内のthrowではなく、TestSubFuncメソッド内のthrowになっています。

また、実際例外オブジェクトをthrowする場合、サンプルの様にExceptionのインスタンスでは例外がおきたことを知らせることはできますが、何が起きたかは不明ですのでcatchブロックで補足できても対処が出来ないと思います。(全ての例外に対処するプログラムになるため)というわけでExeptionを継承したオリジナルの例外クラスを用意しそれをthrowしcatchブロックで補足することに成ります。

また、throwは呼び出し元に向かってプログラムの制御を一気にジャンプする機能であり、例外をthrowする場所とそれをcatchで補足する場所を意識して設計する必要があると思います。最近使うことが少なくなったGOTO命令と似たような感じですが、GOTOがラベルに対してジャンプしますが、throwはcatchされる場所が複数考えられますので、かなり複雑で扱いが難しそうです。

感想

例外処理についての理解が少し進んだような気がします。しかしどのような状況で使うと良いのかは今一つ理解が出来ていない感じがします。なんとなくですが、頑張ってtry-catchステートメント沢山使うと想定していない例外が握りつぶされてしまい、あたかも問題が無いように見えてデバッグ?が難しくなりそうな予感がします。
また、例外クラスを作るのも面倒なのですが、とりあえずアプリケーション用の例外クラスを1つ作成し、自分が書いたプログラムのthrowなのか見分けることが出来るようにしたいと思います。

思いつく例外処理

  • データベースのトランザクションとロールバック
  • ファイルのダウンロードのタイムアウトと再試行

思いつく例外処理・ファイルが存在しない

ファイルを開こうとして、ファイルが存在していない場合、例外が発生すると思います。
try-catchステートメントで例外を捕捉するより先にファイルを開く前にファイルの有無を確認して例外が発生しにくいプログラムを作ることを考えます。
ただ、事前にファイルの有無をチェックしてもファイルが開かれるまでの間にファイルが削除される可能性は0ではないです。
そのような状況に対応するためにtry-catchステートメントを記述するのもアリかとは思います。

コメント