コード契約の代替前提条件チェック

実際のプロジェクトでコードコントラクトライブラリを使用しようとすると、多少の困難が生じる場合があります:前提条件と事後条件をチェックするメソッドを持つコントラクトクラス自体は、.NET Frameworkの4番目のバージョンからmscorlibにありますが、コードコントラクトライブラリ自体をインストールせずに、それらに該当しません結果のアセンブリ。



契約の通常の使用では、すべての開発者が追加の拡張機能をインストールする必要があるため、これは大規模な分散チームでいくつかの問題を引き起こす可能性があります。 そして、プロジェクトの重要な人々は、私たちが本当にこの良いものを必要とするかどうかについて明確な確実性を持たないかもしれないので、そのような移行は難しい場合があります。



ただし、コードコントラクトは追加の「互換性モード」をサポートします。これにより、結果のコードに前提条件チェックを「ハードワイヤー」して、コントラクトが開発者のマシンにインストールされているかどうかに関係なく、全員に表示されるようにします。



問題文


問題が何であるかをより明確に示す例を見てみましょう。



class SimpleClass { public int Foo(string s) { Contract.Requires(s != null); return s.Length; } }
      
      







このコードはすべて問題なく、 Fooメソッドをnullで呼び出そうとすると、コード違反ライブラリがインストールされ、前提条件チェックがオンになっているときに例外「System.Diagnostics.Contracts .__ ContractsRuntime.ContractException」がスローされるという契約違反が発生します。



はい、これはまさに私たちが期待していることですが、 例外は、 例外生成コードがコンパイラによってではなく、コンパイル直後に開始される別のプロセスによって生成されることです 。 そして、これは、コードコントラクトライブラリがないと、引数の追加検証が残っていないため、このコードの実行によりNullReferenceExcpetionが生成されることを意味します。 この振る舞いが次の反応を引き起こしたという事実に繰り返し気付きました。「WTF? 私の小切手はどこに行ったんだ!」



このような「WTF?!?」を聞きたくないので、契約を結んでいない同僚から、より徹底的な方法で前提条件チェックを縫製する方法を望んでいます。



手動の事前条件チェック


Code Contractsライブラリを使用すると、古い形式の前提条件を使用できます。 つまり、既存のメソッドに既に入力パラメーターのチェック(つまり、前提条件のチェック)が含まれている場合、それらを本格的な前提条件に変換するには、それらの後にContract呼び出しを追加するだけで十分です。 EndContractBlock



 public class SimpleClass { public int Foo(string s) { if (s == null) throw new ArgumentNullException("s"); Contract.EndContractBlock(); return s.Length; } }
      
      







契約コールの追加 EndContractBlockは、入力パラメーターの1つ(または複数)のチェックを本格的な前提条件に変換します。 これで、契約のない開発者にとって、このコードは以前と同じようになります。 同時に、契約の所有者として、静的チェッカーを使用したプログラムの有効性の確認、ドキュメントの自動生成、契約のすべての違反をキャッチする可能性など、すべての利点を活用することができます(これについては以下で詳しく説明します)。 この検証方法の違いは、それらをオフにしてコードを完全に切り取ることができないことだけです。



このアプローチは、より高度な契約テクニックと組み合わせることができます。 したがって、たとえば、 古い スタイルのチェック前提条件と、ポスト条件および不変条件のチェックを組み合わせることができます。 しかし、事後条件と不変条件は、顧客ではなくクラス自体により関連するため、これは契約を持たないすべての開発者に影響を与えるわけではありません。





コードコントラクトライブラリを使用すると、結果のコードに残すチェックを構成できます。 開発者がコードに十分自信がある場合、開発者はコードからすべてのチェックを削除し、各チェックでいくつかのプロセッササイクルを節約できます。 監視レベルとその管理の可能性について詳しくは、記事「実行期間中のクレームの監視」をご覧ください。



既存の検証方法を使用する


引数を検証する別の標準的な方法は、 NotNullNotNullOrEmptyなどの異なるメソッドのセットで特別なクラス(ガード)を使用することです コードコントラクトライブラリは、そのようなメソッドを本格的なコントラクトに変換する機能をサポートします。このためには、バリデータクラスのメソッドを属性ContractArgumentValidatorAttributeでマークする必要があります。





残念ながら、属性ContractArgumentValidatorAttributeは .NET Frameworkバージョン4.0の一部ではなく、バージョン4.5でのみ表示されます。 この状況は、ContractExtensions.csファイルをプロジェクトに追加することで解決します。このファイルは、コードコントラクトライブラリをインストールした後、%ProgramFiles%\ Microsoft \ Contracts \ Language \ CSharpに表示されます。



 public static class Guard { [ContractArgumentValidatorAttribute] public static void IsNotNull<T>(T t) where T : class { if (t == null) throw new ArgumentNullException("t"); Contract.EndContractBlock(); } }
      
      







これで、古き良きIsNotNullメソッドを使用して前提条件を確認できます。



 public int Foo(string s) { Guard.IsNotNull(s); return s.Length; }
      
      







トピックからの逸脱。 Contract.ContractFailed


Contractメソッドには2つのバージョンがあることに気づいたかもしれません が必要で 、そのうちの1つは汎用であり、目的のタイプの例外を生成するために使用できます。非一般化バージョンに違反すると、タイプContractExceptionの内部例外が生成されます。



内部例外がデフォルトでスローされる理由は、契約違反をプログラムで修復できないためです。 これはコードのバグであり、修正するにはこのコードを変更する必要があります。 ただし、前提条件のチェックに何らかのアプローチを使用する場合(現在、 Contract。Requires + 2のアプローチが必要です)、ユーザーは例外の基本タイプをインターセプトして例外を「キャッチ」できます。



ContractクラスにはContractFailedイベントがあり、前提条件/事後条件/不変条件に違反すると発生します。 たとえば、統合テストまたは単体テストを開始する前に、このイベントにサブスクライブできます。前提条件が満たされてもテストが緑色のままの場合は、袖をまくり上げて、処理対象外の例外をキャッチする不注意なプログラマーを探します。



おわりに


ここで説明するアプローチのいずれかを使用してコントラクトプログラミングに切り替えると、チームの残りのメンバーに影響を与えることなく、コードをコントラクトにスムーズに移行できます。 同時に、すべての人にマシンに追加のユーティリティをインストールさせることなく、契約のすべてのメリット(前のセクションで説明した前提条件の違反について学習する機能を含む)で古い検証ツールを使用できます。



All Articles