例外の諸相

is BUG ready ? より『@IT/Insider.NET「戻り値の型を動的に変更することは可能ですか?」(id:ladybug:20050505:p2)』.
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=21003&forum=7&78
実はこのスレッド,立った当初からずっと眺めていたのですが,話題が発散している割に派生話題への掘り下げ度合いは今ひとつ物足りませんでした.そこで適当なキーワードを元に 10 分ほど Web を彷徨ってみたところ,レベル「初級」と銘打たれた以下の IBM developerWorks の記事にたどり着きました.
Javaの理論と実践: 例外をめぐる議論「チェックすべきか、チェックせずにおくべきか」

型検査

次のような JavaC# の interface 宣言があるとします.

// Java
public interface IMyFunction
{
    int myFucntion( int value1 ) throws Bounds;
}
// C#
public interface IMyFunction
{
    int myFucntion( int value1 ); //【重要】10000 より大きいときは ArgumentException を発生させる!【例外】
}

興味深いのは,Java における検査例外リストはメソッドシグネチャの一部であることです.Java では戻り値が型検査を受けるのと同様に,検査例外についても型チェックが行われます.この意味では Java では「A, B, C,...,X のいずれかの型の要素を返す関数」と「A 型の要素を返す関数」の区別が存在します.すなわち,「戻り値の型を動的に変更することは可能ですか?」という質問がもし Java に対してなされたのであれば,「(検査例外を用いて) メソッドシグネチャを変更する」はある意味自然な回答です (「動的」という単語に善意からの拡大解釈を行うことになりますが).C# でこのような複数の戻り値型を用いる方法としては,文法的な択一の強制はできませんが,ref/out を用いるか,(id:ladybug:20050505:p2) で述べられているように明示的に新たな戻り値の型を作成*1することになります.
このように Java 屋さんが言うところの「例外を使えば良い」は,メソッドシグネチャ,ひいてはインターフェイス仕様の変更を意味する可能性があることには注意しておく必要があるでしょう.一方 C# では実装クラスで例外を発生させるるようにしてもインターフェイス仕様には何ら変化が無く,ref/out による参照渡し引数の追加または戻り値型の F1Result への変更こそが Java と同様“きちんと”インターフェイス仕様を変化させる方法です.
他方,厳密な型検査によるプログラムの強固さは『コンパイル時にエラーを検出する(id:ladybug:20050512:p1)』でも述べられているように多くのプログラマによって支持されていますし,私もその一人です.しかしこれはインターフェイス仕様の互換性が「Java で言うところの例外の追加」に対してひどく fragile であることも意味します.

不安定なメソッド・シグニチャーの問題は先の問題に関連しています。単純にメソッドを通して例外を渡していると、メソッドの実装を変える度にそのメソッド・シグニチャーを変える必要があるだけでなく、そのメソッドを呼ぶ全てのコードも変える必要があります。一旦クラスが実稼働状態に展開されてしまうと、繊細なメソッド・シグニチャーを管理するのは高くつくものになります。ところがこの問題は基本的に、Blochの43項のヒントに従わないことから起きる別の症状なのです。メソッドは失敗があった時には例外を投げるべきですが、その例外が反映すべきなのはそのメソッドが何をするかであって、どのようにするか、ではないのです。

実装変更によるメソッド・シグニチャーへの例外の追加削除にプログラマーが疲れて、対象のレイヤーが投げる例外タイプの定義に抽象化を使わず、単純に全てのメソッドがExceptionを投げるように宣言してしまうことが時々あります。別の言い方をすれば、例外はあまりにも面倒すぎると結論し、例外という電源スイッチを切ってしまうのです。当然ですがこの手法は一般的に、どうでも良いようなコード以外では良いエラー処理とは言えません。

*1:複数の型の合成という意味では akiramei さんが紹介されている Nemerle の variant (id:akiramei:20050323:p2) なども興味深いですね

詳しい理由は MSDN 等で熟知すべし

また,彼らはただ Abort Thread で終わるものではない.
唯一神 CLR が Rudely Unload Application Domain の火の中に投げ込む者達だ.

