Я слышал, что рекомендуется проверять аргументы открытых методов:
- Стоит ли проверять на ноль, если он не ожидает ноль?
- Должен ли метод проверять свои параметры?
- MSDN - CA1062: проверка аргументов общедоступных методов (у меня есть .NET фон, но вопрос не специфичен для C #)
Мотивация понятна. Если модуль будет использоваться неправильно, мы хотим немедленно вызвать исключение, а не непредсказуемое поведение.
Что беспокоит меня, так это то, что неправильные аргументы - не единственная ошибка, которая может быть допущена при использовании модуля. Вот несколько сценариев ошибок, в которых нам нужно добавить логику проверки, если мы следуем рекомендациям и не хотим эскалации ошибок:
- Входящий звонок - неожиданные аргументы
- Входящий звонок - модуль находится в неверном состоянии
- Внешний вызов - неожиданные результаты возвращены
- Внешний вызов - неожиданные побочные эффекты (двойной вход в вызывающий модуль, нарушение других состояний зависимостей)
Я попытался учесть все эти условия и написать простой модуль с одним методом (извините, не-C # ребята):
public sealed class Room
{
private readonly IDoorFactory _doorFactory;
private bool _entered;
private IDoor _door;
public Room(IDoorFactory doorFactory)
{
if (doorFactory == null)
throw new ArgumentNullException("doorFactory");
_doorFactory = doorFactory;
}
public void Open()
{
if (_door != null)
throw new InvalidOperationException("Room is already opened");
if (_entered)
throw new InvalidOperationException("Double entry is not allowed");
_entered = true;
_door = _doorFactory.Create();
if (_door == null)
throw new IncompatibleDependencyException("doorFactory");
_door.Open();
_entered = false;
}
}
Теперь безопасно =)
Это довольно жутко. Но представьте, насколько это может быть жутко в реальном модуле с десятками методов, сложным состоянием и множеством внешних вызовов (привет, любители внедрения зависимостей!). Обратите внимание, что если вы вызываете модуль, поведение которого может быть переопределено (незакрытый класс в C #), то вы делаете внешний вызов, и последствия не будут предсказуемы в области действия вызывающей стороны.
Подводя итог, что это правильный путь и почему? Если вы можете выбрать один из вариантов ниже, пожалуйста, ответьте на дополнительные вопросы.
Проверьте использование всего модуля. Нужны ли нам юнит-тесты? Есть ли примеры такого кода? Следует ли ограничивать использование зависимостей в использовании (так как это приведет к большей логике проверки)? Разве не практично переносить эти проверки во время отладки (не включать в выпуск)?
Проверьте только аргументы. Исходя из моего опыта, проверка аргументов - особенно проверка на ноль - является наименее эффективной проверкой, потому что ошибка аргумента редко приводит к сложным ошибкам и эскалации ошибок. Большую часть времени вы получите NullReferenceException
на следующей линии. Так почему проверки аргументов такие особые?
Не проверяйте использование модуля. Это довольно непопулярное мнение, вы можете объяснить, почему?