design - Почему пустые блоки блокировки имеют плохую идею?




exception-handling try-catch (14)

Я имею в виду, например, иногда вы хотите получить некоторую дополнительную информацию где-нибудь (webservice, database), и вам действительно все равно, получите ли вы эту информацию или нет. Поэтому вы пытаетесь его получить, и если что-нибудь случится, это нормально, я просто добавлю «catch (Exception ignored) {}», и все это

Итак, идем с вашим примером, это плохая идея в этом случае, потому что вы ловите и игнорируете все исключения. Если вы ловили только EInfoFromIrrelevantSourceNotAvailable и игнорировали его, это было бы хорошо, но вы этого не сделали. Вы также игнорируете ENetworkIsDown , что может быть или не быть важным. Вы игнорируете ENetworkCardHasMelted и EFPUHasDecidedThatOnePlusOneIsSeventeen , которые почти наверняка важны.

Пустой блок catch не является проблемой, если он настроен только для исключения (и игнорирования) исключений определенных типов, которые, как вам известно, несущественны. Ситуации, в которых это хорошая идея - подавлять и молча игнорировать все исключения, не останавливаясь сначала на их рассмотрении, чтобы увидеть, ожидаются ли они / нормальные / нерелевантные или нет, чрезвычайно редки.

Я только что видел вопрос о try-catch , который люди (включая Jon Skeet) говорят, что пустые блоки блокировки - действительно плохая идея? Почему это? Нет ли ситуации, когда пустой улов не является неправильным дизайнерским решением?

Я имею в виду, например, иногда вы хотите получить некоторую дополнительную информацию где-нибудь (webservice, database), и вам действительно все равно, получите ли вы эту информацию или нет. Поэтому вы пытаетесь его получить, и если что-нибудь случится, это нормально, я просто добавлю «catch (Exception ignored) {}», и все это


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

  • регистрация исключений; в зависимости от контекста вам может потребоваться необработанное исключение или отправленное сообщение.

  • циклические технические ситуации, такие как рендеринг или обработка звука или обратный вызов списка, где само по себе будет демонстрировать проблему, бросание исключения будет просто мешать, и запись в журнал исключений, скорее всего, приведет к появлению 1000 сообщений «не в XXX» ,

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

для большинства приложений winforms я обнаружил, что для каждого пользовательского ввода достаточно иметь один оператор try. Я использую следующие методы: (AlertBox - это просто быстрая оболочка MessageBox.Show)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Тогда каждый обработчик событий может сделать что-то вроде:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

или

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

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


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

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }

Есть редкие случаи, когда это может быть оправдано. В Python вы часто видите такую ​​конструкцию:

try:
    result = foo()
except ValueError:
    result = None

Так что может быть хорошо (в зависимости от вашего приложения):

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

В недавнем .NET-проекте мне пришлось написать код для перечисления DLL-модулей плагинов, чтобы найти классы, реализующие определенный интерфейс. Соответствующий бит кода (в VB.NET, извините):

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

Хотя даже в этом случае я бы признал, что регистрация неисправности где-то, вероятно, будет улучшением.


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

Я могу только думать о нескольких случаях, когда пустой блок catch имеет какую-то значимую цель. Если какое-либо конкретное исключение, которое вы ловите, «обрабатывается», просто повторив действие, в блоке catch не нужно ничего делать. Тем не менее, было бы неплохо зарегистрировать тот факт, что произошло исключение.

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

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


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

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

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


Потому что, если вы выбрали исключение, вы никогда не увидите его - безуспешно - это самый худший вариант - вы получите ошибочное поведение и не будете думать, где это происходит. По крайней мере, поместите там сообщение в журнал! Даже если это то, что «никогда не произойдет»!


Пустой блок catch, по сути, говорит: «Я не хочу знать, какие ошибки выбрасываются, я просто проигнорирую их».

Это похоже на VB6's On Error Resume Next , за исключением того, что что-либо в блоке try после того, как выбрано исключение, будет пропущено.

Что не помогает, когда что-то ломается.


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

В соответствующей заметке большинство людей не знают, что для блока try {} может следовать либо catch {}, либо finally {}, требуется только одно.


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


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

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

Например, рассмотрим некоторую библиотеку http-сервера, мне все равно, если сервер выдает исключение, потому что клиент отключен, и index.html не может быть отправлен.


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

Но даже в этом случае сообщение об отладке может быть в порядке.


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


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

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







try-catch