понедельник, 10 октября 2011 г.

Code Contracts и Visual Studio Express

На сайте DevLabs (разработчиков Code Contracts) написано, что Visual Studio Express не поддерживается. Мне это показалось весьма странным, с учетом того, что System.Diagnostics.Contracts включен в .Net Framework 4. Конечно, Express версии не поддерживают PlugIn’ов, но ведь это совсем не повод, чтобы отказываться от использования CodeContract’ов, тем более, что все равно нужно выкачивать и устанавливать на машину разработчика то, что и называется CodeContracts. Установка содержит

  • ccrewrite утилита для осуществления проверки во время выполнения;
  • ccdoc утилита для генерации документации;
  • набор бибилиотек для .Net Framework’a 3.5 (по-моему, 3’й не поддерживается)

Премиум также содержит cccheck статический анализатор времени компиляции;

После установки в Visual Studio, в версиях выше Professional, в свойствах проекта появляется еще одна закладка: “Code Contracts”

 

image

Если отжать Perform Runtime Checking, то контракты начинаю срабатывать.

Если после этого открыть проект в Express версии (или в #Develope ), то контракты продолжат работать, не смотря на то, что закладки “Code Contacts” в свойствах проекта не появляется.

Итак, что же изменилось? А изменяется вот что: в файле проекта добавляются строки описывающие поведение контрактов.

Чтобы увидеть изменения нужно открыть сам файл проекта (.csproj для С#) в текстовом виде:

  • В VS Professional и выше: выгружаем проект в Solution Explorer,  правой кнопкой щелкаем на проект и выбираем Edit ProjectName.csproj;
  • в Express версии, просто лезем в папку с проектом и открываем любым простейшим текстовым редактором.

В каждую PrepertyGroup соответствующую вариантам сборки (X86/Debug/Release) добавились следующие строчки:

<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>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
<CodeContractsCustomRewriterAssembly />
<CodeContractsCustomRewriterClass />
<CodeContractsLibPaths />
<CodeContractsExtraRewriteOptions />
<CodeContractsExtraAnalysisOptions />
<CodeContractsBaseLineFile />
<CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
<CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
<CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly>
<CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>

Привожу, за что какие параметры отвечают:

  • CodeContractsEnableRuntimeChecking: если значение установлено в true, ccrewrite будет вживлять в проект проверку условий контракта. Фактически проверки выполнения контрактов начинаются с установки  этого значения и для того чтобы разобраться с использованием достаточно включить только его; 
  • CodeContractsRuntimeCheckingLevel: устанавливает уровни использования контрактов, то есть можно включить проверку только пред или постусловий. Принимаемые значения: Full, Pre and Post, Preconditions, ReleaseRequires или None;
  • CodeContractsRuntimeThrowOnFailure: если установлено в true, то контракт будет выбрасывать исключение. Если false, то проверка контракта будет вести себя как Debug.Assert;
  • CodeContractsRuntimeOnlyPublicSurface: если установлено true, то обрабатываются только публичные методы;
  • CodeContractsRuntimeCallSiteRequires: когда установлено true, реврайтер внедряет контракты в вызывающие ваш код приложения; ;
  • CodeContractsReferenceAssembly: принимает значения {Build, DoNotBuild, None}. Если выставить в Build, то создается дополнительная сборка с набором контрактов для этого проекта. Это нужно если на ваш проект ссылается еще один и ему нужно видеть, какие контракты используются или если вы хотите генерировать документацию по контрактам;
  • CodeContractsEmitXMLDocs: если установлено в true вместе с CodeContractsReferenceAssembly, генерируемая документацию к сборке будет содержать описание контрактов;
  • CodeContractsCustomRewriterAssembly: указывает пусть к сборке содержащей свою реализацию методов реврайтера;
  • CodeContractsCustomRewriterClass: содержит имя класса в котором реализованы методы, используется вместе с CodeContractsCustomRewriterAssembly;
  • CodeContractsLibPaths: здесь указываются через точку с запятой  пути к дополнительным сборкам содержащим контракты;
  • CodeContractsRunCodeAnalysis: значение включает статическую проверку кода собираемого проекта;
  • CodeContractsNonNullObligations: когда установлено true, статический анализатор будет пытаться проверять все ссылки на корректность;
  • CodeContractsBoundsObligations: когда установлено true, статический анализатор будет пытаться проверять корректность указания границ массивов;
  • CodeContractsArithmeticObligations: когда установлено true, статический анализатор будет пытаться проверять корректность математических операций, таких как деление на ноль;
  • CodeContractsRedundantAssumptions: когда установлено true, статический анализатор будет выдавать warning’и при “очевидных сравнениях” (не знаю как правильно перевести Redundant Assumptions) ;
  • CodeContractsRunInBackground: если установлено в true,  статический анализатор работает в фоновом процессе, не блокируя сборку проекта;
  • CodeContractsExtraAnalysisOptions: используется для передачи дополнительных параметров статического анализатора;
  • CodeContractsRuntimeSkipQuantifiers: если установлено в true, то некоторые контракты содержащие вызовы Contract.ForAll и Contract.Exists пропускаются. Это дает большую разницу в производительности;
  • CodeContractsEnumObligations: если установлено в true, статический анализатор проверяет границы перечислений; 
  • CodeContractsShowSquigglies: включает подчеркивание волнистой линией ошибок;
  • CodeContractsUseBaseLine: включает так называемую БазовуюЛинию. Она используется для того, чтобы статический анализатор мог отслеживать предупреждения только в изменяемом коде. При включении генерируется файл содержащий предупреждения и при последующих компиляциях они не отображаются;
  • CodeContractsBaseLineFile: имя файла в который записываются предупреждения;
  • CodeContractsExtraRewriteOptions: содержит дополнительные параметры для ccrewrite;
  • CodeContractsCacheAnalysisResults: включает кэширование статическим анализатором результатов;
  • CodeContractsAnalysisWarningLevel:  устанавливает уровень эвристики статического анализатора.

Большинство этих параметров вносить не обязательно. Параметры, которые относятся к статическому анализатору можно точно откинуть, а остальные, за исключением CodeContractsEnableRuntimeChecking имеют вполне рабочие значения по умолчанию. Если вы хотите избавить свой проект от нагромождений if…throw,то достаточно использовать  CodeContractsEnableRuntimeChecking.

Например.

Создаем в Visual Studio Express новый проект консольного приложения с таким кодом:

static void Main(string[] args)
{
    PrintMessage("Привет");
    Console.ReadLine();
}

static void PrintMessage(string str)
{
    Contract.Requires(str.Length > 10,"Message to short");
    Console.WriteLine("Сообщение: {0}",str);
}

Если приложение запустить, оно отлично отрабатывает, не смотря на то, что мы указали в контракте, что длина сообщения должна быть больше 10 символов.

Сохраняем, закрываем VS Express, вносим изменения в файл проекта, снова открываем проект в VS Express.

image

Контракты начинают срабатывать.