Почему плохая обработка исключений?

Язык Google Go не имеет исключений как выбор дизайна, а Линус, известный в Linux, назвал исключения чушью. Почему?


person joemoe    schedule 15.11.2009    source источник
comment
Создатель ZeroMQ пишет о том, что, по его мнению, было ошибкой писать его на C ++ (в основном из-за обработки ошибок) 250bpm.com/blog:4   -  person serbaut    schedule 20.10.2012
comment
Go может не иметь исключений, но у него есть паники, от которых вы можете избавиться (пока отложенные операторы все еще выполняются) и которые обеспечивают нелокальный поток управления ...   -  person Kerrek SB    schedule 19.03.2017
comment
Вот хорошая статья lighterra.com/papers/exceptionsharmful (Обработка исключений считается вредной)   -  person masterxilo    schedule 17.10.2017
comment
Afaics, в Go можно моделировать исключения со значительным шаблоном, хотя это point может быть более значимым для переноса из синтаксического сахара, чем для написания шаблона вручную.   -  person Shelby Moore III    schedule 22.01.2018


Ответы (15)


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

Рассмотрим на простом примере что-то вроде этого:

class Frobber
{
    int m_NumberOfFrobs;
    FrobManager m_FrobManager;

public:
    void Frob()
    {
        m_NumberOfFrobs++;

        m_FrobManager.HandleFrob(new FrobObject());
    }
};

Если предположить, что FrobManager будет delete FrobObject, это выглядит нормально, верно? Или, может быть, нет ... Представьте, что FrobManager::HandleFrob() или operator new выдают исключение. В этом примере приращение m_NumberOfFrobs не откатывается. Таким образом, любой, кто использует этот экземпляр Frobber, будет иметь возможно поврежденный объект.

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

Например, вы можете думать об этом, как о мьютексах. Внутри критического раздела вы полагаетесь на несколько операторов, чтобы убедиться, что структуры данных не повреждены и другие потоки не могут видеть ваши промежуточные значения. Если какое-либо из этих утверждений просто случайно не выполняется, вы попадаете в мир боли. Теперь уберите блокировки и параллелизм и подумайте о каждом подобном методе. Думайте о каждом методе как о транзакции перестановок состояния объекта, если хотите. В начале вызова метода объект должен быть в чистом состоянии, а в конце также должно быть чистое состояние. Между ними переменная foo может не соответствовать bar, но ваш код со временем исправит это. Что означают исключения, так это то, что любое из ваших утверждений может прервать вас в любой момент. В каждом отдельном методе ответственность лежит на вас, чтобы сделать все правильно и откатиться, когда это произойдет, или упорядочить свои операции, чтобы выбросы не влияли на состояние объекта. Если вы ошиблись (а допустить такую ​​ошибку легко), то вызывающий абонент увидит ваши промежуточные значения.

Такие методы, как RAII, которые программисты на C ++ любят упоминать как окончательное решение этой проблемы, имеют большое значение для защиты от этого. Но они не серебряная пуля. Это гарантирует, что вы высвобождаете ресурсы сразу же, но не избавляет вас от необходимости думать о повреждении состояния объекта и о том, что вызывающие абоненты видят промежуточные значения. Таким образом, для многих проще сказать по стилю кодирования без исключений. Если вы ограничиваете тип кода, который пишете, эти ошибки труднее вносить. Если вы этого не сделаете, довольно легко ошибиться.

О безопасном кодировании на C ++ написаны целые книги. Многие эксперты ошиблись. Если это действительно так сложно и имеет так много нюансов, возможно, это хороший знак, что вам нужно игнорировать эту функцию. :-)

