Я наткнулся на эту страницу MSDN, в которой говорится:
Не создавайте исключения Exception , SystemException , NullReferenceException или IndexOutOfRangeException намеренно из собственного исходного кода.
К сожалению, он не пытается объяснить, почему. Я могу догадаться о причинах, но надеюсь, что кто-то более авторитетный в этом вопросе поделится своим мнением.
Первые два имеют некоторый очевидный смысл, но последние два кажутся теми, кого вы хотели бы использовать (и на самом деле я так и сделал).
Кроме того, это единственные исключения, которых следует избегать? Если есть другие, то какие они и почему их тоже следует избегать?
c#
exception-handling
DonBoitnott
источник
источник
NullArgumentException
что некоторые люди могут спутать обоих.ApplicationException
Ответы:
Exception
является базовым типом для всех исключений и, как таковой, ужасно неопределенным. Вы никогда не должны вызывать это исключение, потому что оно просто не содержит никакой полезной информации. Вызов кода, перехватывающего исключения, не может отличить намеренно созданное исключение (из вашей логики) от других системных исключений, которые совершенно нежелательны и указывают на реальные ошибки.Та же причина относится и к
SystemException
. Если вы посмотрите на список производных типов, вы увидите огромное количество других исключений с очень разной семантикой.NullReferenceException
иIndexOutOfRangeException
бывают другого рода. Это очень специфические исключения, поэтому их можно выбросить . Однако вы все равно не захотите их бросать, поскольку они обычно означают, что в вашей логике есть некоторые реальные ошибки. Например, исключение нулевой ссылки означает, что вы пытаетесь получить доступ к члену объекта, который являетсяnull
. Если это возможно в вашем коде, вам всегда следует явно проверятьnull
и генерировать более полезное исключение (напримерArgumentNullException
). Так же,IndexOutOfRangeException
s возникают, когда вы обращаетесь к недопустимому индексу (для массивов, а не списков). Вы всегда должны сначала убедиться, что вы этого не делаете, и сначала проверять границы, например, массива.Есть несколько других исключений, таких как эти два, например
InvalidCastException
илиDivideByZeroException
, которые возникают из-за определенных ошибок в вашем коде и обычно означают, что вы делаете что-то неправильно или сначала не проверяете некоторые недопустимые значения. Умышленно выбрасывая их из своего кода, вы просто усложняете вызывающему коду определение, были ли они выброшены из-за какой-то ошибки в коде или просто потому, что вы решили повторно использовать их для чего-то в своей реализации.Конечно, из этих правил есть некоторые исключения (ха). Если вы создаете что-то, что может вызвать исключение, которое точно соответствует существующему, не стесняйтесь использовать это, особенно если вы пытаетесь сопоставить какое-то встроенное поведение. Просто убедитесь, что вы выбрали очень конкретный тип исключения.
В целом, однако, если вы не найдете (конкретного) исключения, которое удовлетворяет ваши потребности, вам всегда следует подумать о создании собственных типов исключений для определенных ожидаемых исключений. Это может быть очень полезно для разделения источников исключений, особенно когда вы пишете код библиотеки.
источник
IList
реализацию, это не в ваших силах повлиять на запрошенные индексы, это логическая ошибка вызывающего абонента, когда индекс недействителен, и вы можете только сообщить им этой логической ошибки, вызвав соответствующее исключение. ПочемуIndexOutOfRangeException
не подходит?IList
, то вы будете бросать,ArgumentOutOfRangeException
как предлагает документация по интерфейсу .IndexOutOfRangeException
предназначен для массивов, и, насколько я знаю, вы не можете повторно реализовать массивы.NullReferenceException
обычно вызывается внутри как частный случайAccessViolationException
(IIRC, тест выглядит примерно такcmp [addr], addr
, то есть он пытается разыменовать указатель, и если он терпит неудачу с нарушением доступа, он обрабатывает разницу между NRE и AVE в полученном обработчике прерывания). Таким образом, помимо семантических причин, здесь также присутствует некоторый обман. Это также может помочь отговорить вас от проверкиnull
вручную, когда это бесполезно - если вы все равно собираетесь бросить NRE, почему бы не позволить .NET сделать это?Я подозреваю, что намерение последних двух состоит в том, чтобы предотвратить путаницу со встроенными исключениями, которые имеют ожидаемое значение. Однако я считаю, что если вы сохраняете точное намерение исключения : оно правильное
throw
. Например, если вы пишете собственную коллекцию, кажется вполне разумным использоватьIndexOutOfRangeException
- более ясный и конкретный, IMO, чемArgumentOutOfRangeException
. И хотя выList<T>
можете выбрать последнее, в BCL есть не менее 41 места (благодаря отражателю) (не считая массивов), которые создают индивидуальные настройкиIndexOutOfRangeException
- ни одно из них не является «низким уровнем», чтобы заслуживать особого исключения. Так что да, я думаю, вы можете справедливо возразить, что это правило глупо. Точно так же,NullReferenceException
полезен в методах расширения - если вы хотите сохранить семантику, которая:выкидывает
NullReferenceException
когдаobj
естьnull
.источник
SomeMethod()
нет необходимости выполнять доступ к члену, принудительно его делать неправильно. Точно так же: возьмите эту точку зрения с 41 местом в BCL, которые создают обычайIndexOutOfRangeException
, и 16 местами, которые создают обычайNullReferenceException
ArgumentNullException
вместоNullReferenceException
. Даже если синтаксический сахар из методов расширения допускает тот же синтаксис, что и обычный доступ к членам, он все равно работает по-другому. И получить NREMyStaticHelpers.SomeMethod(obj)
было бы неправильно.Как вы указываете, в статье Создание и выброс исключений (Руководство по программированию на C #) в разделе « Чего следует избегать при
System.IndexOutOfRangeException
создании исключений» Microsoft действительно указывает тип исключения, который не следует намеренно создавать из вашего собственного исходного кода.Однако в отличие от статьи throw (Справочник по C #) Microsoft, похоже, нарушает свои собственные правила. Вот метод, который Microsoft включила в свой пример:
Таким образом, сама Microsoft не является последовательной, поскольку
IndexOutOfRangeException
в своей документации дляthrow
!Это заставляет меня думать, что, по крайней мере, в случае
IndexOutOfRangeException
, могут быть случаи, когда этот тип исключения может быть выдан программистом и считаться приемлемой практикой.источник
Когда я прочитал ваш вопрос, я спросил себя, при каких условиях нужно выбросить типы исключений
NullReferenceException
,InvalidCastException
илиArgumentOutOfRangeException
.На мой взгляд, при встрече с одним из этих типов исключений я (разработчик) обеспокоен предупреждением в том смысле, что компилятор обращается ко мне. Таким образом, разрешение вам (разработчику) генерировать такие типы исключений эквивалентно продаже ответственности (компилятором). Например, это говорит о том, что компилятор теперь должен позволить разработчику решать, является ли объект
null
. Но на самом деле это должно быть делом компилятора.PS: С 2003 года я занимаюсь разработкой собственных исключений, поэтому могу выбросить их как захочу. Я думаю, что это считается лучшей практикой.
источник
Отложив обсуждение
NullReferenceException
иIndexOutOfBoundsException
отложим в сторону:Насчет ловли и бросания
System.Exception
. Я часто создавал исключения этого типа в своем коде, и меня это никогда не подводило. Точно так же очень часто я ловлю неспецифическийException
тип, и у меня это тоже неплохо сработало. Итак, почему это так?Обычно пользователи утверждают, что они должны уметь распознавать причины ошибок. Исходя из моего опыта, существует всего несколько ситуаций, в которых вам нужно обрабатывать разные типы исключений по-разному. В тех случаях, когда вы ожидаете, что пользователи будут обрабатывать ошибки программно, вам следует создать исключение более определенного типа. В других случаях меня не убеждают общие рекомендации по передовой практике.
Итак, что касается метания,
Exception
я не вижу причин запрещать это во всех случаях.РЕДАКТИРОВАТЬ: также со страницы MSDN:
Также не рекомендуется переборщить с предложениями catch с индивидуальной логикой для разных типов исключений.
источник