HabréのMicrosoft CodeContractsについては 、 すでに書いていますが 、Visual Studio用のライブラリおよびツールキットであり、C#で「契約プログラミング」の要素を使用できます。
私たちは比較的最近プロジェクトでCodeContracts(以降、単に「契約」と呼びます)を使い始め、一般的には満足していますが、コンパイル時間をさらに数秒待っています。
もちろん、ReSharperを使用しますが、追加の紹介は不要です。
しかし、効果的に機能するためには、これらの2つのツールを少し友達にする必要があるという事実には、いくつかのニュアンスがあります。
Nuance 1.メソッドの呼び出しはスキップされます
まず、ReSharperはデフォルトで、次のような契約への呼び出しの一部がアセンブリから除外され、容赦なく「ゴミ」になると考えていることに気付くでしょう。
実際、これらの呼び出しはアセンブリから除外されません 。
Contract.Requires
や
Contract.Ensures
などの契約の一部のメソッドは
[Conditional("CONTRACTS_FULL")]
属性でマークされますが、
CONTRACTS_FULL
フラグは、コンパイルの前に、プロジェクトのビルド段階で既に契約メカニズムによって設定されているため、ReSharper '設定で事前に定義されたコンパイル文字のリストを確認します。
通常のコンパイル後、コントラクトメカニズムは結果のILコードをインスツルメントし、これらの呼び出しを
__ConstractRuntime
クラスの他の呼び出しに置き換えます。 Reflectorを使用すると、これを簡単に詳細に確認できます(まだ無料ですが、急いでください)。
ただし、
Contract.Assert
の呼び出しは最初からコードに存在し、実行時に書き換えなく動作します。 それらは
[Conditional("CONTRACTS_FULL")]
としてマークされるだけでなく、
Conditional("DEBUG")
としてもマークされます。 その結果、デバッグ構成(
DEBUG
シンボルが定義されている場合)では、ReSharper自身がこの呼び出しが本物であることを理解しています。
それでは、ReSharperに
Contract.Requires
も「正直な」呼び出しであり、エディターで「台無しにすべきではない」ことをどのように説明しますか? ランタイムコントラクトチェックをオンにしているプロジェクト構成のプロジェクトビルドの[ビルド]タブで、条件付きコンパイルシンボル
CONSTRACTS_FULL
を設定するだけです(プロジェクトオプション[コードコード]> [ランタイムチェック])。
ReSharperは、欠落しているメソッドをすぐに「見る」でしょう! もちろん、1つのタブと別のタブで既に設定されているフラグの意味を繰り返すことはあまり便利ではありませんが、少なくともプロジェクトの構成ごとに1回は実行できます。
JetBrainsはすでにこの問題について知っています。そして、将来のバージョンでは、ReSharperがすぐに契約をサポートすることを願っています。
Nuance 2.考えられるNullReferenceException
契約でReSharperの機能を最大化するために行う必要がある別の設定は、価値分析設定です。 特に、ReSharperは、
null
メソッドパラメーターのチェックの有無を検出できるため、そのようなチェックがない場合に引数オブジェクトのメソッドにアクセスすると、
NullReferenceException
例外がスローされる可能性があることを警告します。
ただし、デフォルトでは、ReSharperは契約の前提条件
Contract.Requires(visitor != null)
を追加すると、
null
必要なチェックが提供されると推測できません。
幸いなことに、ReSharperにはいわゆるメカニズムがあります。 外部アノテーション。これにより、ReSharperの追加のメタ情報を使用して、サードパーティライブラリのクラスとメソッドをマークアップできます。 具体的には、ReSharperが契約チェックを「認識」し始めるために、
Contract
クラスのメソッドのメタ記述を含む特別なxmlファイルを\ Bin \ ExternalAnnotations \ mscorlib \フォルダに追加する必要があります。
<assembly name="mscorlib"> <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)"> <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/> <parameter name="condition"> <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)"> <argument>0</argument> </attribute> </parameter> </member> </assembly>
その後、すべてが適切に配置され、CodeContractsの使用が原因で誤っている場合を気にせずに、お気に入りのツールのヒントに頼ることができます!