Какие исключения следует использовать для недопустимых или неожиданных параметров в .NET?

Какие типы исключений должны быть выбраны для недопустимых или неожиданных параметров в .NET? Когда я буду выбирать один вместо другого?

Последующий:

Какое исключение вы бы использовали, если у вас есть функция, ожидающая целое число, соответствующее месяцу, и вы перешли в "42"? Попадет ли это в категорию "вне диапазона", даже если это не коллекция?

Ответ 1

Мне нравится использовать: ArgumentException, ArgumentNullException и ArgumentOutOfRangeException.

  • ArgumentException - Что-то не так с аргументом.
  • ArgumentNullException - Аргумент имеет значение null.
  • ArgumentOutOfRangeException - Я не использую этот много, но обычное использование индексируется в коллекцию и дает индекс, который большой.

Существуют и другие варианты, которые не фокусируются на самом аргументе, а скорее определяют вызов в целом:

  • InvalidOperationException - Аргумент может быть ОК, но не в текущем состоянии объекта. Кредит переходит на STW (ранее Yoooder). Vote его ответ.
  • NotSupportedException - Прошедшие аргументы действительны, но просто не поддерживаются в этой реализации. Представьте себе FTP-клиент, и вы передаете команду, в которой клиент не поддерживает.

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

Мне нравится, когда сообщения об ошибках указывают на помощь, документацию или другие ресурсы. Например, Microsoft сделала хороший первый шаг со своими статьями в KB, например. "Почему я получаю сообщение об ошибке" Операция прерывания "при посещении веб-страницы в Internet Explorer?" . Когда вы сталкиваетесь с ошибкой, они указывают на статью KB в сообщении об ошибке. То, что они не делают хорошо, это то, что они не говорят вам, почему именно это не удалось.

Спасибо STW (ex Yoooder) снова за комментарии.


В ответ на ваш запрос я бы выбрал ArgumentOutOfRangeException. Посмотрите, что говорит MSDN об этом исключении:

ArgumentOutOfRangeException бросается когда метод вызывается и, по крайней мере, один из аргументов, переданных метод не равен нулю ссылка (Nothing в Visual Basic) и не содержит допустимого значения.

Итак, в этом случае вы передаете значение, но это недопустимое значение, так как ваш диапазон равен 1-12. Однако способ, которым вы документируете это, дает понять, что делает ваш API. Потому что, хотя я мог бы сказать ArgumentOutOfRangeException, другой разработчик мог бы сказать ArgumentException. Сделайте это проще и документируйте поведение.

Ответ 2

В зависимости от фактического значения и того, какое исключение лучше всего подходит:

Если это недостаточно точное, просто выведите свой собственный класс исключений из ArgumentException.

Ответ Yoooder просветил меня. Вход недействителен, если он недействителен в любое время, а вход неожиданно, если он недействителен для текущего состояния системы. Таким образом, в более позднем случае InvalidOperationException является разумным выбором.

Ответ 3

Я проголосовал за Josh answer, но хотел бы добавить еще один список:

Исключение System.InvalidOperationException должно быть выбрано, если аргумент действителен, но объект находится в состоянии, в котором аргумент не должен использоваться.

Обновление, взятое из MSDN:

InvalidOperationException используется в случаях, когда отказ вызвать метод вызван другими причинами неверные аргументы.

Скажем, что ваш объект имеет метод PerformAction (enmSomeAction), действительные enmSomeActions - это Open и Close. Если вы дважды вызываете функцию PerformAction (enmSomeAction.Open) подряд, тогда второй вызов должен вызывать InvalidOperationException (так как это было допустимо, но не для текущего состояния элемента управления)

Поскольку вы уже поступаете правильно, программируя в обороне, я имею еще одно исключение, которое упоминается в ObjectDisposedException. Если ваш объект реализует IDisposable, вы всегда должны иметь переменную класса, отслеживающую удаленное состояние; если ваш объект был удален и вызывается метод, вы должны поднять ObjectDisposedException:

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

Обновление:. Чтобы ответить на ваш вопрос: это немного неоднозначная ситуация и немного усложняется общим типом данных (не в смысле .NET Generics), являющимся используется для представления определенного набора данных; переименование или другой сильно типизированный объект будет более подходящим, но мы не всегда имеем этот контроль.

Я лично склоняюсь к ArgumentOutOfRangeException и предоставляю сообщение, которое указывает, что допустимые значения равны 1-12. Мое рассуждение состоит в том, что, когда вы говорите около месяца, если предположить, что все целочисленные представления месяцев действительны, то вы ожидаете значения в диапазоне от 1 до 12. Если бы были допущены только определенные месяцы (например, месяцы, которые имели 31 день), вы бы не имели дело с диапазоном per-se, и я бы выбрал общее исключение ArgumentException, которое указывало бы допустимые значения, и я также буду документировать их в комментариях метода.

Ответ 5

ArgumentException:

ArgumentException выбрасывается, когда метод вызывается и по меньшей мере один из переданные аргументы не соответствуют спецификация параметров вызываемого метод. Все случаи ArgumentException должен нести содержательное сообщение об ошибке, описывающее неверный аргумент, а также ожидаемый диапазон значений для аргумент.

Существует несколько подклассов для определенных типов недействительности. Ссылка содержит сводки подтипов и когда они должны применяться.

Ответ 6

Короткий ответ:
Ни

Более длинный ответ:
использование Argument * Exception (за исключением библиотеки, которая является продуктом на своем устройстве, например, библиотеки компонентов) является запахом. Исключениями являются обработка исключительной ситуации, а не ошибки, а не пользователь (например, пользовательский интерфейс API).

Самый длинный ответ:
Выбрасывание исключений для недопустимых аргументов является грубым, если вы не пишете библиотеку.
Я предпочитаю использовать утверждения по двум (или более) причинам:

  • Утверждения не нуждаются в проверке, в то время как утверждают броски, и проверяют против поиска ArgumentNullException смешно (попробуйте).
  • Утверждения лучше сообщают предполагаемое использование устройства и ближе к исполняемому файлу документация, чем поведение класса Спецификация.
  • Вы можете изменить поведение нарушения утверждения. Например, в отладочной компиляции поле сообщения прекрасное, так что ваш QA сразу ударит вас (вы также получите нарушение IDE на линии, где оно происходит), а в unit test вы можете указать ошибку утверждения как тест отказ.

Вот как выглядит обработка нулевого исключения (саркастично, очевидно):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

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

BTW: В этом конкретном случае вы могли бы использовать тип месяца, а не int. С# падает, когда дело доходит до типа безопасности (Aspect # rulez!), Но иногда вы можете предотвратить (или поймать во время компиляции) все эти ошибки.

И да, MicroSoft ошибается в этом.

Ответ 7

Существует стандартное ArgumentException, которое вы могли бы использовать, или вы могли бы подклассы и создавать свои собственные. Существует несколько определенных классов ArgumentException:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Какой из лучших работает.