person asveikau    schedule 15.11.2009
comment
Интересный ответ, но он ничего не отражает в моем опыте программирования. Так что я предполагаю, что это либо зависит от культуры (может быть, больше проблема для Java или C ++, чем, скажем, Python), либо от предметной области. - person ddaa; 16.11.2009
comment
Исключения, написанные на управляемом языке с использованием шаблона try-catch-finally, никогда не должны оставлять недопустимое состояние, если они написаны правильно; так как блок finally гарантированно будет выполнен, объекты могут быть освобождены оттуда. Об остальном должны позаботиться переменные, выходящие за пределы области видимости и сборка мусора. - person Robert Harvey; 17.11.2009
comment
@ddaa Проблема определенно возможна в Python. В результате обычно бывает трудно воспроизвести ошибку. Возможно, вы проявили особую дотошность или вам повезло. Но тогда вы правы, что это больше проблема C ++, где наиболее распространенной ошибкой из-за плохого EH является утечка памяти. Я постарался подчеркнуть, что утечки - не самая серьезная проблема. @Robert GC уменьшит утечку памяти, но я не уверен, что управляемый код избавит вас от ошибки программиста. В частности, если кто-то не обращает внимания на безопасность исключений, потому что не думает, что это проблема на их языке, это плохой знак. - person asveikau; 17.11.2009
comment
ИМО, мы можем обрабатывать ошибки с помощью возвращаемых значений, иногда нам приходится использовать исключения, потому что третьи стороны будут генерировать исключения --- Есть ли какие-либо преимущества исключения перед возвращаемым значением? - person baye; 27.09.2010
comment
@lzprgmr, безусловно, есть: исключения позволяют вам иметь дело с diff. типы ошибок при разн. места в коде. Обработка ошибки подключения может потребовать повторного подключения, но не в середине глубоко вложенной функции. Вы хотите передать это диспетчеру соединений или чему-то еще. Затем работа с возвращаемыми значениями заставляет вас проверять ошибки при каждом отдельном вызове и всплывать вручную (например, в случае ошибки сброса соединения). Кроме того, возвращаемые значения складываются во вложенные вызовы: func3 может возвращать -1, func2 вызывает func3, возвращает -2 при своих ошибках, -1 для func3 и т. Д. - person abourget; 20.05.2013
comment
Я голосовал против, но я отменил это, потому что это причина, по которой на исключения смотрят свысока. Однако, на мой взгляд, почти любой метод или фрагмент кода могут потерпеть неудачу. Вы не можете обработать каждое состояние ошибки, вводя для него возвращаемое значение. Вы потеряете информацию об ошибке. Думая, что вы можете сохранить все хорошо синхронизированным, проверяя каждый оператор и выполняя очистку, вы получаете очень запутанный код - обнаружение ошибки в нескольких операторах и очистка одного или двух ресурсов, не связанных с GC, намного чище. - person Maarten Bodewes; 05.05.2014
comment
Серьезно, если вы хотите исключить исключения как концепцию, не используйте C ++ для демонстрации проблемы. Все мы знаем, что исключения в C ++ - это PITA, но почти все остальное в C ++ тоже сложно. Используйте что-то вроде Python или Java. - person Bastian Voigt; 05.06.2014
comment
@owlstead: если код может вернуть объект ошибки, тип которого не зависит от нормального типа возвращаемого значения, то код, который замечает, что объект ошибки был возвращен, сможет получить столько же информации - если не больше - об ошибке это произошло, чем код, перехватывающий исключение. Настоящие проблемы с исключениями заключаются в следующем: (1) существующие фреймворки не предлагают хороших способов борьбы с ошибками, возникающими при очистке от других ошибок; (2) существующие фреймворки не предлагают хорошего способа отличить исключение, которое ускользает от метода по причинам, ожидаемым этим методом, и исключение, которое ускользает ... - person supercat; 04.07.2014
comment
... потому что они были неожиданно выброшены изнутри внутреннего метода. - person supercat; 04.07.2014
comment
вы показываете случай, когда вы не можете получить надежные гарантии автоматически (RAII или нет, вам понадобится дополнительный код), но как другие методы могли бы решить проблему более простым и элегантным способом? - person a.lasram; 29.08.2015
comment
Здесь личное раздражение, но что, черт возьми, должно означать почти все? «большинство» означает «больше половины, но не все», «каждый» означает «все», поэтому «почти все» означает «более половины, но не все, но все», что является просто тарабарщиной. Вы ищете слово «почти». - person Neutrino; 24.05.2016
comment
Я не понимаю аргумент, что приращение m_NumberOfFrobs не откатывается и в целом об инвариантах. Если бы мы использовали возвращаемое значение без исключения, каков выигрыш? По-прежнему m_NumberOfFrobs не откатывается. Чтобы откатить m_NumberOfFrobs, вам нужно перехватить исключение или проверить возвращаемое значение. Эти две вещи кажутся мне одинаково плохими или хорошими для этой цели. А при 10-кратном вызове HandleFrob обработка исключений позволяет избежать дублирования кода. - person robsosno; 09.10.2017
comment
По крайней мере, для Java-разработчика вполне естественно, что любой вызов может вызвать исключение. Так что это не скрытая вещь, это предположение по умолчанию. Поэтому в вашем примере мы просто делаем логичную вещь: увеличиваем m_NumberOfFrobs после того, как frob был успешно добавлен в менеджер. Но вы должны были сделать то же самое и в Go / Rust, потому что, если вы увеличиваете, а после этого явно возвращаетесь с ошибкой, вы получаете ту же проблему. - person ddekany; 09.01.2021
comment
Разве исключения не предназначены только для отладки? Я обнаружил, что обычно могу находить определенные ошибки, связанные с памятью, намного быстрее, позволяя своим исключениям вызывать основные ошибки, обрабатывать их, а затем закрывать программу. Затем я беру результат и исправляю проблему. - person ZeroPhase; 07.05.2021
comment
Это ужасный и совершенно неверный пример. Прежде всего, вы увеличиваете Frobcount перед успешным добавлением FrobObject. Это просто плохое программирование. Во-вторых, если новый FrobObject выдает исключение, ваш HandleFrob даже не будет выполняться, не будет ничего добавлять (объект должен быть создан сначала, прежде чем вы добавляете его к чему-либо), поэтому нет несогласованного состояния. Это тот тип мышления, при котором исключение ложно обозначается как плохое. - person Walt Howard; 18.05.2021

