Я не знаю, звідки походить твердження про те, що "він не робить і часто не може виконати статичний аналіз". Перша частина твердження явно неправильна. Другий залежить від того, що ви маєте на увазі під «часто». Я хотів би сказати, що часто він проводить статичний аналіз, і рідко - це не вдається. У звичайному застосуванні для бізнесу рідко стає набагато ближчим до ніколи .
Отож ось ця справа - перша користь:
Користь 1: статичний аналіз
Звичайні твердження та перевірка аргументів мають недолік: вони відкладаються до виконання коду. З іншого боку, кодові контракти проявляються на набагато більш ранньому рівні, або на етапі кодування, або під час складання програми. Чим раніше ви виявите помилку, тим дешевше її виправити.
Перевага 2: сортування завжди актуальної документації
Код контрактів також надає своєрідну документацію, яка завжди актуальна. Якщо коментар XML методу SetProductPrice(int newPrice)
говорить про те, що newPrice
має бути вище або дорівнює нулю, ви можете сподіватися, що документація оновлена, але ви також можете виявити, що хтось змінив метод так, що newPrice = 0
кидає ArgumentOutOfRangeException
, але ніколи не змінював відповідну документацію. Зважаючи на кореляцію між кодовими контрактами та самим кодом, у вас немає проблеми із документацією, що не відповідає синхронізації.
Документація, що надається кодовими контрактами, також дорогоцінна таким чином, що часто коментарі XML не пояснюють добре прийнятних значень. Скільки разів мені було цікаво, null
чи це string.Empty
чи \r\n
є авторизованим значенням методу, і коментарі XML про це мовчали!
На закінчення, без кодових контрактів, безліч фрагментів коду є такими:
Я прийму деякі значення, але не інші, але вам доведеться відгадати чи прочитати документацію, якщо така є. Власне, не читайте документацію: вона застаріла. Просто перегляньте всі значення, і ви побачите ті, які змушують мене кидати винятки. Ви також повинні відгадати діапазон значень, який може бути повернутий, тому що навіть якщо я розповім вам трохи більше про них, це може бути неправдою, враховуючи сотні змін, внесених до мене за останні роки.
При кодових контрактах це стає:
Аргумент заголовка може бути ненульовим рядком довжиною 0..500. Наступне ціле число - це додатне значення, яке може бути нульовим лише тоді, коли рядок порожній. Нарешті, я поверну IDefinition
об’єкт, ніколи не нульовий.
Перевага 3: договори інтерфейсів
Третя перевага полягає в тому, що кодові договори розширюють можливості інтерфейсів. Скажімо, у вас є щось на кшталт:
public interface ICommittable
{
public ICollection<AtomicChange> PendingChanges { get; }
public void CommitChanges();
...
}
Як би ви, використовуючи лише твердження та винятки, гарантували, що CommitChanges
можна викликати лише тоді, коли PendingChanges
він не порожній? Як би ви гарантували, що PendingChanges
цього ніколи не буде null
?
Перевага 4: застосування результатів методу
Нарешті, четверта користь - це можливість отримувати Contract.Ensure
результати. Що робити, якщо при написанні методу, який повертає ціле число, я хочу бути впевненим, що значення ніколи не поступається або дорівнює нулю? У тому числі через п’ять років, після багатьох змін розробників? Як тільки метод має декілька точок повернення, Assert
перетворіться на кошмар технічного обслуговування для цього.
Розгляньте кодові контракти не тільки як засіб коректності вашого коду, але і більш суворий спосіб написання коду. Аналогічним чином, людина, яка використовувала виключно динамічні мови, може запитати, чому ви застосовуватимете типи на мовному рівні, тоді як ви можете робити те ж саме у твердженнях, коли це необхідно. Можна, але статичне введення простіше у використанні, менш схильне до помилок порівняно з купою тверджень та самодокументування.
Різниця між динамічним введенням та статичним введенням надзвичайно близька до різниці між звичайним програмуванням та програмуванням за контрактами.