Throw / do-not-throw исключение на основе параметра - почему это не очень хорошая идея?
я копался в MSDN и нашел в этой статье которой был один интересный совет: не имеют публичных членов, которые могут либо бросать, либо не бросать исключения на основе какой-либо опции.
например:
Uri ParseUri(string uriValue, bool throwOnError)
теперь, конечно, я вижу, что в 99% случаев это было бы ужасно, но оправдано ли его случайное использование?
один случай, который я видел, используется с параметром "AllowEmpty" при доступе к данным в база данных или файл конфигурации. Например:
object LoadConfigSetting(string key, bool allowEmpty);
в этом случае альтернативой будет возвращать null. Но тогда вызывающий код будет завален проверкой нулевых ссылок. (И метод также исключил бы возможность фактически разрешить null как специально настраиваемое значение, если бы Вы были склонны).
каковы ваши мысли? Почему это должно быть большой проблемой?
8 ответов
Я думаю, что это определенно плохая идея, чтобы решение throw / no throw было основано на булевом. А именно потому, что это требует от разработчиков, глядя на кусок кода, чтобы иметь функциональные знания 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 ложен? Теперь пользователь должен угадать, если NULL, если будет возвращен, или бог знает...
True существует постоянная дискуссия между исключениями и возвращаемыми значениями как лучший метод обработки ошибок, но я уверен существует консенсус о том, чтобы быть последовательным и придерживаться любого выбора, который вы делаете. API не может удивить своих пользователей, и обработка ошибок должна быть частью интерфейса и быть четко определена как интерфейс.
Это довольно неприятно с точки зрения читаемости. Разработчики склонны ожидать, что каждый метод выдаст исключение, и если они хотят игнорировать исключение, они поймают его сами. При подходе "логический флаг"каждый метод должен реализовать этот семантический метод, препятствующий исключению.
тем не менее, я думаю, что статья MSDN строго относится к флагам "throwOnError". В этих случаях либо ошибка игнорируется внутри самого метода (плохо, так как она скрыта), либо некоторые возвращается тип объекта null/error (плохо, потому что вы не используете исключения для обработки ошибки, которая несовместима и сама подвержена ошибкам).
тогда как ваш пример кажется мне прекрасным. Исключение указывает на невыполнение метода своих обязанностей-возвращаемое значение отсутствует. Однако, 'allowEmpty' флаг изменения семантики метода-так что было бы исключением ("пустое значение") теперь ожидается и законно. Плюс, если вы had возникло исключение,вы не сможете легко вернуть данные конфигурации. Так что в этом случае все в порядке.
в любом публичном API действительно плохая идея иметь два способа проверить неисправное состояние, потому что тогда становится неочевидным, что произойдет, если произойдет ошибка. Просто глядя на код не поможет. Вы должны понимать семантику параметра flag (и ничто не мешает ему быть выражением).
Если проверка на null не является опцией, и если мне нужно восстановить из этого конкретного сбоя, я предпочитаю создать конкретное исключение, чтобы я мог поймать это позже и обращаться с ним соответствующим образом. В любом другом случае я делаю общее исключение.
Другим примером в соответствии с этим может быть набор методов TryParse для некоторых типов значений
bool DateTime.TryParse(string text, out 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");
}
в любом случае, это задача вызывающего абонента решить, как обрабатывать (или нет) исключение, а не вызываемого абонента.
в связанной статье есть примечание, что исключения не должны использоваться для потока управления, что, по - видимому, подразумевается в Примере questsions. Исключения должны отражать сбой уровня метода. Чтобы иметь подпись, что это нормально, чтобы бросить ошибку, кажется, что дизайн не продуман.
Джеффри Рихтерс книга CLR через C# указывает - "вы должны бросить исключение, когда метод не может выполнить свою задачу, как указано его именем".
его книги указал на очень распространенную ошибку. Люди склонны писать код, чтобы поймать все (его слова " вездесущая ошибка разработчиков, которые не были должным образом обучены правильному использованию исключений, как правило, используют блоки catch слишком часто и неправильно. Когда вы ловите исключение, вы заявляете, что ожидали этого исключения, понимаете, почему оно произошло, И знаете, как с ним бороться.")
Это заставило меня попытаться кодировать исключения, которые я могу ожидать и обрабатывать в своей логике иначе это будет ошибкой.
Проверьте свои аргументы и предотвратите исключения и поймайте только то, что вы можете обработать.
Я бы сказал, что часто полезно иметь параметр, который указывает, должен ли сбой вызвать исключение или просто вернуть индикацию ошибки, поскольку такие параметры могут быть легко переданы из внешней процедуры во внутреннюю. Рассмотрим что-то вроде:
Byte[] ReadPacket (bool DontThrowIfNone) // задокументировано как возвращающее null, если нет { int len = ReadByte (DontThrowIfNone); / / задокументировано как возвращающее -1, если ничего если (ленЕсли что-то вроде Исключение TimeoutException при чтении данных должно вызывать исключение, такое исключение должно быть создано в ReadByte () или ReadMultiBytesbytes (). Если, однако, такое отсутствие данных следует считать нормальным, то процедура ReadByte() или ReadMultiBytesbytes() не должна создавать исключения. Если просто использовать шаблон do/try, процедуры ReadPacket и TryReadPacket должны иметь почти одинаковый код, но с одним, использующим методы Read*, а другой-методы TryRead*. Мерзкий.
возможно, лучше использовать перечисление, а не логическое значение.