Причина отсутствия исключений в Go объясняется в FAQ по языковому дизайну Go:

Исключения - похожая история. Было предложено несколько вариантов исключений, но каждый из них значительно усложняет язык и время выполнения. По самой своей природе исключения охватывают функции и, возможно, даже горутины; они имеют самые разные последствия. Также есть опасения по поводу того, какое влияние они окажут на библиотеки. Они, по определению, являются исключительными, но опыт работы с другими поддерживающими их языками показывает, что они оказывают глубокое влияние на спецификации библиотек и интерфейсов. Было бы неплохо найти дизайн, который позволил бы им быть действительно исключительными, не поощряя превращение распространенных ошибок в особый поток управления, который требует компенсации от каждого программиста.

Как и дженерики, исключения остаются открытой проблемой.

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

ОБНОВЛЕНИЕ - май 2012 г.

Дизайнеры Go слезли с заборов. В их FAQ теперь говорится следующее:

Мы считаем, что привязка исключений к структуре управления, как в идиоме try-catch-finally, приводит к запутанному коду. Это также побуждает программистов отмечать слишком много обычных ошибок, таких как невозможность открытия файла, как исключительные.

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

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

Подробнее см. В статье «Отложить, паника и восстановление».

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


... и Линус, известный в Linux, назвал исключения чушью.

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

Принципиально нарушена вся обработка исключений C ++. Особенно это плохо для ядер.

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

Я пришел к выводу, что Линус вообще не называл исключения (в общем) чушью!

person Stephen C    schedule 15.11.2009

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

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

