web-dev-qa-db-ja.com

VS2017はCodeContractsと連携しますか?

新しくリリースされたVisual Studio 2017 Enterprise(RC)をインストールしました。ただし、Microsoft CodeContractsで動作させるのに問題があります。 VS2015でCodeContractを使用しても問題ありません。何か不足していますか?

46
user2106007

この記事の執筆時点では、VS2017の契約定義はありませんが、 NugetパッケージDotNet.Contracts を使用すると、次の方法で回避できます。

  • CodeContracts nugetパッケージディレクトリに移動します(DotNet.Contracts.1.10.20606.1\MsBuild
  • v14.0フォルダーをコピーします
  • 名前をv15.0に変更します

すべてが期待どおりに構築されます。

13
user6706499

現在、Visual Studio 2017をサポートする Code Contracts for .NET のバージョンはありません。ただし、次のターゲットファイルをコピーすると、問題を解決できます。

C:\Program Files (x86)\MSBuild\4.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets

vS2017 MSBuildのImportAfterロケーションに:

C:\Program Files (x86)\Microsoft Visual Studio\2017\#YourVS2017Product#\MSBuild\15.0\Microsoft.Common.targets\ImportAfter

注:上記のパスの#YourVS2017Product#をVS2017製品名に置き換えます。コミュニティ。

これにより、VS2017でコードコントラクトを使用してビルドできますが、プロジェクト設定に表示されないCCタブの問題は解決しません。そのためには、VS2015に切り替える必要があります。

7
Edin

VS 2017でコードコントラクトが機能しない理由は次のとおりです。

  1. コードコントラクトのMSBuildファイルは、msbuildファイルのVS 2017ツリーにインポートされません(簡単に修正できます)
  2. コードコントラクトの構成UIはVS 2017プロジェクトプロパティにありません(CodeContracts msbuildプロパティを含めることで簡単に修正できます)

確かにCodeContractsの将来に関する質問は有効ですが、次を実装してCodeContractsを使用する既存のプロジェクトをVS 2017でビルドできるようにすることができます。

  1. C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targetsの内容をcsprojファイルに追加します(直接またはインポートを介して間接的に)。最も基本的なアプローチは、これをcsprojファイルに追加することです。

    <PropertyGroup>
      <CodeContractsInstallDir Condition="'$(CodeContractsInstallDir)'==''">C:\Program Files (x86)\Microsoft\Contracts\</CodeContractsInstallDir>
    </PropertyGroup>
    <Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)MsBuild\v$(VisualStudioVersion)\Microsoft.CodeContracts.targets" />
    

CodeContractsがインストールされている場合、最初のPropertyGroupは不要であり、b/c CodeContractsInstallDirは環境変数として指定する必要があることに注意してください。その場合、追加するだけで逃げられます

<Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)MsBuild\v$(VisualStudioVersion)\Microsoft.CodeContracts.targets" />

* .csprojファイルに。

  1. * .csprojファイルですべてのCodeContractsプロパティを指定します(直接またはインポート経由で間接的に)。例えば:

    <Project ToolsVersion="4.0" xmlns="http://schemas.Microsoft.com/developer/msbuild/2003">
    
    <!-- Code Contracts settings -->
    <PropertyGroup>
      <CodeContractsAssemblyMode>1</CodeContractsAssemblyMode>
      <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
      <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
      <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
      <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
      <CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
      <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
      <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
      <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
      <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
      <CodeContractsEnumObligations>False</CodeContractsEnumObligations>
      <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
      <CodeContractsInferRequires>False</CodeContractsInferRequires>
      <CodeContractsInferEnsures>False</CodeContractsInferEnsures>
      <CodeContractsInferObjectInvariants>False</CodeContractsInferObjectInvariants>
      <CodeContractsSuggestAssumptions>False</CodeContractsSuggestAssumptions>
      <CodeContractsSuggestRequires>True</CodeContractsSuggestRequires>
      <CodeContractsSuggestEnsures>False</CodeContractsSuggestEnsures>
      <CodeContractsSuggestObjectInvariants>False</CodeContractsSuggestObjectInvariants>
      <CodeContractsDisjunctiveRequires>False</CodeContractsDisjunctiveRequires>
      <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
      <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
      <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
      <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs>
      <CodeContractsCacheAnalysisResults>True</CodeContractsCacheAnalysisResults>
      <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
      <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
      <CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
    </PropertyGroup>
    
    <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
    </PropertyGroup>
    
    <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
      <CodeContractsRuntimeCheckingLevel>ReleaseRequires</CodeContractsRuntimeCheckingLevel>
    </PropertyGroup>
    
    </Project>
    

いくつかのプロジェクトがある場合は、これらをプライベートのヌゲットパッケージに入れ、各プロジェクトでそのヌゲットパッケージを参照することをお勧めします。コードコントラクトの設定(ステップ2から)はmycompany.codecontracts.propsファイルに入れ、コードコントラクトのターゲット(ステップ1から)はmycompany.codecontracts.targetsファイルに入れます。

Nubuildパッケージでのmsbuildプロパティ/ターゲットのパッケージ化の詳細については、こちらをご覧ください: https://docs.Microsoft.com/en-us/nuget/create-packages/creating-a-package#include -msbuild-props-and-targets-in-a-package

十分な関心がある場合は、GitHubで例を提供します。

5
crimbo

ここで提案されているアプローチは単純ではなく、特に各開発者のマシンとビルドサーバーの変更が必要になるということがわかりました。

呼び出し元クラスでusing宣言のグローバル置換のみを必要とするContract.Requires()の独自の非常に簡略化されたバージョンを作成することにしました。

using MYCommon.Diagnostics; //System.Diagnostics.Contracts;

System.Diagnostics.ContractsVS 2017で利用可能 および.NetStandardの場合/ ifの場合、適切なバージョンに簡単に戻すことができます。

実際のクラスは次のとおりです。

    /// <summary>
    ///   Contract.Requires(config != null); in VS 2017 not throw  ArgumentNullException
    /// The class is workaround for https://stackoverflow.com/questions/40767941/does-vs2017-work-with-codecontracts 
    /// </summary>
    public class Contract
    {
        public static void Requires(bool condition, string message = null)
        {
            Requires<ArgumentNullException>(condition, message);
        }
        public static void Requires<TException>(bool condition, string message=null) where TException:Exception , new ()
        {
            if (!condition)
            {
                //https://stackoverflow.com/questions/41397/asking-a-generic-method-to-throw-specific-exception-type-on-fail/41450#41450
                var e=default(TException);
                try
                {
                    message = message ?? "Unexpected Condition"; //TODO consider to pass condition as lambda expression
                    e =  Activator.CreateInstance(typeof(TException), message) as TException;
                }
                catch (MissingMethodException ex)
                {
                    e = new TException();
                }
                throw e;
            }
        }
    }

本質的な制限は、通常の使用法Contract.Requires(param1!=null);では、パラメーターの名前を使用して例外をスローできないことであり、より適切な使用法は少し長くなります。

Contract.Requires<ArgumentNullException>(param1!=null, "param1 is null");
1