Так, є вагомі причини:
- Він точно визначає, що є нульовим, що може бути не очевидним із
NullReferenceException
- Це робить код невдалим при неправильному введенні, навіть якщо якась інша умова означає, що значення не розменоване
- Це робить виняток ще до того, як метод може мати будь-які інші побічні ефекти, до яких ви могли б дістатись до першого переорієнтування
- Це означає, що ви можете бути впевнені, що якщо передати параметр у щось інше, ви не порушуєте їхній контракт
- Він документує вимоги вашого методу (використання кодових контрактів навіть краще, ніж звичайно)
Щодо ваших заперечень:
- Це повільніше : Ви виявили, що це насправді є вузьким місцем у вашому коді, або ви здогадуєтесь? Перевірка недійсності відбувається дуже швидко, і в переважній більшості випадків вони не будуть вузьким місцем
- Це ускладнює підтримку коду : я думаю навпаки. Я думаю, що простіше використовувати код там, де чітко зрозуміло, чи може параметр нульовий чи ні, і де ви впевнені, що ця умова виконується.
І на ваше твердження:
Очевидно, що код, який використовує s, все одно видасть виняток.
Справді? Розглянемо:
void f(SomeType s)
{
Console.WriteLine("I've got a message of {0}", s);
}
Це використовує s
, але не створює винятку. Якщо недійсним s
є значення null, і це вказує на те, що щось не так, виняток є найбільш підходящою поведінкою тут.
Тепер, де ви розміщуєте ці перевірки перевірки аргументів, це інша справа. Ви можете вирішити довіряти коду у вашому власному класі, тому не турбуйтеся про приватні методи. Ви можете вирішити довіряти решті збірки, тому не турбуйтеся про внутрішні методи. Ви майже напевно повинні перевірити аргументи для загальнодоступних методів.
Побічна примітка: перевантаження конструктора з одним параметром ArgumentNullException
має бути просто іменем параметра, тому ваш тест повинен бути:
if (s == null)
{
throw new ArgumentNullException("s");
}
Крім того, ви можете створити метод розширення, дозволяючи дещо лаконічніше:
s.ThrowIfNull("s");
У моїй версії (загального) методу розширення я змушую його повертати початкове значення, якщо воно не є нульовим, що дозволяє писати такі речі:
this.name = name.ThrowIfNull("name");
Ви також можете мати перевантаження, яке не приймає імені параметра, якщо вас це не надто турбує.