person Robert Harvey    schedule 15.11.2009
comment
@ Роберт: вы не должны использовать их для управления потоком программы, я не думал об этом таким образом, новая перспектива для меня: P +1 - person o.k.w; 15.11.2009
comment
Это тоже действительно зависит от языка. Трудно избежать исключений, если вы, например, программируете на Java. - person Charles Salvia; 15.11.2009
comment
@Charles: Я думаю, дело в том, что исключения подходят в ситуациях, которые указывают на ошибку, неправильно настроенную систему или необоснованные вводы. Большинство исключений библиотеки Java можно избежать с помощью обычного кода рабочего процесса. - person Artelius; 15.11.2009
comment
они не должны стоить дорого. например, вы можете реализовать попытку, которая требует нулевого времени выполнения, и запросить обработчики исключений в таблице на основе адресов вызывающих, которые он видит в стеке ... Я бы сказал, что главные причины не использовать исключения - это не вообще связано с производительностью. - person asveikau; 15.11.2009
comment
Перефразировано; вопрос явно намекает на использование исключений в целом или не использование исключений вообще (они дерьмо или даже не на языке). Ваш ответ только показывает, почему исключения плохо сказываются на производительности при использовании для потока управления программой. - person Maarten Bodewes; 06.05.2014
comment
@owlstead: у меня нет другого мнения. - person Robert Harvey; 06.05.2014
comment
Конечно, вы - разработчик :) Но дело в том, что вы объясняете, когда использовать исключения. И вы это хорошо объясняете. Но об этом ли спрашивали? - person Maarten Bodewes; 06.05.2014
comment
@owlstead: Только что понял, что отвечаю на это четыре с половиной года назад. - person Robert Harvey; 06.05.2014
comment
Ага, извини, копаю червей. Я попал сюда, когда искал конкретные вопросы / ответы по исключениям. Я часто так делаю, голосую и при необходимости внося правки. Иногда я тоже голосую против, в этом случае я думал, что это требует объяснения, извините, что побеспокоил :) - person Maarten Bodewes; 06.05.2014
comment
Дорогостоящая стоимость связана с другим стилем кодирования, и чтобы код отслеживания, который делает попытку бесплатной, требует, чтобы некоторые оптимизации не могли быть выполнены. - person Lothar; 04.02.2015
comment
А когда вы начали ненавидеть Python;) В Python идоматическим способом часто является использование исключений для управления потоком (например, получение значения из dict). Я не согласен с этим, кстати, но следует отметить, что исключения должны отмечать исключительные условия, и что вы не должны использовать их для управления потоком программы, очень далеко от всеобщего мнения. - person Paul Draper; 07.10.2015
comment
@asveikau подробнее о влиянии на производительность. - person Shelby Moore III; 22.01.2018
comment
Обратите внимание, что Python полностью противоречит этому практическому правилу (это означает критику Python). - person martinkunev; 18.05.2021

Я не согласен с тем, что «исключать исключения можно только в исключительной ситуации». Хотя в целом это правда, это заблуждение. Исключения составляют условия ошибки (сбои выполнения).

Независимо от того, какой язык вы используете, возьмите копию Руководства по разработке платформы: условные обозначения, идиомы и шаблоны для многоразовых библиотек .NET (2-е издание). Глава о выбросе исключений не имеет аналогов. Несколько цитат из первого издания (2-е у меня на работе):

  • НЕ возвращать коды ошибок.
  • Коды ошибок можно легко проигнорировать, что часто бывает.
  • Исключения являются основным средством сообщения об ошибках в рамках.
  • Хорошее практическое правило состоит в том, что если метод не выполняет то, что предполагает его название, это следует рассматривать как сбой на уровне метода, приводящий к исключению.
  • НЕ используйте исключения для нормального потока управления, если это возможно.

Есть страницы с заметками о преимуществах исключений (согласованность API, выбор местоположения кода обработки ошибок, повышенная надежность и т. Д.). Есть раздел о производительности, который включает несколько шаблонов (Tester-Doer, Try-Parse).

Исключения и обработка исключений - это неплохо. Как и любую другую функцию, ими можно злоупотреблять.

person TrueWill    schedule 15.11.2009
comment
Я не могу согласиться с этим. Я не против исключений, и эта книга просто необходима, однако она ориентирована на разработку .NET и C #. - person ; 12.10.2012
comment
Я знаю, что это очень давно, просто хотел прокомментировать, что, похоже, между типом .NET и типом * nix возникнет общее разногласие по стилю. Все библиотеки, которые я использовал в качестве разработчика Linux, используют коды возврата и руководства по стилю * nix, которые я читал (например, моя компания и Google, например,) просто говорят, что мы не делаем исключений. Думаю, это интересно. - person jarvisteve; 18.07.2014
comment
Платформы должны обрабатывать исключения иначе, чем приложения конечного пользователя. У фреймворков нет другого способа справляться с ошибками, кроме создания исключения, в отличие от потребительских приложений. - person 0x6C38; 28.05.2016
comment
БИНГО. Исключения - это немного больше, чем просто условия ошибки. Это любое состояние, при котором ваша функция не может выполнять свою работу, соответствовать ожиданиям. Он столкнулся с исключительными обстоятельствами. Должен ли openfile () вызывать исключение, если файл отсутствует? Это зависит от того, что было обещано. Если openfile () разрешено создать файл, если он не существует, без исключения. - person Walt Howard; 18.05.2021

С точки зрения golang, я полагаю, что отсутствие обработки исключений делает процесс компиляции простым и безопасным.

С точки зрения Линуса, я понимаю, что код ядра - это ВСЕ угловые случаи. Так что есть смысл отказаться от исключений.

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

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

