Работает ли Visual Studio 2017 с кодовыми контрактами?

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


person user2106007    schedule 23.11.2016    source источник
comment
Может ли кто-нибудь из Microsoft сделать комментарий, пожалуйста?   -  person user2106007    schedule 28.11.2016
comment
Кто-нибудь из команды Microsoft VS??? VS2017 вообще работает с CodeContracts?   -  person user2106007    schedule 04.03.2017
comment
Открыт официальный выпуск на github CodeContracts, не работающий в VS2017 #476, а также Поддержка Visual Studio 2017 № 451   -  person Michael Freidgeim    schedule 03.09.2017


Ответы (5)


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

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

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

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

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

Кроме того, по состоянию на 15 июня 2017 г., а затем обновлено 6 февраля, 2018 г. — участник Игорь Бек включил это обновление в свой Пакет NuGet, поэтому проще всего просто добавить CodeContracts.MSBuild к packages.config через:

Install-Package CodeContracts.MSBuild -Version 1.12.0

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

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

person Jeremy Caney    schedule 25.09.2017
comment
К вашему сведению: я проверил, что эта информация остается актуальной на момент этого комментария. Я изменил комментарий, указав ссылку на более позднее обновление пакета NuGet Игоря Бека, которое было выпущено с тех пор, как я первоначально отправил этот ответ. - person Jeremy Caney; 06.03.2020

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

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

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

person user6706499    schedule 28.11.2016
comment
Спасибо за ответ. Похоже, мне нужно вернуться к основам... как вы используете пакет CodeContracts nuget в VS2015? Вам нужно установить дополнительный компонент (например, Contracts.devlab9ts.msi)? - person user2106007; 30.11.2016
comment
Без установки Contracts.devlab9ts.msi я не вижу вкладку Code Contracts в свойствах проекта. - person user2106007; 30.11.2016
comment
user6706499 - как вы заставили его работать? вам нужно установить Contracts.devlab9ts.msi? - person user2106007; 03.12.2016
comment
Я создал тестовый проект на своей машине разработки с установленными VS2015 и VS2017. Открыв проект в VS2015, я вижу вкладку Code Contracts. Но открывая проект в VS2017, я НЕ вижу вкладку Code Contracts. Любое обходное решение? Информация? - person user2106007; 12.12.2016
comment
Ты герой!! - person Brendan; 22.02.2017
comment
Игорь - я заметил, что вы отредактировали ответ. Мне интересно, удалось ли вам заставить его работать с таким кодом, как Contract.Requires‹ArgumentException›(условие), который требует ccrewrites? - person user2106007; 18.03.2017
comment
@user2106007 — см. ответ от @JeremyCaney, я установил пакет NuGet CodeContracts.MSBuild. Это, а также редактирование файла .csproj для указания конфигурации — это все, что вам нужно сделать. - person Igor; 19.03.2018

В настоящее время нет версии Code Contracts для .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.

person Edin    schedule 23.03.2017
comment
Спасибо, Эдин. Означает ли это, что все функции кодовых контрактов будут работать, если проект уже настроен на их использование? - person user2106007; 24.03.2017
comment
Да, именно так, но я бы предложил сделать несколько тестов, чтобы убедиться, что ваши dll были переписаны с помощью CC. - person Edin; 24.03.2017
comment
Спасибо, Эдин. Мы решили двигаться дальше, учитывая неопределенное будущее Microsoft CC, потребовалось около недели, чтобы заменить Microsoft CC мини-платформой CC собственной разработки, чтобы сохранить концепции CC и большинство ее функций. Однако нам очень не хватает интерфейсных контрактов — думаю, вы понимаете, о чем я. - person user2106007; 25.03.2017
comment
Другой вариант — использовать Post# (который является коммерческим, но не слишком дорогим по сравнению с созданием собственного). Синтаксис отличается (используются атрибуты), но возможности те же. - person Jimmy Zimms; 27.04.2017
comment
Чтобы помочь другим найти его: PostSharp - person Artyom; 04.08.2017
comment
@ user2106007: Мне любопытно, захочет ли ваша организация открыть исходный код (или, по крайней мере, опубликовать) вашу внутреннюю разработку CC framework? Даже если вы сэкономите другим разработчикам недельное время разработки, мне кажется, что коллективный вклад принесет пользу. Мне также любопытно, как вы смогли справиться с постусловиями, поскольку они кажутся мне самой сложной частью для репликации без серьезного рефакторинга вызывающего кода (например, путем размещения Ensures() в конце члена). - person Jeremy Caney; 24.09.2017

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

  1. Контракты кода Файлы MSBuild не импортируются в дерево файлов msbuild VS 2017 (легко исправить)
  2. Пользовательский интерфейс конфигурации контрактов кода отсутствует в свойствах проекта 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" />
    

Обратите внимание, что первая 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#Include-msbuild-props-и-цели-в-пакете

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

person crimbo    schedule 17.08.2017
comment
@crimbo: Спасибо за подробный ответ. Установка этого на место, похоже, ничему не повредит в процессе компиляции, но также, похоже, CCRewrite не выполняется должным образом. Таким образом, во время выполнения я получаю сообщение об ошибке: Сборка (вероятно…) должна быть переписана с использованием переписчиков двоичных кодов контрактов (CCRewrite), поскольку она вызывает Contract.Requires‹TEception› и определен символ CONTRACTS_FULL. Ожидается ли это? Есть ли в Visual Studio 2017 сценарий, поддерживающий CCRewrite? - person Jeremy Caney; 24.09.2017
comment
@crimbo К вашему сведению: я решил проблему, описанную выше, вместо этого используя более новый CodeContracts.MSBuild Пакет NuGet, который включает обновления контрактов кода, необходимые для поддержки Visual Studio 2017 (по крайней мере, с точки зрения процесса MSBuild). При этом я вижу как статический анализ кода, так и его переписывание. Это также избавляет от необходимости изменять цели сборки в контрактах кода или вручную импортировать их в файл csproj. - person Jeremy Caney; 25.09.2017
comment
@JeremyCaney: я также пробовал пакет CodeContracts.MSBuild NuGet. По сути, это обрабатывает шаг 1 выше, но не обрабатывает шаг 2 (это не для меня). ccrewrite не запускался, пока не были определены свойства CodeContracts. Их можно настроить в VS 2015 через пользовательский интерфейс настроек проекта (который добавляет их в ваш .csproj); либо их можно добавить вручную или импортировать из файла. - person crimbo; 27.09.2017
comment
@crimbo: это хорошее разъяснение; Спасибо. В моем случае у меня уже были эти настройки в моем файле csproj. Но они определенно необходимы, чтобы в противном случае включить как статическую проверку, так и проверку во время выполнения. - person Jeremy Caney; 28.09.2017

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

Я решил создать свою собственную очень упрощенную версию 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");
person Michael Freidgeim    schedule 03.09.2017
comment
Это эффективная стратегия, если вашей основной задачей является проверка Requires() во время выполнения. И его можно расширить для поддержки Assert(), Assume(), Exists() и ForAll() без особых усилий. Однако, если они потребуются, будет непрактично (без серьезного рефакторинга вызывающих) поддерживать Ensures(), Invariant(), Result() или ValueAtReturn(), используя этот подход. Другими словами, это действительно работает только как сокращение для проверки предварительных условий. - person Jeremy Caney; 24.09.2017
comment
К сожалению, отсутствие поддержки Ensure делает этот вопрос спорным. С другой стороны, белый Require легко, Ensure чрезвычайно сложен. - person Wiktor Zychla; 03.06.2020