developer tip

일반적인 예외를 잡는 것이 정말 나쁜가요?

copycodes 2021. 1. 5. 08:14
반응형

일반적인 예외를 잡는 것이 정말 나쁜가요?


FXCop으로 일부 레거시 코드를 분석하는 동안 try 블록 내에서 일반적인 예외 오류를 포착하거나 특정 예외를 찾고 있어야하는 것이 정말 나쁘다는 생각이 듭니다. 엽서에 대한 의견을 부탁드립니다.


분명히 이것은 유일한 대답이 "상황에 따라 다르다"는 질문 중 하나입니다.

가장 중요한 것은 예외를 포착하는 곳입니다. 일반적으로 라이브러리는 예외를 포착하는 데 더 보수적이어야하지만 프로그램의 최상위 수준 (예 : 메인 메소드 또는 컨트롤러의 액션 메소드 상단 등)에서는 포착하는 것에 대해 더 자유로울 수 있습니다.

그 이유는 "OutOfMemoryException"과 같이 라이브러리와 관련이없는 문제를 숨길 수 있기 때문에 라이브러리의 모든 예외를 포착하고 싶지 않기 때문입니다. 반면에 예외를 포착하고 표시 한 다음 종료하는 main () 메서드 내에서 예외를 포착하는 것에 대해 이야기하고 있다면 여기에서 예외를 포착하는 것이 안전 할 것입니다.

모든 예외를 포착하는 가장 중요한 규칙은 모든 예외를 조용히 삼키지 말아야한다는 것입니다. 예를 들어 Java에서 다음과 같이합니다.

try { 
    something(); 
} catch (Exception ex) {}

또는 이것은 Python에서 :

try:
    something()
except:
    pass

이는 추적하기 가장 어려운 문제 일 수 있기 때문입니다.

경험상의 좋은 규칙은 자신이 적절하게 처리 할 수있는 예외 만 포착해야한다는 것입니다. 예외를 완전히 처리 할 수없는 경우 가능한 사람에게 버블 링되도록해야합니다.


응용 프로그램의 프런트 엔드에서 일부 로깅 및 코드 정리를 수행하지 않는 한 모든 예외를 포착하는 것은 나쁘다고 생각합니다.

내 경험의 기본 규칙은 예상되는 모든 예외를 포착하는 것이며 다른 것은 버그입니다.

모든 것을 잡고 계속하면 자동차 대시 보드의 경고등 위에 고착 석고를 붙이는 것과 비슷합니다. 더 이상 볼 수 없지만 모든 것이 정상이라는 의미는 아닙니다.


예! (신청서의 "상단"제외)

예외를 포착하고 코드 실행이 계속되도록 허용함으로써 특정 문제를 처리하고 우회하거나 수정하는 방법을 알고 있음을 나타냅니다. 이것이 복구 가능한 상황이라고 말씀하셨습니다 . 예외 또는 SystemException 포착은 IO 오류, 네트워크 오류, 메모리 부족 오류, 코드 누락 오류, 널 포인터 파생 오류 등과 같은 문제를 포착 할 수 있음을 의미합니다. 이것들을 다룰 수 있다고 말하는 것은 거짓말입니다.

잘 구성된 응용 프로그램에서 이러한 복구 불가능한 문제는 스택에서 처리해야합니다.

또한 코드가 발전함에 따라 나중에 호출 된 메서드에 추가되는 새 예외를 함수에서 포착하지 않도록해야합니다 .


제 생각에는 예상하는 모든 예외를 포착해야 하지만이 규칙은 인터페이스 로직 이외의 모든 것에 적용됩니다. 호출 스택의 끝까지 모든 예외를 포착하고 로깅 / 사용자 피드백을 제공하는 방법을 만들어야하며, 필요하고 가능하면 정상적으로 종료해야합니다.

사용자에게 친숙하지 않은 스택 트레이스가 화면에 덤프되어 애플리케이션이 충돌하는 것보다 더 나쁜 것은 없습니다. 코드에 대한 (아마도 원치 않는) 통찰력을 줄뿐만 아니라 최종 사용자를 혼란스럽게하고 때로는 경쟁 응용 프로그램으로 겁을 주기도합니다.