person ddaa    schedule 15.11.2009
comment
Но то, что верно для кода ядра, верно и для длительно выполняющихся собственных серверных процессов. - person Lothar; 04.02.2015
comment
Но языки высокого уровня существуют для того, чтобы облегчить программирование ЧЕЛОВЕКАМИ, а не для того, чтобы доставить удовольствие компьютеру или компилятору. - person Walt Howard; 18.05.2021

Исключения сами по себе не являются «плохими», это способ обработки исключений иногда бывает плохим. Есть несколько рекомендаций, которые можно применить при обработке исключений, чтобы облегчить некоторые из этих проблем. Некоторые из них включают (но, конечно, не ограничиваются):

  1. Не используйте исключения для управления потоком программы - т.е. не полагайтесь на операторы catch для изменения потока логики. Это не только скрывает различные детали логики, но и может привести к снижению производительности.
  2. Не генерируйте исключения из функции, если возвращаемый «статус» имеет больше смысла - генерируйте исключения только в исключительной ситуации. Создание исключений - дорогостоящая и требовательная к производительности операция. Например, если вы вызываете метод для открытия файла, но этот файл не существует, вызовите исключение FileNotFound. Если вы вызываете метод, который определяет, существует ли учетная запись клиента, возвращайте логическое значение, не возвращайте исключение «CustomerNotFound».
  3. При определении того, обрабатывать ли исключение или нет, не используйте предложение «try ... catch», если вы не можете сделать что-то полезное с исключением. Если вы не можете обработать исключение, просто позвольте ему всплыть в стеке вызовов. В противном случае исключения могут быть «проглочены» обработчиком, и детали будут потеряны (если вы не повторно выбросите исключение).
person Jeff Bramwell    schedule 15.11.2009
comment
Вернуть статус - дело непростое. Я видел слишком много кода, в котором метод GetCustomer возвращает объект Customer в случае успеха или null в случае неудачи. Во многих случаях вызывающий код никогда не проверял результат, а сразу обращался к Заказчику. Это работало большую часть времени ... - person TrueWill; 15.11.2009
comment
Но если GetCustomer выдает исключение вместо возврата null, клиентский код все равно должен обрабатывать исключение. Независимо от того, проверяет ли это значение на null или обрабатывает исключения, ответственность лежит на клиентском коде - если он что-то не выполняет должным образом, то рано или поздно что-то взорвется. - person Chris; 19.11.2009
comment
@TrueWill В настоящее время языки, поддерживающие шаблоны / обобщения, решают эту проблему, возвращая Option<T> вместо null. Например, только что познакомился с Java 8, взяв подсказку от Guava (и других). - person Maarten Bodewes; 05.05.2014
comment
@owlstead Ага. Любите монаду «может быть». Это хороший вариант, если ваш язык поддерживает его и предлагает сопоставление с образцом. - person TrueWill; 06.05.2014
comment
@owlstead Опять же, то, что сказал Крис, по-прежнему актуально для этого - пользователь должен помнить о вызове функции .orElse (defaultObject) или какой бы идиомой ни придерживался рассматриваемый язык. В конце концов, проблема скорее в программистах, чем в методе обработки ошибок. - person Pharap; 19.07.2014
comment
Вот что сбивает людей с толку в отношении исключений. Вы должны возвращать значения из функций только тогда, когда значение - это то, что функция должна была получить для вас, а не какое-то внеполосное значение, которое означает ошибку. Как вы должны вернуть код ошибки из функции, возвращающей строку? Это настолько просто, что это слишком просто для людей; Если ваша функция не может выполнять свою работу, бросьте. Вот и все. - person Walt Howard; 18.05.2021

Типичные аргументы заключаются в том, что невозможно определить, какие исключения будут исходить из определенного фрагмента кода (в зависимости от языка), и что они слишком похожи на gotos, что затрудняет мысленное отслеживание выполнения.

http://www.joelonsoftware.com/items/2003/10/13.html

По этому поводу однозначно нет единого мнения. Я бы сказал, что с точки зрения жесткого программиста на C, такого как Линус, исключения - определенно плохая идея. Однако типичный программист на Java находится в совершенно другой ситуации.

