51

我刚刚安装了新发布的 Visual Studio 2017 Enterprise (RC)。但是,我无法让它与Code Contracts一起使用。我在 Visual Studio 2015 中使用代码合同没有问题。我错过了什么吗?

4

5 回答 5

27

正如其他人所指出的,微软没有优先考虑代码合同,其长期支持仍不清楚(尽管通过Roslyn正在进行一些关于语言级集成的讨论)。

然而,截至2017 年 3 月 11 日,社区贡献者Yaakov至少更新了源代码以包含Visual Studio 2017 构建目标(谢谢!)。此版本支持编译期间的静态检查,以及使用CCRewrite.

注意:此版本不通过项目的属性窗格提供配置支持。因此,需要通过手动将适当的属性添加到csproj文件来配置代码合同。有关属性的完整列表,请参阅下面的@crimbo 答案。

不幸的是,虽然这些更新已合并到主代码分支中,但它们既没有反映在Marketplace 分发版中,也没有反映在官方NuGet 包中。因此,您需要从存储库下载并编译源代码(这很简单;只需使用提供的BuildCC.bat文件)。

重要提示:代码协定的静态分析对.NET 3.5具有硬编码依赖性,默认情况下, Windows 10Visual Studio 2017中不再安装。因此,您需要确保启用此“功能”(或单独下载);否则,你会得到一个编译时错误。

或者,截至2017 年 6 月 15 日(后来于 2018 年 2 月 6 日更新),贡献者Igor Bek已将此更新包含在他的NuGet 包中,因此最简单的方法是添加CodeContracts.MSBuild到您的packages.configvia:

Install-Package CodeContracts.MSBuild -Version 1.12.0

背景Igor Bek首先将这个包放在一起作为代码合同团队的概念验证,后来它成为官方 NuGet 包(在 v1.10.10126.2 中)的基础。由于微软还没有更新官方的 NuGet 包,所以他现在是最新的。

鉴于当前的支持状态,我不鼓励人们为新项目采用代码契约,但这应该为已经投资于现有 .NET Framework 项目的代码契约的开发人员提供向后兼容性。

于 2017-09-25T19:37:36.617 回答
14

在撰写本文时,没有 VS2017 的合约定义,但如果使用Nuget 包 DotNet.Contracts ,您可以通过以下方式解决它:

  • 导航到 CodeContracts nuget 包目录 ( DotNet.Contracts.1.10.20606.1\MsBuild)
  • 复制v14.0文件夹
  • 将其重命名为v15.0

一切都应该按预期构建。

于 2016-11-28T15:53:37.957 回答
7

代码合约在 VS 2017 中不起作用的原因是:

  1. 代码协定 MSBuild 文件未在 VS 2017 的 msbuild 文件树中导入(易于修复)
  2. VS 2017 项目属性中不存在代码合同配置 UI(通过包含 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/cCodeContractsInstallDir指定为环境变量。在这种情况下,您只需添加

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

到您的 *.csproj 文件。

  1. 在 *.csproj 文件中指定所有 CodeContracts 属性(直接或通过 Import 间接)。例如:

    <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>
    

如果您有多个项目,我建议将它们放在私有 nuget 包中,并在每个项目中引用该 nuget 包。代码协定设置(来自第 2 步)可以进入您的 mycompany.codecontracts.props 文件,代码协定目标(来自第 1 步)可以进入您的 mycompany.codecontracts.targets 文件。

有关在 nuget 包中打包 msbuild 属性/目标的更多信息:https ://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#include-msbuild-props-and-targets-包内

如果有足够的兴趣,我愿意在 GitHub 上提供一个示例。

于 2017-08-17T23:22:52.413 回答
7

当前没有支持 Visual Studio 2017的 .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 产品名称,例如 Community。

这将允许您在 VS2017 中使用代码合同进行构建,但不会解决 CC 选项卡未显示在项目设置中的问题。为此,您仍然需要切换到 VS2015。

于 2017-03-23T10:49:59.073 回答
1

我发现这里建议的方法并不简单,特别是它需要在每个开发人员的机器和构建服务器上进行更改。

我决定创建我自己的非常简化的版本,Contract.Requires()只需要全局替换using调用者类中的声明。

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

何时/如果System.Diagnostics.Contracts 可用于 VS 2017和 .NetStandard,将很容易恢复到正确的版本。

实际的类是:

    /// <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");
于 2017-09-03T12:45:34.007 回答