Страницы

Поиск по вопросам

воскресенье, 24 ноября 2019 г.

Что использовать правильней: if(), или try-catch?


Например что использовать, когда нужно создавать колонку в таблице только в том случае, если такой колонки в ней нет?

Я могу, как написать код, который будет проверять, существует ли колонка, и лиш
потом добавлять без ошибок, так могу и без проверки пытаться добавить колонку, обернув метод в try-catch (если есть — перехватится исключение; если нет — колонка добавится).

Результат работы будет одинаковым.

И таких примеров можно привести массу, например можно проверять файлы на существование, и только потом делать копирование, а можно перехватывать исключения.

Какой метод более грамотный, или правильный?
    


Ответы

Ответ 1



Исключения позволяют сделать код чище и понятнее, поскольку с их помощью можно разделит выполнение действий и обработку ошибок. В книге Мартина «Чистый код» этот аспект описан самым первым. При этом, по моему опыту, надо именно разделять код: выносить блок try/catch в отдельны метод. Программист, который будет разбираться с вашей программой, скажет вам «спасибо». Эта конструкция весьма громоздка, даже в простой форме try/finally, и в середине большого метода озадачит кого угодно. Второй плюс исключений в том, что они позволяют передавать дополнительную информацию Функция atoi из C ничего не могла сказать о том, почему именно не удалось конвертировать строку в целое число. int result; result = atoi("123"); /* в result 123 */ . . . result = atoi("foo"); /* что в result? */ В таких языках, как Java и C# вы можете добавить в свой класс исключений необходимы свойства, сочетая какие-нибудь коды ошибок с контекстами вызова и ещё чем-нибудь. Пример: . . . catch (SqlException e) { Console.WriteLine("Ошибка '{2}' в строке {0} процедуры {1}", e.LineNumber, e.Procedure, e.Message); } . . . Разработчики, которые давно и прочно перешли на исключения, делают свой код ещё чище, не возвращая никаких кодов ошибок, в частности, пресловутого null: // Что будет, если в хранилище нет пользователя с указанным userid? // Вернёт null или сгенерирует исключение? User user = userRepository.GetById(userId); В настоящее время считается, что правильнее создавать исключение (за аргументам снова отсылаю к книге «Чистый код»). Если метод используется для проверки наличия пользователя, его рекомендуют переделать в форму TryX. Она неуклюжа, но уже привычна, по крайней мере, для программистов .NET: User user; if (userRepository.TryGetById(userId, out user)) { . . . } Что более ценно, она однозначна: смотря на код, вы не думаете: «а что, если такого пользователя нет?» Теперь посмотрим на ситуацию с другой стороны — а когда не надо использовать исключения На мой взгляд, тогда, когда они затрудняют понимание кода. Если ситуация не исключительная, тогда в тексте программы должна быть обычная проверка. Например, приложение для нового документа генерирует имена Untitled.foo, Untitled1.foo, Untitled2.foo и т.д. Та ситуация, что файл с таким именем уже существует, является вполне обычной, н исключительной, поэтому и реализовать код корректнее с помощью обычной проверки: public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do { filename = prefix + (++suffix).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } Этот код не только быстрее, чем аналогичный с использованием исключений, но, чт важнее, понятнее другим программистам, потому что неявно передаёт им дополнительную информацию: эта штука будет случаться регулярно, и мы к этому готовы. А вот, например, невероятная ситуация, что в папке скопилось 2 миллиарда untitled-файлов — несомненное исключение. public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do { if (suffix == int.MaxValue) throw new InvalidOperationException("You're crazy!"); filename = prefix + (++suffix).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } Такой код выглядит более запутанным. К счастью, мы можем часть проверок возложить на компилятор C#: public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do checked { filename = prefix + (suffix++).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } Откуда возникает ещё одно правило: код можно сделать чище, если знать язык, платформу, библиотеку, и опираться на их исключения. Выше я написал, что исключения выполняются медленнее, чем проверки, и хочу уточнит свою мысль: не надо опираться на производительность при принятии решения. Правильна передача смысла другому программисту, чистота кода — то, к чему следует стремится. Разница в производительности, хотя и существует, никогда не была настолько большой, чтобы пользователи её замечали. Ну, если только вы не пишите код для самого вложенного цикла в каком-нибудь графическом движке.

Ответ 2



Предварительная проверка быстрее работает и зачастую является более точной. Но только обработка исключений дает полную гарантию. Пример: простое создание файла. Ошибок тут возможно - море: в имени могут быть запрещенные символы (набор разрешенных символов зависит от файловой системы!); полный путь к файлу может оказаться слишком длинным; файл мог быть уже создан; может не быть доступа для создания файлов; диск может быть защищен от записи или вообще не поддерживать ее (CD-ROM тому пример). Все эти варианты можно проверить заранее - и выдать пользователю понятное сообщение на русском языке. Но есть ситуации, которые ни одна предварительная проверка не отловит: файл может быть создан другой программой сразу после проверки; файл может быть заблокирован антивирусом; если файл создается на сетевом ресурсе - может "отвалиться" сеть.

Комментариев нет:

Отправить комментарий