これら全てでマニュアルの問題が提起されているのは注目に値します.
私の印象としては,MSDN Library の構成の問題かもしれませんが,インターフェイスやスーパークラスのマニュアルと,実装クラスやサブクラスのマニュアルとの距離が非常に遠く感じます.人類全てをニュータイプに革新させるのは無理にしても,個々のメソッドのページにインターフェイスのメソッドマニュアルやスーパークラスのメソッドマニュアルをインポーズするなど小細工の余地はまだまだ残っているんじゃないかと.

例外の窓辺

ここからはもう少し Windows 依存な話でも.Windows 開発でのいわゆる「例外処理」については cbrumme's WebLogThe Exception Model に網羅されています.C++ の構造化例外や .NET の例外機構以外にも,Win32 Structured Exception Handling (SEH) や Win32 Vectored Exception Handlers (VEH) といった Windows 固有の例外機構が存在します.

Visual C++ の非同期例外

Visual C++ のコンパイルオプションに,/EH (例外処理モデル) というものがあります._set_se_translator を用いて SEH による例外情報を C++ の例外に変換する場合,このオプションは EHa (非同期例外処理モデル) を選択する必要があります.SEH による例外は,C++ の文法上は分割されない処理単位に割り込む可能性があるため,C++ の例外処理がサポートする stack unwinding に余計なオブジェクト追跡が必要となります.

.NET の非同期例外

.NET での非同期例外は,しばしば悲劇をもたらす悪魔の使いとして登場します.
(id:NyaRuRu:20050320:p2)で紹介した例は,戻り値を変数に代入する直前に非同期例外が発生することで,ハンドル情報を失ってしまうというものでした.

IntPtr myFileHandle = NativeMethods.FindFirstFile(fullPath, data);

この問題の解決方法はいくつか存在します.

  1. out/ref を用いて関数からの return と変数への代入の順序を入れ替える
  2. マネージ型によるラッパークラスと Finalizer の改良で対処する
  3. 非同期例外の発生を抑制する.

このうち(2)と(3)にあたるものが,.NET Framework 2.0 で導入される CriticalFinalizer であり,Constrained Execution Region (CER) です.もちろんこの問題以外にも応用はありますけどね.
最近読んだ中では以下の記事が参考になりました.
Atomicity and asynchronous exception failures

R.I.Q.

@IT の件のスレッドより.

例外だと、当該メソッドを呼び出した側がキッチリ例外を把握していないと(当該メソッド呼び出しだけを厳密にtry-catchで囲った上で、投げられる例外の種類をきちんと把握していないと)危ないでしょう、ということです。

全然 危なくないです。プログラマが例外を把握していなかったら、より上位にその例外が伝播するだけでしょう。最終的にはアプリケーションが落ちることになると思いますが、それが危険かというと、それはアプリケーションの性質によりますね。世の中には、異常値で処理を続行してしまうよりもアプリケーションが落ちたほうが良い、というものがあるのです。私が実装しているものの多くもそうです。

菊池 Blogより.

プロジェクトを作成してすぐにCode Analyseを有効にするとMark assemblies with ReliabilityContractって怒られる。

M.I.Q. 1 (少年マガジンコミックス)

M.I.Q. 1 (少年マガジンコミックス)

ルールが変わったんだよ!
適当に例外投げてあとは上に任せればいいやという古いルールから,
頭働かせて MayCorruptAppDomain, MayCorruptInstance, MayCorruptProcess, WillNotCorruptState のどれに該当するか考えなきゃならないルールにな!

まあ,世の中のプログラマの信頼性のI.Q が低ければ,安っぽい信頼性のプログラムがプログラムのすべてと思わせることができる……というのはあるかもなぁ(陰謀説).
話は変わりますが,Visual Studio 2005 beta2 の Code Analysis 様のご意見,私も興味深く拝聴しております.Team Foundation Server は正直スルー気味ですが,Visual Studio Team System に付属する機能はやっぱり無視できなさそうです.下手すると数ヶ月後には「なんかぁ国内コミュニティで議論するよりぃ Code Analysis にお伺いを立ててぇ適当に海外 blog 漁った方がぁ楽しいし勉強になるよねぇ」とか電車の中で女子高生が話してたりしそうで怖いものがありますな.あるいは山奥にこもって Code Analysis と MSDN だけで修行したスーパー高校生プログラマの登場とか.