VB.NETでも契約プログラミングをしたい(その2)

はじめに

ふと思い出したように過去の記事見返してみたら、いやぁーすんごい詳しく書いてますね。 特に初期のPythonで自前のエンコーダとデコーダを実装している一連の記事。 いやほんと。前回のXMLその6と本当に同一人物が書いているんですかって疑うくらい。

前回のなんてマジで存在価値が無いですからね。 アレで分かるんだったらフツーにmsdn読んで理解できんだろって感じです。

というわけで今回はすこし間隔が開きましたが契約プログラミングのお話です。

契約プログラミングその1でリンクだけ紹介したあのマニュアルを一通り読み終わったので、それについて書きたいと思います。 ただ、TOEICが驚きの低得点で周りをドン引きさせた実績のある弊社ですから、多分間違ってます。

その辺を事情を察していただけるとこれ幸いといった感じです。

あとはいつもの弊社のクセといいますか、あまり厳密な事は把握しないで感覚で書いてるアブストラクトな感じで書いているので厳密さには欠けてますので、気付いたら適宜コメントに書いていただけるといい感じに修正しますのでいい感じにお願いします。

イントロダクション

契約とは何かとものっそいざっくり言うと、『何を渡して、何を得るか』です。 つまり、引数の型や返り値の型も契約と見なせます。

Integer型を受け取るメソッドStringをぶち込んでは正しく処理なんかできません。 VBですとOption Strict Offで亜空間型変換*1を行ってくれるのでStringからIntegerに正常に変換できるなら正しく処理を行えますが、正しく処理を行えるかどうかなんて実行時にしか分からない訳です。

逆にDecimal型が欲しいのにDoubleなんか返されても困る訳です。(ショウヒゼイガー、ハスウショリガー、アビャー

エラーは可能な限り早期に検出するのが.NETなどの静的型付けな環境での一般的な認識となっています。 すなわち可能ならコンパイル時に、実行時の場合にしか分からない場合でも早期に検出してエラーを送出すべきです。

また、契約プログラミングはその『エラーの早期検出』を補助する役割を持つ訳です。 そう考えるとVB.NETOption Strict Off契約プログラミングはかなり相性が悪い事が分かります。

つまり、Option Strict Offはクソ。 まずはOption Strict Onにしましょう。話はそれからです。

クッソ適当な分類

かなりの行数を割いてOption Strict Offをディスった次はようやく Code Contract のお話です。

Code Contracts を大まかに分類すると、以下の三つに分けられます。

  • 呼び出し側が満たすべき引数の条件(事前条件)
  • 呼び出された側が満たすべき返り値の条件(事後条件)
  • 呼び出された側が満たすべき内部状態の条件(内部状態条件)

この3つの条件を専用のAPIでコード中に記述する事で静的コードチェッカが認識できるようマークします。 そして、条件が満たされる事が証明できない場合にはコンパイル時に警告を出す事が出来るようになります。

また、実行時に条件が満たされない事が検出された場合にはただちに例外がスローされ、契約を満たさない不届き者を早期に排除出来るようになるわけです。

事前条件とアセンブリモード

とりあえず、試しに Code Contracts を導入すると決めた場合にまずは決めなくてはならなくてはならない事が1つあります。

それは事前条件の形式と形式によって決定されるアセンブリモードです。

  • リリースビルド時に引数検査を含める
    • No
      • Usage1:
        • 常にRequireを使用する
        • ランタイムチェックをデバックビルド時のみ有効にする
    • Yes
      • リリースビルド時に ccrewriter などのツールを使用する
        • Yes
          • Usage2:
            • パブリックメソッドの引数検査にRequire(Of Exn)(...)を使用する
            • もしくはRequireを使用する
            • 常にランタイムチェックを有効にする
            • オプションでパブリックメソッドの事前条件をアセンブリに出力する
        • No
          • Usage3:
            • if -then - throw(レガシー形式)形式の検査をリリースビットで行う
            • 引数検査を手動で継承する
            • if -then - throw形式の検査はEndContractBlock()メソッドでマークする
            • もしくはRequireメソッドを使用する
            • ランタイムチェックはデバックビルド時のみ有効にする

そんでもって、Usage1 or 2 の場合はアセンブリモードにStandard Contract Requiresを指定し、Usage3 の場合はCustom Parameter Validationを指定してやればOKというわけなのです。

こいつは一度決めてしまうと簡単に切り替えられないので*2、決定するときはある程度は慎重に判断してください。

おわりに

今回は取り敢えず Code Contract 導入以前と言うことで、アセンブリモードの軽い紹介と対応する引数チェックの形式について軽く紹介できました。

次回は・・・次回は何も決めてません。 まぁ、いい感じにマニュアル見ながらテキトーなことを垂れ流します。

続く

*1:積極的な暗黙的型変換の事

*2:コードの書き換えが面倒という意味で