Это будет скорее концептуальный ответ на вопрос, почему эти предупреждения могут существовать даже при использовании подхода «попробуй с ресурсами». К сожалению, это не то простое решение, на которое вы, возможно, надеетесь достичь.
Ошибка восстановления не может завершиться ошибкой
finally
моделирует поток управления после транзакции, который выполняется независимо от того, успешна ли транзакция или нет.
В случае терпят неудачу, finally
захватывает логик, которая выполняется в середине восстановления от ошибки, до того как он был полностью восстановлен (прежде чем мы достигнем нашего catch
назначения).
Представьте себе концептуальные проблемы , которые он представляет , чтобы столкнуться с ошибкой в середине восстановления после ошибки.
Представьте себе сервер базы данных, на котором мы пытаемся совершить транзакцию, и она не удается в середине (скажем, серверу не хватило памяти в середине). Теперь сервер хочет откатить транзакцию до точки, как будто ничего не произошло. Однако представьте, что в процессе отката возникает еще одна ошибка. Теперь мы получаем половинную транзакцию в базе данных - атомарность и неделимый характер транзакции теперь нарушены, и целостность базы данных будет нарушена.
Эта концептуальная проблема существует в любом языке, который имеет дело с ошибками, будь то C с ручным распространением кода ошибки, C ++ с исключениями и деструкторами или Java с исключениями и finally
.
finally
не может потерпеть неудачу в языках, которые предоставляют его таким же образом, деструкторы не могут потерпеть неудачу в C ++ в процессе обнаружения исключений.
Единственный способ избежать этой концептуальной и сложной проблемы - убедиться, что процесс отката транзакций и освобождения ресурсов в середине не может встретить рекурсивное исключение / ошибку.
Таким образом, единственный безопасный дизайн здесь - это проект, который writer.close()
не может потерпеть неудачу. В дизайне обычно есть способы избежать сценариев, когда такие вещи могут потерпеть неудачу во время восстановления, делая это невозможным.
К сожалению, это единственный способ - восстановление после ошибки не может быть неудачным Самый простой способ обеспечить это - сделать так, чтобы функции «освобождения ресурсов» и «обратных побочных эффектов» не могли работать. Это нелегко - правильное восстановление после ошибок сложно, а также, к сожалению, сложно проверить. Но способ добиться этого - убедиться, что любые функции, которые «уничтожают», «закрывают», «выключают», «откатывают» и т. Д., Не могут столкнуться с внешней ошибкой в процессе, поскольку такие функции часто должны быть вызванным во время восстановления после существующей ошибки.
Пример: ведение журнала
Допустим, вы хотите регистрировать вещи внутри finally
блока. Это часто будет огромной проблемой, если регистрация не может завершиться ошибкой . Ведение журнала почти наверняка может потерпеть неудачу, так как может потребоваться добавить больше данных в файл, и это может легко найти множество причин сбоя.
Таким образом, решение здесь состоит в том, чтобы сделать так, чтобы любая функция регистрации, используемая в finally
блоках, не могла бросить вызывающей стороне (она могла бы потерпеть неудачу, но она не бросит). Как мы можем это сделать? Если ваш язык позволяет выбрасывать в контексте finally, при условии, что есть вложенный блок try / catch, это был бы один из способов избежать выброса вызывающей стороне, проглатывая исключения и превращая их в коды ошибок, например, возможно, запись в журнал может быть выполнена в отдельном процесс или поток, который может выйти из строя отдельно и вне существующего стека восстановления после восстановления, раскручивается. Пока вы можете общаться с этим процессом без возможности столкнуться с ошибкой, это также будет безопасным для исключений, поскольку проблема безопасности существует только в этом сценарии, если мы рекурсивно выбрасываем из одного потока,
В этом случае мы можем избежать неудачного ведения журнала при условии, что он не генерирует ошибку, так как невозможность войти в систему и ничего не делать - это не конец света (это не утечка каких-либо ресурсов или неспособность откатить побочные эффекты, например).
В любом случае, я уверен, что вы уже можете представить, как невероятно сложно действительно сделать программное обеспечение безопасным от исключений. Возможно, нет необходимости искать это в полной мере во всем, кроме самого критически важного программного обеспечения. Но стоит обратить внимание на то, как по-настоящему добиться безопасности исключений, поскольку даже авторы библиотек очень общего назначения часто возятся здесь и разрушают всю безопасность ваших приложений с помощью библиотеки.
SomeFileWriter
Если SomeFileWriter
можно бросить внутрь close
, то я бы сказал, что это вообще несовместимо с обработкой исключений, если только вы никогда не пытаетесь закрыть его в контексте, который включает восстановление из существующего исключения. Если код для него находится вне вашего контроля, мы могли бы быть SOL, но стоило бы уведомить авторов этой вопиющей проблемы безопасности исключений. Если это находится под вашим контролем, моя главная рекомендация - убедиться, что закрытие его не может завершиться неудачей любыми необходимыми средствами.
Представьте себе, если операционная система не может закрыть файл. Теперь любая программа, которая пытается закрыть файл при завершении работы, не сможет завершить работу . Что мы должны делать сейчас, просто держать приложение открытым и в подвешенном состоянии (возможно, нет), просто утечь файловый ресурс и проигнорировать проблему (возможно, хорошо, если это не так критично)? Самый безопасный дизайн: сделайте так, чтобы невозможно было закрыть файл.