person Tim Sylvester    schedule 15.11.2009
comment
В коде C есть исключения, только по-другому. Вам нужно заключать каждый вызов нетривиальной функции в ifs, что делает использование этого языка головной болью! - person RCIX; 15.11.2009
comment
Также есть вещи _1 _ / _ 2_, что довольно плохо. - person Tim Sylvester; 15.11.2009
comment
Вы действительно хотите прислушаться к совету человека, который ценит программистов Duct Tape и не верит в необходимость модульных тестов? joelonsoftware.com/items/2009/09/23.html - person TrueWill; 15.11.2009
comment
Это классическая ошибка (или обман) в дискуссии, когда аргументы по теме заменяются отсылками к личности. Обычно это признак ухудшения дискуссии. - person Petr Gladkikh; 13.04.2012
comment
@PetrGladkikh Обсуждение началось с OP, ссылающегося на мнение Линуса ... этот обман известен как ошибка обращения к власти. Дискуссия может только пойти в гору, и не будет уловкой ответить на вопрос, почему Линус не любит исключения, ссылаясь на свою личность. - person Jim Balter; 05.08.2012
comment
@PetrGladkikh Это не обсуждение обработки исключений как таковое, оно спрашивает, почему исключения считаются людьми плохими структурами, поэтому объяснение Джоэла ничуть не хуже других - даже если вы с ним не согласны (а я, конечно, согласен, но +1 тем не менее). - person Maarten Bodewes; 06.05.2014
comment
В дополнение к комментарию @ TrueWill я хотел бы указать, что Линус не против использования gotos, поэтому его причины, по которым он не любит исключения, вероятно, совершенно не связаны с какой-либо путаницей в отношении отслеживания выполнения. - person Pharap; 19.07.2014
comment
На самом деле я склонен согласиться с Джоэлом в той статье о программистах на клейкой ленте, но только до некоторой степени. Да, очень важно отправить продукт и действительно что-то сделать, что является конечной точкой статьи. Но некоторые вещи, о которых он упомянул, кажутся совершенно неразумными, например, отказ от использования шаблонов или C ++. Я вижу, что не использую модульные тесты, потому что МНОГИЕ успешные проекты обошлись без модульных тестов, и они ДЕЙСТВИТЕЛЬНО занимают довольно много времени. С другой стороны, если у вас ОГРОМНЫЙ проект, то модульные тесты, вероятно, сэкономят вам больше времени, чем их написание. - person SeanRamey; 20.11.2017
comment
Линус может думать, что исключения не подходят, но это не так, если ваш код уже был написан без учета исключений. Если вы используете исключения в C и C ++, вам нужны самоуничтожающиеся переменные. Даже с C ++ вам нужно использовать интеллектуальные указатели для ресурсов, не относящихся к стеку (элементы, созданные с помощью new в куче). Исключения делаются НАМНОГО излишне усложненными. Вам нужен только ОДИН класс исключения для вашего приложения, и он должен содержать всю необходимую информацию об ошибке. - person Walt Howard; 18.05.2021

Исключения неплохие. Они хорошо вписываются в модель RAII C ++, которая является самой элегантной особенностью C ++. Если у вас уже есть код, небезопасный в отношении исключений, то он плох в этом контексте. Если вы пишете действительно низкоуровневое программное обеспечение, такое как ОС Linux, тогда оно плохое. Если вам нравится засорять свой код кучей проверок возврата ошибок, то они бесполезны. Если у вас нет плана управления ресурсами при возникновении исключения (которые предоставляют деструкторы C ++), то они плохие.

person paul    schedule 15.11.2009
comment
RAII полезен даже без исключений. - person Mark Ransom; 15.11.2009
comment
однако исключения бесполезны без RAII (или другого автоматического управления ресурсами). - person Greg Rogers; 16.11.2009
comment
+1 за указание на ситуации, когда исключения неуместны и что исключения не являются плохими по своей сути. - person Pharap; 19.07.2014

Таким образом, отличный вариант использования исключений ...

