Бросить / не делать бросок исключение на основе параметра - почему это не очень хорошая идея? - PullRequest
7 голосов
/ 09 марта 2009

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

Например:

Uri ParseUri(string uriValue, bool throwOnError)

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

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

object LoadConfigSetting(string key, bool allowEmpty);

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

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

Ответы [ 8 ]

9 голосов
/ 09 марта 2009

Я думаю, что определенно плохая идея, чтобы решение о броске / без броска основывалось на логическом значении. А именно потому, что разработчикам, рассматривающим фрагмент кода, необходимо иметь функциональные знания 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

3 голосов
/ 09 марта 2009

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

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

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

1 голос
/ 09 марта 2009

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

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

1 голос
/ 09 марта 2009

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

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

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

0 голосов
/ 17 октября 2011

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

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

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

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

0 голосов
/ 09 марта 2009

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

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

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

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

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

0 голосов
/ 09 марта 2009

Наличие параметра 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");
}

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

0 голосов
/ 09 марта 2009

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

bool DateTime.TryParse(string text, out DateTime)

...