講義メモ

テキスト編:p.321「例外処理の基礎」から
ゲーム開発演習:自弾の複数化、敵機の出現

第13章 例外

p.321 例外処理の基礎

・例えば、整数演算でゼロ除算になると実行時エラーとして異常終了する
・この実行時エラーを例外といい、例外の種類や状況によっては、異常終了して欲しくない場合がある
・この場合に用いる仕掛けが例外処理
・例外処理を用いると、例外発生の検知と対処を見やすく記述できる
・ただし、即時に異常終了すべき例外(例:メモリ不足や故障)もあるので、例外処理には配慮と知識が必要

p.322 exception01.cs

//p.322 exception01.cs
using System;
class MyClass {
    public static void Main() {
        Console.Write("割られる数--");
        string strA = Console.ReadLine();
        double a = double.Parse(strA); //形式例外の可能性有り
        Console.Write("割る数---");
        string strB = Console.ReadLine();
        double b = double.Parse(strB); //形式例外の可能性有り
        Console.WriteLine("{0} ÷ {1} = {2}", a, b, a / b);
    }
}

p.322 exception01.csの例外発生時の情報について

・p.322 exception01.csで「a」などの数値以外の文字を入力すると実行時エラーになる
・この時のメッセージについて分析しよう

ハンドルされていない例外: System.FormatException: 入力文字列の形式が正しくありません。
↑例外処理がないことを示す ↑例外を表すクラス(形式例外) ↑例外クラスの持つメッセージ
場所 System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
  ↑実際に実行時エラー(例外)が発生した場所(※今回はC#が提供したクラス内)
場所 System.Double.Parse(String s)
  ↑実際に実行時エラー(例外)が発生したメソッドを呼んでいるメソッド名
場所 MyClass.Main() 場所 D:\ha232_C#_omiya\Chap11\Chap11\exception01.cs:行 7
  ↑例外が発生したメソッドを呼んでいるメソッドを呼んでいるプログラムと発生行番号

アレンジ演習:p.322 exception01.cs

・2変数をdouble型ではなくint型にすると、下記の3つの例外を発生させることができる
 ① 形式例外:整数に変換できない文字が入力された場合
 ② ゼロ除算例外:整数演算でゼロで除算した場合(※実数演算では発生しない)
 ③ オーバーフロー例外:変換結果がint型の範囲を超える場合(例:22億)
・以上が発生することを試そう

作成例

//アレンジ演習:p.322 exception01.cs
using System;
class MyClass {
    public static void Main() {
        Console.Write("割られる数--");
        string strA = Console.ReadLine();
        int a = int.Parse(strA); //形式例外、オーバーフロー例外の可能性有り
        Console.Write("割る数---");
        string strB = Console.ReadLine();
        int b = int.Parse(strB); //形式例外、オーバーフロー例外の可能性有り
        Console.WriteLine("{0} ÷ {1} = {2}", a, b, a / b); //ゼロ除算例外の可能性有り
    }
}

実行例(形式例外)

割られる数--a

ハンドルされていない例外: System.FormatException: 入力文字列の形式が正しくありません。
   場所 System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   場所 System.Int32.Parse(String s)
   場所 MyClass.Main() 場所 D:\ha232_C#_omiya\Chap11\Chap11\exception01.cs:行 7

実行例(オーバーフロー例外)

割られる数--2200000000

ハンドルされていない例外: System.OverflowException: Int32 型の値が大きすぎるか、または小さすぎます。
   場所 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   場所 System.Int32.Parse(String s)
   場所 MyClass.Main() 場所 D:\ha232_C#_omiya\Chap11\Chap11\exception01.cs:行 7

実行例(ゼロ除算例外)

割られる数--5
割る数---0

ハンドルされていない例外: System.DivideByZeroException: 0 で除算しようとしました。
   場所 MyClass.Main() 場所 D:\ha232_C#_omiya\Chap11\Chap11\exception01.cs:行 11

p.323(tryブロックとcatchブロック)

・例外処理の基本は処理対象の範囲を決めることからであり、決まった範囲をtryブロックにする
・書式: try { 例外処理対象 }
・tryブロックの中で例外が発生したら、即時に異常終了することはなくなり、システム側が例外処理定義のチェックと対処を行ってくれる
・なお、tryブロック内の途中で例外が発生したら、例外処理の有無に関わらず、発生場所からtryブロックの終わりまでの記述内容は
 実行されない
・tryブロックには1つ以上のcatchブロックが必要で、例外発生時の対処を記述する。
・対処が完了できたらプログラムは正常モードに戻り、catchブロックの次の行から実行を再開する。
・最も単純な書式: try { 例外処理対象 } catch { 例外処理 }

p.324 exception02.cs

//p.324 exception02.cs
using System;
class MyClass {
    public static void Main() {
        double a = 0.0, b = 0.0;
        Console.Write("割られる数--");
        string strA = Console.ReadLine();
        try { //例外処理対象①
            a = double.Parse(strA); //形式例外発生の可能性
        } catch { //①の例外処理
            Console.WriteLine("不適切な入力です"); //表示後、処理を続行
        }
        Console.Write("割る数---");
        string strB = Console.ReadLine();
        try { //例外処理対象②
            b = double.Parse(strB); //形式例外発生の可能性
        } catch { //②の例外処理
            Console.WriteLine("不適切な入力です");//表示後、処理を続行
        }
        Console.WriteLine("{0} ÷ {1} = {2}", a, b, a / b);
    }
}

p.325 例外クラス

・例外発生時に用いられるのがC#が定義している例外を表すクラスで、Exceptionクラスを基本クラスとする派生クラスになっている
・また、各種の例外のグループ化が例外クラスの継承関係によって表現されているので、複数の派生クラスを共通の基本クラスで扱うこともできる
・catchブロックに引数として例外クラスを記述でき、その例外発生時にのみ対処させることが可能
・この指定により「どんな例外でもcatchしてしまう」ことによる弊害が防止できる
・最も単純な書式: try { 例外処理対象 } catch(例外クラス名) { 例外処理 }
・なお、例外処理の中で、発生した例外に関する情報を例外クラスのオブジェクト経由で得ることが可能
・これは、例外発生時に発生情報を例外クラスのオブジェクトとしてシステムが生成して投げて(送って)くれるからで、
 catchブロックに引数として例外クラスと引数名を指定することで利用できる
・単純な書式: try { 例外処理対象 } catch(例外クラス名 引数名) { 例外処理 }

p.325 主な例外クラス

・すでに説明した例外クラス
 System.FormatException: 形式例外(整数変換や実数変換などが失敗した)
 System.OverflowException: オーバーフロー例外(データがその型の最大値・最小値を超えた)
 System.DivideByZeroException: ゼロ除算例外(整数演算で0で除算)
・頻出の例外クラス
 System.IndexOutOfRangeException: 配列添字範囲外例外(添字が要素数以上または0未満)
・例外の詳細やツリー構造は公式ドキュメントで閲覧可能
 例: https://learn.microsoft.com/ja-jp/dotnet/api/system.formatexception

p.326 例外クラスの主なメンバ

・catchブロックに引数として例外クラスと引数名を指定することで得られる例外オブジェクトが持つ情報は、
 例外クラスに共通なメンバ(メソッドとプロパティ)を用いて得られる
・詳細は公式ドキュメントで閲覧可能
 https://learn.microsoft.com/ja-jp/dotnet/api/system.exception?view=net-8.0
・なお、テキストに記載のToString()メソッドは、例外クラスに限ったものではなく、すべてのクラスにおいて、
 その情報を返すメソッドとして記述する(オーバーライドする)ことが推奨されている。

p.326 System.Objectクラス

・Object型はInt32などと同様の.NETフレームワーク型であり、.NETフレームワーク型にはすべて対応するクラスまたは構造体が提供されている
・int型などと同様に、object型を用いると、.NETフレームワークのObject型として扱われる
・object/Object型に対応するクラスがSystem.Objectクラスで、全てのクラスの暗黙の基本クラス(ルートクラス)として定義されており、
 継承を指定しなくても、自動的に用いられる
・このSystem.Objectクラスの持っているメンバの一つがToString()メソッドなので、すべてのクラスにおいてToString()メソッドの
 オーバーライドが可能。
・このことを利用して、オブジェクト名(参照変数)をConsole.Writeなどに指定すると、自動的に「参照変数.ToString()」が
 実行されるようになっている。
例: p.327 exception03.csで「Console.Write(io)」と「Console.Write(io.ToString())」を実行しているが同じ結果になる

コメントを残す

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