Допустим, вы работаете над проектом, и каждый контроллер (около 20 различных основных) расширяет один контроллер суперкласса с помощью метода действия. Затем каждый контроллер выполняет кучу вещей, отличных друг от друга, вызывая объекты B, C, D в одном случае и F, G, D в другом случае. Во многих случаях здесь на помощь приходят исключения, когда код возврата был огромным и КАЖДЫЙ контроллер обрабатывал его по-разному. Я взломал весь этот код, выбросил правильное исключение из «D», поймал его в методе действий контроллера суперкласса, и теперь все наши контроллеры согласованы. Раньше D возвращал null для НЕСКОЛЬКИХ различных случаев ошибок, о которых мы хотим сообщить конечному пользователю, но не могли, и я не хотел превращать StreamResponse в неприятный объект ErrorOrStreamResponse (смешивание структуры данных с ошибками, на мой взгляд, является неприятный запах, и я вижу, что много кода возвращают "Stream" или другой тип объекта со встроенной в него информацией об ошибке (это действительно должна быть функция, возвращающая структуру успеха ИЛИ структуру ошибки, которую я могу сделать с исключениями и кодами возврата ) .... хотя способ множественных ответов C # - это то, что я могу иногда рассмотреть, хотя во многих случаях исключение может пропускать множество слоев (слоев, которые мне не нужны для очистки ресурсов).

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

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

person Dean Hiller    schedule 03.07.2014
comment
+1 Один из лучших аргументов в пользу использования исключений, которые я читал. Можно было бы использовать более подробные примеры (например, диаграмму UML, какой-то псевдокод или какой-то реальный код), но мысль о том, что подклассы работают согласованно, является хорошей. Кроме того, тот факт, что ваши доказательства анекдотичны, показывает, что исключения полезны в реальных ситуациях, а полезность - это основная цель любой языковой функции. - person Pharap; 19.07.2014
comment
в качестве дополнения, если вы работаете в scala, вы можете вместо этого вернуть Try [ResponseType], который представляет исключение или фактический ответ. Затем вы можете следовать тому же шаблону, который я избегал выше, без фактических исключений, кроме тех, которые используются в попытке. Тогда это похоже на то, что каждый метод, который у вас есть, возвращает типы ответов 1 + n, которые, как мне кажется, необходимы. Однако в scala мы возвращаем Future [response], который работает так же, как Try, но может помочь с более асинхронным программированием. - person Dean Hiller; 16.03.2015

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

Но реальность - это совсем другое дело. У нас всегда бывают «неожиданные» ситуации. Вот почему нам нужны исключения.

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

Я думаю, что в Go они представят кое-что, что поможет справиться с «неожиданными» ситуациями. Я могу предположить, что они попытаются сделать так, чтобы это звучало менее деструктивно, как исключения, и больше как логика приложения. Но это только мое предположение.

person Mike Chaliy    schedule 15.11.2009
comment
Посмотрите на функциональные языки, у них нет побочных эффектов, поэтому у них практически нет источника для обычных ситуаций. Это сильное преувеличение. - person Stephen C; 15.11.2009
comment
Не могли бы вы подробнее рассказать об этом? - person Mike Chaliy; 15.11.2009
comment
Что 5/0 по математике? Арчин (200)? Sqrt (-1)? В математике есть множество исключительных ситуаций. - person Robert Fraser; 27.03.2010
comment
Это не исключительные ситуации ... они просто не имеют значения ... и из-за этого могут быть реализованы как исключения ... но также могут быть реализованы как варианты предварительных условий ... так что это зависит от технической реализации. - person Mike Chaliy; 27.03.2010
comment
@MikeChaliy - Мне очень жаль, но это просто софизм. Вы можете применить такое рассуждение, чтобы сказать, что НИКАКИХ исключительных ситуаций нет ни в чем, НИКОГДА. В действительности математические выражения, не имеющие значения (или не имеющие определенного значения), ЯВЛЯЮТСЯ исключительными. Это не означает, что с ними нужно бороться, выбрасывая и перехватывая исключения ... но если вы этого не сделаете, вам понадобятся либо специальные значения (например, Inf и Nan), либо операции, которые возвращают несколько значений. Короче говоря, эти случаи требуют особого обращения. - person Stephen C; 16.04.2013
comment
Наш разговор начался 3 года назад :). Во всяком случае, какое-то особое отношение, это как раз и было моей отправной точкой. - person Mike Chaliy; 16.04.2013
comment
Компьютеры - это машины состояний. Не идеальный математический мир. - person Arunav Sanyal; 12.10.2016
comment
@RobertFraser: именно поэтому у вас есть NaN в арифметике с плавающей запятой. - person shuhalo; 12.05.2021

