2022-02-05 VBA 自作関数における例外の使い方

1
ほえほえ@スプシマン @hoehoe1234

スクリプト言語、VM言語における「例外機構」って劇的に便利でプログラムを簡潔に書くための必須要素なんだけどあまり理解されてない感じ。理解しなくても組み込み関数の挙動をみればその便利さがわかるはず。これを意図的に使えるようになると関数の「まちがえてなさ」が向上します。

2022-02-05 17:58:16
ほえほえ@スプシマン @hoehoe1234

なんども投稿していますが、関数(sub/function)でもっともいけないのは「間違えたまま動き続けること」です。止まるのは問題ありません。前段階で失敗しているのに後ろのステップが動くと破壊的な結果をもたらすことは容易に想像できます。

2022-02-05 17:59:41
ほえほえ@スプシマン @hoehoe1234

ステップ1でAAAシートをコピー、ステップ2でAAAシートを削除するような場合は、ステップ1がうまく行かない場合は少なくとも停止しなければいけません。この例ではAAAシートのコピー失敗は組み込みの例外が発生しますが、関数が「前の処理が正常に終了したことを前提」とする限りエラーチェックは

2022-02-05 18:01:20
ほえほえ@スプシマン @hoehoe1234

ノンプロだろうが本職だろうが必須のものになります。業務プログラムではコードの半分ぐらいがエラー対処コードだったりします(C言語とかの場合)。こんなのノンプロアプリでは馬鹿げていますよね。そこで例外の利用です。スクリプト言語の例外の仕組みによりコードからエラー処理を取り除くことが

2022-02-05 18:03:12
ほえほえ@スプシマン @hoehoe1234

できます。もちろんきちんと作る場合は最上位とかあるレイヤで例外を処理する必要があるのですが、エクセルVBAで想定するノンプロアプリの場合はそんなことをする必要はありません。単にアプリを止めればよいのですから。ではどうやって止めるか?になります。

2022-02-05 18:04:28
ほえほえ@スプシマン @hoehoe1234

配列の上限/下限を超えてアクセスすると実行時例外が起きますね。なので配列のアクセスにはエラー処理コードが不用なのです。間違っていればVBランタイムに教えてもらえるので。これと同じ考え方を自分で作成する関数にも全面的に採用します。

2022-02-05 18:05:37
ほえほえ@スプシマン @hoehoe1234

エラーで有ることを関数の戻り値で返すことはむずかしいんですね。なぜなら値は常に範囲があるので「例外の値を設定する」ことを都度考えなければいけません。C言語では0とかNullとか-1とか関数の特性に応じてエラーの値を考える必要があります。これはとりもなおさず関数への深い理解と経験を要求

2022-02-05 18:07:18
ほえほえ@スプシマン @hoehoe1234

します。VBAでは幸いに「エラー値(CVerr関数)」が生成できます。どの範囲の値とも違うエラー値を利用することで関数のエラー状況を的確に呼び出し元に伝えることができます。便利ですね。でもこれでもまだ「呼び出し元がチェックをしなければいけない」というコードが残ります。関数呼び出しのたびに

2022-02-05 18:08:54
ほえほえ@スプシマン @hoehoe1234

If文を書く必要があります。めんどくさいですね。しかし、これはこれで使いみちがありますが今回の主題から外れるので説明しません。ではどうすればよいのでしょうか?そうですね。関数内で不整合が起きた場合は全て例外を投げれば良いのです。

2022-02-05 18:10:19
ほえほえ@スプシマン @hoehoe1234

例外は関数からは透過なのでSubでもFunctionでも投げることができます。具体的には「Err.Raise 番号」だけです。典型的には引数が期待したものでない場合とかでえすと  If a_number < 0 then Err.Raise 番号 とするだけです。これは代表的な例です。ほとんどの不整合はこれに類するもののはずです。

2022-02-05 18:13:15
ほえほえ@スプシマン @hoehoe1234

「例外なんだけど呼び出し側には処理は継続してほしい」なんて状況もあります。この場合には例外を投げるのかエラーを返すのかは悩みますね。これは設計なので絶対的な正解はないと思います。でもこのようなケースは少ないので、大体は例外を投げてアプリを止めてしまえばいいわけです。

2022-02-05 18:14:56
ほえほえ@スプシマン @hoehoe1234

例外はそんなに種類は必要ありません。規定のエラーはだいたいこんな感じです。たしか、Docsでユーザ定義のエラーは何番以上にしたほうがよい。なんて情報もあったかと思います。ですから、自分作成の関数で投げる例外はConstかEnumで定義しておくとよいと思います。 paper.dropbox.com/doc/VBA--BbFp4…

2022-02-05 18:16:59
ほえほえ@スプシマン @hoehoe1234

どうせ止めるので1つだけでもいいのですがそれでは少々寂しいので4~5つ定義するとよいと思います。 日本語で書くと ・引数間違い ・期待していたデータ(型)でない ・内部ステータス不整合 ・致命的な内部エラー ぐらいでしょうか。組み込み関数のエラーを流用してもよいかと思います。

2022-02-05 18:19:54
ほえほえ@スプシマン @hoehoe1234

例外の種類(番号)を指定してトラップできる言語であれば詳細に分類することは意味があり、必要なことですがVBAではできないので4~5つ、もっと極端にいえば1つだけ例外番号を決めておけばよいのです。

2022-02-05 18:21:23
ほえほえ@スプシマン @hoehoe1234

例外を補足する手法、すなわち呼び出し側は例外をどうあつかうべきか?については別記事で作成したいと思います。ここでは「間違ったまま処理を進めない」という観点から例外の使用をについて説明しました。例外はスクリプト言語に与えられた特権です。どんどん使いましょう。

2022-02-05 18:22:46