投稿者 魔界の仮面弁士  (社会人) 投稿日時 2011/8/19 11:00:31
先の投稿の続き。

>> resume nextは処理によっては大変なことになりかねないので避けた方がいいです。
> そんなことが起こったりする可能性があるんですか!?

旧方式を避けた方が良いというか、できるだけ Try ステートメントを使った方が良いというか。

そもそも On Error ~ や Resume ~ は旧方式という事もあってか、Try に比べるとデメリットも多いです。
たとえば、Exception継承クラスと Err オブジェクトを比べると、情報量が多いのは圧倒的に前者です。
(Exception なら、どのメソッド内でエラーが発生したのかという情報までも追跡できます)

それでも旧方式を好むのであれば、個々の動作を十分に理解した上で使うようにしましょう。
個人的には Try を強く推奨していますが、旧方式の利用を否定するつもりもありません。


さて、旧方式について軽くおさらい。

「Resume」は、エラー発生個所を再度実行するもの。「Resume 復帰場所」は指定位置からの再開。
これらはたとえば、ダウンロード処理でタイムアウトした場合にリトライさせたり、
別のファイル名で再保存を試みたりするような状況で使えるでしょう。

このような Resume 処理は意図的に使われる事が多いため、問題となる可能性はやや低いかと思います。
(エラー回避処理が不十分だと、無限ループの様相を呈してしまう可能性はありますけれどね)


一方、問題になりやすいのは、今回話題となっている「On Error Resume Next」や「Resume Next」です。
前者は、エラーがあっても実行を止めず、Err オブジェクトとして保持するだけですし、
後者は、エラーのあった次の行から実行を再開させるステートメントです。

特に前者は、ある意味ではお気楽なステートメントに見えてしまい、乱用されがちなのですが、
結局のところ、エラーのあった行をスキップして処理を続行しているだけなのですから、
『なぜエラーが発生したのか』あるいは『その行を実行せずに次の行に進んで良いのか』などを
きちんと把握しておかないと、問題が出てしまうことになります。

たとえば、一度使用した変数値をうっかりリセットし忘れて、二回目以降の演算結果が桁あふれしたとします。
そのエラーをログに残すだけで、そのまま計算処理を続行したとして、正しい演算結果が得られるでしょうか。
あるいはデータベースを読み書きする処理があったとして、途中でエラーが発生したことを無視していた場合、
そのまま処理を続けて、異常値でデータを上書きしてしまう可能性は無いでしょうか。単純に言えば、そういう話です。


逆にいえば、エラーとなった処理をスキップしても大きな問題にはならない事が分かっている場合や、
Err.Number 判定等を行い、何らかの発生時のエラー対処処理が作りこまれているのであれば、
On Error Resume Next を使っても、堅牢なアプリケーションを作ることはできます。

なお、盲目的にすべてのエラーをただ捕まえるだけということの危険性は、
「On Error GoTo ~」や「Try」であっても同じ事です。
http://msdn.microsoft.com/ja-jp/library/ms182137%28VS.90%29.aspx


>> 絶対に例外が発生しないと分かっている箇所以外はあった方が良いと
>> 思います。
いろいろな考え方があると思いますが、実のところ、私はその逆だと思っていたりします。
ケースバイケースではありますが、基本は「書かない」と割り切った方が良い、と。

(とはいえ本当に必要なのは、例外を Catch すべきかどうかという議論ではなく、
 どんな例外を Catch し、それを Catch した後どうすべきか、という話なのですけれどね)


私の場合、Catch するのは事前に対処できない部分(ファイル入出力、ネットワーク接続処理など)に
絞っています。最低限必要なものだけを Catch し、それ以外は基本的に Catch しないという方法です。

これだと想定外のエラーに関しては取りこぼされることになりますが、デバッグ時には
むしろ問題箇所を発見するための手助けとなりますので、開発者にとっては分かりやすいです。

ただしデバッグ時以外だと、ユーザーに生のエラー画面を見せてしまう可能性があるため、
あまり望ましくありません。これに関しては、最上位のメソッド(いわゆる「Sub Main()」や、
集約例外ハンドラ(UnhandledException イベントや ThreadException イベントなど)で
対処するという手法が利用できます。
http://jehupc.exblog.jp/9247467/


> おっしゃるとおり、絶対確信がある場所以外は例外処理をおいたほうがいいですね。

ここで、もう一度考えてみて欲しい事があります。

確信の無い処においたとして、そこにはどのような対処コードを記述すべきでしょうか。
確信の無いまま記述された処理は、正しく動作するのでしょうか。
そもそも、それらの例外処理はどこに配置されるべきでしょうか。
確信の無きエラー処理が、不具合を隠す/見えにくくする可能性はないのでしょうか。
エラーをログに記録するとして、そのログには、エラーがどんな時にどのメソッドで
発生していたのかを追跡できるだけの情報を備えさせているでしょうか。


例外発生のダイアログをユーザーの目に触れさせてしまうのは、もちろん避けねばなりませんが、
だからといって無策の例外処理を施しただけなら、それは問題の解決とはなりえません。ゆえに

・そもそも、できるだけ例外が起きないようにする(例外処理以前の話)
・例外の発生を抑えられない場合は、個々の例外に対処するコードを書く(Try ~ End Try)
・続行不能な例外や、想定していないエラーが発生した場合には、それを通知したり、
 安全に中断/終了させるための処理を講ずる
・ログとして記録するのであれば、後から解析するに足る十分な情報を残すようにする

という心構えが大切となってきます。

それらをきちんと理解した上で、あえてエラーを無視しているケースにおいては、
それはそれで正しいコードであろうと思います。しかし、そうした考慮を行うことなしに、
闇雲にただ処理を Catch しているだけだとしたら問題です。それならむしろ例外処理を
書かない方が、原因不明の問題を作りこんでしまう事態を避けやすくなるでしょう。

ということで、むやみに Try~Catch や On Error を書くべきでは無く、書くのであれば、
処理の流れを意識した上で、本当に必要な場所に絞って記述することをおすすめしておきます。