Парадигма обработки исключений C ++, которая составляет частичную основу для Java и, в свою очередь, .net, вводит несколько хороших концепций, но также имеет некоторые серьезные ограничения. Одно из ключевых намерений разработки обработки исключений - позволить методам гарантировать, что они будут либо удовлетворять своим постусловиям, либо генерировать исключение, а также гарантировать, что любая очистка, которая должна произойти перед выходом метода, произойдет. К сожалению, парадигмы обработки исключений в C ++, Java и .net не могут предоставить никаких хороших средств обработки ситуации, когда неожиданные факторы препятствуют выполнению ожидаемой очистки. Это, в свою очередь, означает, что нужно либо рискнуть, что все остановится, если произойдет что-то неожиданное (подход C ++ к обработке исключения происходит во время раскрутки стека), либо принять возможность того, что условие, которое не может быть разрешено из-за возникшей проблемы во время очистки стека будет ошибочно принята за проблему, которая может быть разрешена (и могла бы быть, если бы очистка была успешной), или принять возможность того, что неразрешимая проблема, очистка которой с разматыванием стека вызывает исключение, которое обычно разрешается, может пойти незамеченным, поскольку код, который обрабатывает последнюю проблему, объявляет ее «решенной».

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

person supercat    schedule 11.12.2012

Я не читал все другие ответы, поэтому об этом уже упоминалось, но есть одна критика в том, что они вызывают разрыв программ в длинных цепочках, что затрудняет отслеживание ошибок при отладке кода. Например, если Foo () вызывает Bar (), который вызывает Wah (), который вызывает ToString (), то случайное нажатие неправильных данных в ToString () в конечном итоге выглядит как ошибка в Foo (), почти полностью не связанной функции.

person kingfrito_5005    schedule 16.06.2015
comment
По крайней мере, в Java у вас будет трассировка стека для каждого исключения, которая будет отображать всю цепочку вызовов с ToString () в ней. - person ddekany; 09.01.2021
comment
Вы можете добавить трассировку стека в C ++. Используйте Google. Лично напишите одну строку, например: string data = Socket (TCP) .Connect (75.32.44.23) .ReadAll () Если гораздо удобнее для чтения, чем SOCKET s = socket (TCP); если (если s == -1) вернуть errono; int rval = s.connect (75.32.44.23); если (rval == -1) return errno; char buffer [1024]; int rval = s.read (sizeof (буфер), буфер); если (rval == -1) return errno; буфер возврата; ‹--- ошибка, не удается вернуть буфер на основе стека. - person Walt Howard; 18.05.2021

Для меня вопрос очень простой. Многие программисты неправильно используют обработчик исключений. Больше языковых ресурсов - лучше. Уметь обрабатывать исключения - это хорошо. Одним из примеров неправильного использования является значение, которое должно быть целым числом, которое не проверяется, или другой ввод, который может делиться и не проверяться на деление нуля ... обработка исключений может быть простым способом избежать дополнительной работы и тяжелого мышления, программист может захотеть сделать грязный ярлык и применить обработку исключений ... Утверждение: «профессиональный код НИКОГДА не дает сбоев» может быть иллюзорным, если некоторые из проблем, обрабатываемых алгоритмом, являются неопределенными по своей природе. Возможно, в неизвестных по своей природе ситуациях хорошо сработает обработчик исключений. Хорошие методы программирования являются предметом дискуссий.

person iuri    schedule 13.09.2013
comment
Проблема не в том (или не должно быть) в том, может ли код выйти из строя - более серьезная проблема заключается в том, насколько, если код все-таки выходит из строя, нужно заботиться о деталях. Если кто-то пытается загрузить документ и один из методов чтения данных терпит неудачу, часто все равно, какой из них, так как результат остается тем же самым: документ не может быть загружен. По идее, обработка исключений должна подходить для этого. Проблема в том, что парадигмы обработки исключений в .NET и Java не предоставляют никакого удобного способа отличить скучные исключения, которые следует объединять вместе, от тех, которые не должны. - person supercat; 13.09.2013

  • Необработанное исключение - это вообще плохо.
  • Плохо обработанное исключение - это плохо (конечно).
  • «Хорошее / плохое» обработки исключений зависит от контекста / области видимости и уместности, а не ради этого.
person o.k.w    schedule 15.11.2009

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

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

person mario    schedule 13.11.2010