Выбрасывать / не выбрасывать исключение на основе параметра - почему это не очень хорошая идея?

StackOverflow https://stackoverflow.com/questions/624647

Вопрос

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

Например:

Uri ParseUri(string uriValue, bool throwOnError)

Теперь, конечно, я вижу, что в 99% случаев это было бы ужасно, но оправдано ли его случайное использование?

Один случай, который я видел, когда он использовался, связан с параметром "allowEmpty" при доступе к данным в базе данных или файле конфигурации.Например:

object LoadConfigSetting(string key, bool allowEmpty);

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

О чем вы думаете?Почему это должно быть большой проблемой?

Это было полезно?

Решение

Я думаю, что определенно плохая идея, чтобы решение о броске / без броска основывалось на логическом значении. А именно потому, что разработчикам, рассматривающим фрагмент кода, необходимо иметь функциональные знания API, чтобы определить, что означает логическое значение. Это само по себе плохо, но когда оно меняет основную обработку ошибок, разработчикам очень легко совершать ошибки при чтении кода.

В этом случае было бы намного лучше и удобочитаемее иметь 2 API.

Uri ParseUriOrThrow(string value);

bool TryParseUri(string value, out Uri uri);

В этом случае на 100% понятно, что делают эти API.

Статья о том, почему логические значения являются плохими параметрами: http : //blogs.msdn.com/jaredpar/archive/2007/01/23/boolean-parameters.aspx

Другие советы

Обычно лучше выбрать один механизм обработки ошибок и придерживаться его последовательно. Разрешение такого вида кода триггера не может реально улучшить жизнь разработчиков.

В приведенном выше примере, что произойдет, если синтаксический анализ завершится неудачно и throwOnError имеет значение false? Теперь пользователь должен угадать, если NULL, если будет возвращен, или бог знает ...

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

Это немного противно с точки зрения читабельности. Разработчики склонны ожидать, что каждый метод вызовет исключение, и если они захотят игнорировать исключение, они поймают его сами. С подходом «логического флага» каждый метод должен реализовывать эту семантику запрета исключений.

Однако, я думаю, что статья MSDN строго ссылается на флаги throwOnError. В этих случаях либо ошибка игнорируется внутри самого метода (плохо, поскольку он скрыт), либо возвращается какой-либо объект null / error (плохо, потому что вы не используете исключения для обработки ошибки, которая является несовместимой и сама по себе ошибочной). -prone).

В то время как ваш пример мне подходит. Исключение указывает на неспособность метода выполнять свои обязанности - возвращаемое значение отсутствует. Однако флаг allowEmpty изменяет семантику метода - поэтому то, что было бы исключением («Пустое значение»), теперь ожидается и допустимо. Кроме того, если бы выдало исключение, вы не сможете легко вернуть данные конфигурации. Так что в этом случае все нормально.

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

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

Другим примером в соответствии с этим может быть набор методов TryParse для некоторых типов значений

<код> bool DateTime.TryParse (текст строки, вне DateTime)

Наличие параметра donTThrowException уничтожает всю точку исключений (на любом языке). Если вызывающий код хочет иметь:

public static void Main()
{
        FileStream myFile = File.Open("NonExistent.txt", FileMode.Open, FileAccess.Read);
}

они приветствуются (C # даже не проверял исключения). В Java то же самое можно сделать с помощью:

public static void main(String[] args) throws FileNotFoundException
{
        FileInputStream fs = new FileInputStream("NonExistent.txt");
}

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

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

Книга Джеффри Рихтерса CLR через C # указывает - «вы должны выдать исключение, когда метод не может выполнить свою задачу, как указано его именем».

Его книга также указала на очень распространенную ошибку. Люди склонны писать код, чтобы поймать все (его слова " повсеместная ошибка разработчиков, которые не были должным образом обучены правильному использованию исключений, склонны использовать блоки catch слишком часто и ненадлежащим образом. Когда вы ловите исключение, вы заявляете что вы ожидали этого исключения, вы понимаете, почему оно произошло, и знаете, как с ним бороться. ")

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

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

Я бы сказал, что часто бывает полезно иметь параметр, который указывает, должен ли сбой вызывать исключение или просто возвращать указание на ошибку, поскольку такие параметры могут быть легко переданы из внешней подпрограммы во внутреннюю.Рассмотрим что-то вроде:

Byte[] Пакет чтения (bool DontThrowIfNone) // Документирован как возвращающий значение null, если его нет { int len = ReadByte(DontThrowIfNone);// Документировано как возвращающий значение -1, если ничего нет if (len

Если что-то вроде исключения TimeoutException при чтении данных должно вызвать исключение, такое исключение должно быть выдано в ReadByte() или ReadMultiBytesbytes().Если, однако, такой недостаток данных следует считать нормальным, то подпрограмма ReadByte() или ReadMultiBytesbytes() не должна выдавать исключение.Если бы кто-то просто использовал шаблон do / try, процедуры ReadPacket и TryReadPacket должны были бы иметь почти идентичный код, но при этом одна использовала методы Read *, а другая - методы TryRead *.Мерзкий.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top