Какие исключения должны быть выброшены для неверных или неожиданных параметров в .NET? - PullRequest
149 голосов
/ 21 апреля 2009

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

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

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

Ответы [ 7 ]

232 голосов
/ 21 апреля 2009

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

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

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

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

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

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

Еще раз спасибо STW (бывший Yoooder) за комментарии.


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

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

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

42 голосов
/ 21 апреля 2009

Я голосовал за ответ Джоша , но хотел бы добавить еще один в список:

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

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

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

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

Поскольку вы уже поступаете правильно, программируя в обороне, я должен упомянуть еще одно исключение - 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 день), вы бы не имели дело с самим диапазоном, и я бы выдал обобщенное исключение ArgumentException, в котором указаны действительные значения, и я также задокументировал бы их в комментариях метода.

35 голосов
/ 21 апреля 2009

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

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

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

4 голосов
/ 21 апреля 2009

исключение аргумента.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException
3 голосов
/ 21 апреля 2009

ArgumentException

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

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

1 голос
/ 22 апреля 2009

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

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

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

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

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

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

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

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

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

0 голосов
/ 21 апреля 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...