이 문제에 대해 많은 철학적 토론 (논쟁과 유사)이있었습니다. 개인적으로 당신이 할 수있는 최악의 일은 예외를 삼키는 것이라고 생각합니다. 다음으로 최악의 상황은 예외가 사용자가 기술적 인 mumbo-jumbo로 가득 찬 불쾌한 화면을 얻는 표면까지 버블 링하도록 허용하는 것입니다.


글쎄요, 저는 일반적인 예외를 잡는 것과 특정 예외를 잡는 것 사이에 어떤 차이도 보지 못합니다. 여러 개의 catch 블록이있을 때 예외가 무엇인지에 따라 다르게 반응 할 수 있다는 점을 제외하면 예외입니다.

결론적으로, 당신은 둘 다 잡을 것 IOExceptionNullPointerException일반으로 Exception하지만, 프로그램이 반응해야하는 방식은 아마 다릅니다.


요점은 두 가지입니다.

첫째, 어떤 예외가 발생했는지 모르는 경우 어떻게 복구 할 수 있기를 바랍니다. 사용자가 잘못된 파일 이름을 입력 할 것으로 예상되면 FileNotFoundException을 예상하고 사용자에게 다시 시도하도록 지시 할 수 있습니다. 동일한 코드가 NullReferenceException을 생성하고 사용자에게 다시 시도하라고 말하면 무슨 일이 일어 났는지 알 수 없습니다.

둘째, FxCop 지침은 라이브러리 / 프레임 워크 코드에 중점을 둡니다. 모든 규칙이 EXE 또는 ASP.Net 웹 사이트에 적용되도록 설계된 것은 아닙니다. 따라서 모든 예외를 기록하고 응용 프로그램을 멋지게 종료하는 전역 예외 처리기를 갖는 것은 좋은 일입니다.


모든 예외를 잡기 문제는 당신이 잡기는 기대하지 않는 사람, 또는 실제로 당신이해야한다는 사람이 될 수 있다는 것이다 되지 잡기 될 수있다. 사실 모든 종류의 예외는 문제가 발생했음을 나타내며 계속하기 전에 문제를 해결해야합니다. 그렇지 않으면 추적하기가 쉽지 않은 데이터 무결성 문제와 기타 버그가 발생할 수 있습니다.

한 가지 예를 들어, 한 프로젝트에서 CriticalException이라는 예외 유형을 구현했습니다. 이는 개발자 및 / 또는 관리 직원의 개입이 필요한 오류 상태를 나타냅니다. 그렇지 않으면 고객에게 잘못 청구되거나 기타 데이터 무결성 문제가 발생할 수 있습니다. 예외를 로깅하는 것만으로는 충분하지 않고 전자 메일 경고를 보내야 할 때 다른 유사한 경우에도 사용할 수 있습니다.

예외의 개념을 제대로 이해하지 못한 다른 개발자는 모든 예외를 삭제하는 일반적인 try ... catch 블록에이 예외를 던질 수있는 코드를 래핑했습니다. 다행히도 나는 그것을 발견했지만 심각한 문제를 초래할 수 있었는데, 특히 잡기로되어 있었던 "매우 드문"코너 케이스가 내가 예상했던 것보다 훨씬 더 흔한 것으로 판명 되었기 때문이다.

100 % 당신이 알고 있지 않는 그래서 일반적으로 일반적인 예외를 잡는 것은 나쁜 정확히 예외의 종류가 발생하고있는 상황에서 할 것이다. 확실하지 않은 경우 대신 최상위 예외 처리기로 버블 링하십시오.

여기서 유사한 규칙은 System.Exception 유형의 예외를 절대로 던지지 않습니다. 사용자 (또는 다른 개발자)는 호출 스택의 상위에있는 특정 예외를 포착하면서 다른 사용자가 통과하도록 할 수 있습니다.

(하지만 한 가지주의 할 점이 있습니다. .NET 2.0에서는 스레드에 포착되지 않은 예외가 발생하면 전체 앱 도메인이 언로드됩니다. 따라서 스레드의 본문을 일반적인 try ... catch 블록으로 래핑하고 전달해야합니다. 전역 예외 처리 코드에 대한 모든 예외.)


