(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Альтернативная проверка предусловий в Code Contracts

При попытке использования библиотеки Code Contracts в реальном проекте может возникнуть небольшая сложность: хотя сам класс Contract с методами проверки предусловий и постусловий, располагается в mscorlib начиная с 4-й версии .NET Framework, но без установки самой библиотеки Code Contracts, они не попадают в результирующую сборку.

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

 Однако Code Contracts поддерживает дополнительный "режим совместимости", который позволяет "жестко зашить" проверки предусловий в результирующий код, так что они будут видны всем, не зависимо от того, установлены контракты на машине разработчика или нет.

Постановка проблемы

 Давайте вначале рассмотрим пример, который более четко покажет, в чем проблема.

class SimpleClass
{
    public int Foo(string s)
    {
        Contract.Requires(s != null);
        return s.Length;
    }
}

 С этим кодом совершенно все в порядке и при попытке вызова метода Foo с null, мы получим нарушение контракта, что при установленной библиотеке Code Contracts и включенной проверке предусловий приведет к генерации исключения 'System.Diagnostics.Contracts.__ContractsRuntime.ContractException'.

 Да, именно этого мы и ждем, но особенность заключается в том, что код генерации исключения генерируется не компилятором, а отдельным процессом, который запускается сразу после компиляции. А это значит, что без библиотеки Code Contracts, выполнение этого кода приведет к генерации NullReferenceExcpetion, поскольку никакой дополнительной валидации аргументов не останется и в помине. Я неоднократно сталкивался с тем, что такое поведение вызывало примерно такую реакцию: "WTF? Куда делась моя проверка!"

 Поскольку мы не хотим слышать подобные "WTF?!?" от наших коллег, у которых контракты не установлены, то хотелось бы иметь способ зашить проверку предусловий более основательным образом.

Ручная проверка предусловий

 Библиотека Code Contracts позволяет использовать предусловия в старом формате. Это значит, что если существующий метод уже содержит проверку входных параметров (т.е. проверку предусловий), то для преобразования их в полноценные предусловия после них достаточно добавить вызов Contract.EndContractBlock:

public class SimpleClass
{
    public int Foo(string s)
    {
        if (s == null)
            throw new ArgumentNullException("s");
        Contract.EndContractBlock();
 
        return s.Length;
    }
}

 Добавление вызова Contract.EndContractBlock превращает одну (или несколько) проверок входных параметров в полноценные предусловия. Теперь, для любого разработчика, у которого контракты не установлены, этот код будет выглядеть, как и раньше. В то время, как обладатели контрактов, смогут пользоваться всеми их преимуществами, такими как проверка валидности программы с помощью Static Checker-а, автоматическая генерация документации, возможность отлова всех нарушений контрактов (подробнее об этом будет ниже). Отличие этого способа проверки лишь в том, что их нельзя отключить и выпилить из кода полностью.

 Данный подход можно совмещать с более продвинутыми техниками использования контрактов. Так, например, можно совмещать old-style проверку предусловий совместно с проверкой постусловий и инвариантов. Но поскольку постусловия и инварианты в большей мере касаются самого класса, а не его клиентов, то это никак не затронет всех тех разработчиков, у которых контракты не установлены.

ПРИМЕЧАНИЕ
 Библиотека Code Contracts позволяет настраивать, какие проверки должны оставаться в результирующем коде. Если разработчики достаточно уверены в своем коде, то они могут убрать все проверки из кода и сэкономить несколько тактов процессора на каждой из них. Более подробно об уровнях мониторинга и возможностях по их управлению можно почитать в статье: "Мониторинг утверждений в период выполнения".

Использование существующих методов проверки

 Еще одним стандартным способом валидации аргументов является использование специальных классов (guard-ов) с набором разных методов, типа NotNull, NotNullOrEmpty и т.п. Библиотека Code Contracts поддерживает возможность превращения подобных методов в полноценные контракты: для этого методы класса валидатора нужно пометить атрибутом ContractArgumentValidatorAttribute.

ПРИМЕЧАНИЕ
 К сожалению атрибут ContractArgumentValidatorAttribute не входит в состав .NET Framework версии 4.0, он появится только в версии 4.5. Разруливается эта ситуация путем добавления в ваш проект файла ContractExtensions.cs, который появится в %ProgramFiles%\Microsoft\Contracts\Language\CSharp после установки библиотеки Code Contracts.

public static class Guard
{
    [ContractArgumentValidatorAttribute]
    public static void IsNotNull<T>(T t) where T : class
    {
        if (t == null)
            throw new ArgumentNullException("t");
        Contract.EndContractBlock();
    }
}

 Теперь мы можем использовать старый добрый метод IsNotNull для проверки предусловий:

public int Foo(string s)
{
    Guard.IsNotNull(s);
 
    return s.Length;
}

Отступление от темы. Contract.ContractFailed

 Возможно, вы обращали внимание на существование двух версии метода Contract.Requires, одна из которых является обобщенной (generic) и может использоваться для генерации нужного типа исключения, нарушение же необобщенной версии приводит к генерации внутреннего (internal) исключение типа ContractException.

 Причина, по которой по умолчанию генерируется внутреннее исключение, заключается в том, что нарушение контракта не может быть восстановлено программным путем. Это баг в коде и для его устранения необходимо изменение этого кода. Однако при использовании любого подхода к проверке предусловий (Contract.Requires + 2 рассмотренных сегодня подхода), пользователь может "захавать" исключение, перехватив базовый тип исключения.

 В классе Contract есть событие ContractFailed, которое будет вызываться при нарушении предусловий/постусловий/инвариантов. Например, перед запуском интеграционного или юнит-теста можно подписаться на это событие, и если падает предусловие, но тест остается зеленым, то можно закатывать рукава и идти искать того нерадивого программиста, который ловит исключения, не предназначенные для обработки.

Заключение

 Использование одного из описанных здесь подходов для перехода к контрактному программированию позволяет плавно мигрировать код на контракты, не ломая жизнь остальной части команды. При этом вы можете использовать старые средства валидации со всеми достоинствами контрактов (включая возможность узнать о нарушении предусловий, описанную в предыдущем разделе), не заставляя всех и каждого устанавливать дополнительные утилиты на свои машины.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 16.03.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft 365 Business Standard (corporate)
Microsoft 365 Business Basic (corporate)
Microsoft Windows Professional 10, Электронный ключ
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Реестр Windows. Секреты работы на компьютере
Вопросы и ответы по MS SQL Server
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100