Подтвердить что ты не робот

Работает ли VS2017 с CodeContracts?

Я только что установил недавно выпущенную Visual Studio 2017 Enterprise (RC). Однако мне не удается заставить его работать с Microsoft CodeContracts. У меня нет проблем с использованием CodeContract с VS2015. Я что-то пропустил?

4b9b3361

Ответ 1

В это время написания нет никаких определений контрактов для VS2017, но вы можете обойти его со следующим, если используете пакет Nuget DotNet.Contracts:

  • Перейдите в каталог пакета CodeContracts nuget (DotNet.Contracts.1.10.20606.1\MsBuild)
  • Скопируйте папку v14.0
  • Переименуйте его в v15.0

Все должно строиться так, как ожидалось.

Ответ 2

В настоящее время нет версии Code Contracts for.NET, которая поддерживает Visual Studio 2017. Однако проблема может быть устранена, если вы скопируете следующий целевой файл

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

в папку ImportAfter вашего VS2017 MSBuild:

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

Примечание. Замените # YourVS2017Product # на ваше имя продукта VS2017 по указанному выше пути, например. Сообщество.

Это позволит вам строить с помощью Code Contracts в VS2017, но не решит проблему с вкладкой CC, не отображаемой в настройках проекта. Для этого вам все равно придется переключиться на VS2015.

Ответ 3

Как отмечали другие, Microsoft не установила приоритеты в кодовых контрактах, а долгосрочная поддержка текущее обсуждение интеграции языкового уровня через Roslyn).

От 11 марта 2017 г., однако, участник сообщества Yaakov, по крайней мере, обновил исходный код, чтобы включить Задачи сборки Visual Studio 2017 (спасибо!). Эта версия обеспечивает поддержку как статической проверки во время компиляции, так и проверки времени выполнения с помощью CCRewrite.

Примечание. Эта сборка не обеспечивает поддержку конфигурации через панель свойств . Таким образом, кодовые контракты должны быть настроены вручную, добавив соответствующие свойства в файл csproj. См. Ответ @crimbo выше для полного списка свойств.

К сожалению, хотя эти обновления были объединены в ветвь главного кода, они не отражены в Распределение Marketplace или официальное пакет NuGet. Таким образом, вам необходимо загрузить и скомпилировать исходный код из репозитория (это легко, просто используйте предоставленный BuildCC.bat файл).

Важный. Статический анализ для Code Contracts имеет жестко закодированную зависимость от .NET 3.5, которая больше не устанавливается по умолчанию в Windows 10 или Visual Studio 2017. Таким образом, Я хочу, чтобы эта функция была включена (или скачать ее отдельно); в противном случае вы получите ошибку времени компиляции.

В качестве альтернативы, 15 июня 2017 года, Игорь Бек включил это обновление в свой пакет NuGet, поэтому самый простой подход - просто добавить 'CodeContracts.MSBuild' в ваш packages.config через:

Install-Package CodeContracts.MSBuild -Version 1.9.10714.3

Фон: Игорь Бек сначала поставил этот пакет как доказательство концепции для команды Code Contracts, и это было позже основой официального пакета NuGet (в версии 1.1.10126.2). Поскольку Microsoft не обновила официальный пакет NuGet, он теперь является самым современным.

Учитывая текущее состояние поддержки, я бы не стал поощрять людей к принятию кодовых контрактов для новых проектов, но это должно обеспечить обратную совместимость для разработчиков, которые уже вложили контракты на код для существующих проектов .NET Framework.

Ответ 4

Причины, по которым кодовые контракты не работают в VS 2017, следующие:

  • Кодовые контракты Файлы MSBuild не импортируются в дереве VS 2017 файлов msbuild (легко исправить)
  • Конфигурация кодовых контрактов UI не присутствует в свойствах проекта VS 2017 (легко фиксируется включением свойств Codebucks для MSbuild).

Конечно, вопросы о будущем CodeContracts действительны, но вы можете реализовать следующее, чтобы включить существующие проекты, которые используют CodeContracts для сборки в VS 2017:

  • Добавьте содержимое 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" />
    

Обратите внимание, что первая PropertyGroup не требуется, если CodeContracts установлен, b/c CodeContractsInstallDir должен быть указан как переменная среды. В этом случае вы можете уйти, просто добавив

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

в ваши файлы *.csproj.

  1. Укажите все свойства CodeContracts в файле *.csproj(прямо или косвенно через 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.

Дополнительная информация о свойствах/целях создания свойств msbuild в пакете nuget: https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package

Я хочу привести пример на GitHub, если у вас есть достаточный интерес.

Ответ 5

Я нашел, что предлагаемые здесь подходы не были простыми, в частности, что для этого потребовались бы изменения на всех машинах-разработчиках и на сервере сборки.

Я решил создать свою собственную очень упрощенную версию Contract.Requires(), для которой потребуется только глобальная замена объявления using в классах вызывающих.

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

Когда/if 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/info/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/info/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");