例外について(1/3)
みなさんこんにちは🌚
お昼ご飯をついつい買い込んでしまうブルーノです。ポケ森をやって空腹を忘れさせています😑
さて今回からは例外についてまとめていきます。
例外について
コンパイルが正常にできたプログラムなどにおいてもエラーは度々発生します。例えば境界の範囲以上に配列にアクセスしようとしたり、Parseメソッドなどで文字列型を数値に変換しようとしてしまった場合などがそうです。こういった予期せぬエラーを例外(Exeption)と言います。例外が発生するとその例外を表す例外オブジェクトが自動的に生成され、プログラムに送られてきます。これを「例外がスローされる」と言います。このスローは英語でthrowのことで「投げる」という意味ですね。文字通り例外が投げられるということです。まずは簡単な例を作ってみましょう
見ればわかるように、ユーザーから二つの数を受け取り、引き算をしてその結果を表示する簡単なプログラムです。問題はこの9行目と13行目で、ここでもしstr1,2に文字列が入っていてdouble型に変換できなかったらどうなるでしょうか
私の場合はこのようにスローされましたと表示されました。windowsアプリケーションの場合は強制終了させられるかもしれません。どこかでうっかりミスをしただけでいちいちプログラムを強制終了されたんじゃたまったものじゃないですね。C#ではこれに対応できるように処置が用意されています。
まず、例外が起こりそうな文をtryブロックの中に記述します。そしてそのtryボックスの中の記述通りのことが起こった場合に実行されるcatchブロックを記述します。具体的には以下の通りです。
まず先ほどと違う点として、例外が投げられそうな記述をtryブロックの中に書き込んでいます。そしてそこで例外が発生した時にcatchブロックの中の処理を行なっています。double d1, d2には例外が投げられた時に何も入っていない状態になってしまうために0で初期化しています。つまり
例外が発生しそうな記述をtryブロックで囲む
↓
起こった場合の処理をすぐ下のcatchブロックに記述する
↓
そのまま下へ実行されていく
という流れになります。
さて、例外が起こったらそれへの対応をすることには成功しましたがこのプログラムでは例外の種類には触れていませんでした。 その内容に応じて対応を変えられればより便利そうですね
例えば...
- 「年齢を入力して下さい」→「二十」
→「数字で入力して下さい」
- 「年齢を入力して下さい」→「20」
→「半角で入力して下さい」
など
まあこの場合は開発者が予想できているので変換してやれよという気もしますが笑
さて、実は例外が投げられると先ほどは言いましたが、これは実はSystem名前空間で定義されているExceptionクラスとそこから派生したクラスからなる「例外オブジェクト」というものが生成されプログラムに送られてくるという行程のことを言っていたのです。この投げられた例外オブジェクトは種類によってその中身(メンバ)が様々に派生しています。このメンバによって対応を変えるようcatchブロックの中に色々なケースの対応を書いておけば良いわけです。
3つのcacthブロックの中にそれぞれ例外クラスについての対応を書きました。それぞれの引数の中の記述の説明は以下の通りです。
- IndexOutOfRangeExceptionクラス:Exceptionクラスの派生クラス。配列の境界を超えてアクセスしようとすると投げられる例外
- DivideByZeroExceptionクラス:Exceptionクラスの派生クラス。0で割ろうとした時に投げられる例外
- Exceptionクラス:全ての例外クラスの基本クラス
結果を見ればわかるようにメインメソッド内で0で割ろうとしたために真ん中のDivideByZeroExceptionクラスがキャッチされました。そして注目すべきは3つ目のExceptionクラス。全ての例外クラスを包括している基本クラスなのですが、これはキャッチされていません。これは、手前で先に別のキャッチ節でキャッチされているのでもうこの一連のキャッチ節では捕まらないからです。これは重要なことですね。ではDivideByZeroExceptionとExceptionのキャッチブロックを逆にするとどうなるかというと実はコンパイルエラーになります。全ての例外はExceptionにキャッチされるのは明らかなのでその後のcatch説が意味を持たなくなってしまうためです。
今回はここまでにしておきます。
例外は記述する量が膨大になりそうですがきちんとケアをすることで異常が起きないプログラムが組めそうです。いざとなればExceptionクラスで指定すればいいからヘーキヘーキ(棒