나는 예외를 포착하고 그것을 기록하고 그것을 다시 던지는 악마의 옹호자 역할을하고 싶습니다. 예를 들어 코드 어딘가에 예기치 않은 예외가 발생하면이를 포착하고 간단한 스택 추적에서 사용할 수없는 의미있는 상태 정보를 기록한 다음 상위 계층으로 다시 던져 다루다.


There are two completely different use cases. The first is the one most people are thinking about, putting a try/catch around some operation that requires a checked exception. This should not be a catch-all by any means.

The second, however, is to stop your program from breaking when it could continue. These cases are:

  • The top of all threads (By default, exceptions will vanish without a trace!)
  • Inside a main processing loop that you expect to never exit
  • Inside a Loop processing a list of objects where one failure shouldn't stop others
  • Top of the "main" thread--You might control a crash here, like dump a little data to stdout when you run out of memory.
  • If you have a "Runner" that runs code (for instance, if someone adds a listener to you and you call the listener) then when you run the code you should catch Exception to log the problem and let you continue notifying other listeners.

These cases you ALWAYS want to catch Exception (Maybe even Throwable sometimes) in order to catch programming/unexpected errors, log them and continue.


I think a good guideline is to catch only specific exceptions from within a framework (so that the host application can deal with edge cases like the disk filling up etc), but I don't see why we shouldn't be able to catch all exceptions from our application code. Quite simply there are times where you don't want the app to crash, no matter what might go wrong.


Most of the time catching a general exception is not needed. Of course there are situations where you don't have a choice, but in this case I think it's better to check why you need to catch it. Maybe there's something wrong in your design.


Catching general exception, I feel is like holding a stick of dynamite inside a burning building, and putting out the fuze. It helps for a short while, but dynamite will blow anyways after a while.

Of corse there might be situations where catching a general Exception is necessary, but only for debug purposes. Errors and bugs should be fixed, not hidden.


For my IabManager class, which I used with in-app billing (from the TrivialDrive example online), I noticed sometimes I'd deal with a lot of exceptions. It got to the point where it was unpredictable.

I realized that, as long as I ceased the attempt at trying to consume an in-app product after one exception happens, which is where most of the exceptions would happen (in consume, as opposed to buy), I would be safe.

I just changed all the exceptions to a general exception, and now I don't have to worry about any other random, unpredictable exceptions being thrown.

Before:

    catch (final RemoteException exc)
    {
        exc.printStackTrace();
    }
    catch (final IntentSender.SendIntentException exc)
    {
        exc.printStackTrace();
    }
    catch (final IabHelper.IabAsyncInProgressException exc)
    {
        exc.printStackTrace();
    }
    catch (final NullPointerException exc)
    {
        exc.printStackTrace();
    }
    catch (final IllegalStateException exc)
    {
        exc.printStackTrace();
    }

After:

    catch (final Exception exc)
    {
        exc.printStackTrace();
    }

Unpopular opinion: Not really.

Catch all of the errors you can meaningfully recover from. Sometimes that's all of them.

In my experience, it matters more where the exception came from than which exception is actually thrown. If you keep your exceptions in tight quarters, you won't usually be swallowing anything that would otherwise be useful. Most of the information encoded in the type of an error is ancillary information, so you generally end up effectively catching all of them anyway (but you now have to look up the API docs to get the total set of possible Exceptions).

Keep in mind that some exceptions that should bubble up to the top in almost every case, such as Python's KeyboardInterrupt and SystemExit. Fortunately for Python, these are kept in a separate branch of the exception hierarchy, so you can let them bubble up by catching Exception. A well-designed exception hierarchy makes this type of thing really straightforward.

The main time catching general exceptions will cause serious problems is when dealing with resources that need to be cleaned up (perhaps in a finally clause), since a catch-all handler can easily miss that sort of thing. Fortunately this isn't really an issue for languages with defer, constructs like Python's with, or RAII in C++ and Rust.

ReferenceURL : https://stackoverflow.com/questions/21938/is-it-really-that-bad-to-catch-a-general-exception

반응형