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

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

Ответ 1

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

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

class Frobber
{
private:
    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, будет иметь поврежденный объект.

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

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

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

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

Ответ 2

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

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

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

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

ОБНОВЛЕНИЕ - Май 2012

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

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

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

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

Подробнее см. статью Defer, Panic и Recover.

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


... и линукс Linux-славы назвал исключение дерьмом.

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

"Вся работа по обработке исключений в С++ принципиально нарушена, особенно для ядер".

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

  • Не используйте исключения для управления потоком программы, т.е. не полагайтесь на операторы "catch", чтобы изменить поток логики. Это не только скрывает различные детали вокруг логики, но может привести к низкой производительности.
  • Не исключайте исключения из функции, когда возвращаемый "статус" будет иметь больше смысла - исключать исключения только в исключительной ситуации. Создание исключений - это дорогостоящая, высокопроизводительная операция. Например, если вы вызываете метод для открытия файла и этот файл не существует, бросьте исключение "FileNotFound". Если вы вызываете метод, определяющий, существует ли учетная запись клиента, возвращайте логическое значение, не возвращайте исключение "CustomerNotFound".
  • При определении того, нужно ли обрабатывать исключение, не используйте предложение "try... catch", если вы не можете сделать что-то полезное с исключением. Если вы не можете обработать исключение, вы должны просто позволить ему сбрасывать стек вызовов. В противном случае исключения могут быть "проглочены" обработчиком, и детали будут потеряны (если вы не снимете исключение).

Ответ 8

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

Ответ 9

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

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

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

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

Ответ 10

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

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

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

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

Ответ 11

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

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

Ответ 12

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

Ответ 13

  • Исключение, которое не обрабатывается, обычно плохое.
  • Исключение обработано плохо (конечно).
  • "Хорошее/плохое" обработки исключений зависит от контекста/области действия и уместности, а не ради этого.

Ответ 14

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

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

Ответ 15

Для меня вопрос очень прост. Многие программисты ненадлежащим образом используют обработчик исключений. Больше языкового ресурса лучше. Быть способным справляться с исключениями - это хорошо. Одним из примеров плохого использования является значение, которое должно быть целым, не проверяется, или другой вход, который может делить и не проверяться на деление нуля... обработка исключений может быть простым способом избежать большей работы и усердного мышления, программист может захотеть сделать грязный ярлык и применить обработку исключений... Утверждение: "профессиональный код NEVER fail" может быть иллюзорным, если некоторые из проблем, обрабатываемых алгоритмом, неопределенны по своему характеру. Возможно, в неизвестных ситуациях по природе хорошо вступает в действие обработчик исключений. Хорошая практика программирования - вопрос дебатов.