Try / finally (Catch 제외)에서 예외가 발생합니까?


115

나는 대답이 '예'라고 거의 긍정적입니다. Try finally 블록을 사용하지만 Catch 블록을 사용하지 않으면 예외가 발생합니다. 옳은?

일반적으로 연습에 대한 의견이 있습니까?

세스

답변:


130

네, 당연합니다. finally물론, 당신의 블록이 예외를 던지지 않는다고 가정하면 , 이것은 원래 던진 것을 효과적으로 "대체"할 것입니다.


13
@David : C #의 finally 블록에서는 돌아올 수 없습니다.
Jon Skeet

3
msdn 설명서 는 또한이 대답을 확인합니다. 또는 호출 스택 상위에있는 try-finally 문의 try 블록에서 throw 될 수있는 예외를 포착 할 수 있습니다. 즉, try-finally 문이 포함 된 메서드를 호출하는 메서드, 해당 메서드를 호출하는 메서드 또는 호출 스택의 모든 메서드에서 예외를 포착 할 수 있습니다. 예외가 포착되지 않은 경우 finally 블록의 실행은 운영 체제가 예외 해제 작업을 트리거하도록 선택했는지 여부에 따라 다릅니다.
브로드밴드

60

일반적으로 연습에 대한 의견이 있습니까?

예. 조심해 . finally 블록이 실행 중일 때 처리되지 않은 예기치 않은 예외가 발생 했기 때문에 실행 중일 가능성이 있습니다 . 즉 , 무언가가 고장 났고 완전히 예상치 못한 일이 발생할 수 있습니다.

이 상황에서 finally 블록에서 코드를 전혀 실행하지 말아야 할 경우입니다. finally 블록의 코드는 종속 된 하위 시스템이 실제로는 심각하게 손상 될 수 있지만 정상이라고 가정하도록 빌드 될 수 있습니다. finally 블록의 코드는 상황을 악화시킬 수 있습니다.

예를 들어, 나는 종종 이런 종류의 것을 봅니다.

DisableAccessToTheResource();
try
{
    DoSomethingToTheResource();
}
finally
{
    EnableAccessToTheResource();
}

이 코드의 작성자는 "나는 세계의 상태를 일시적으로 변경하고 있습니다. 상태를 호출되기 전의 상태로 복원해야합니다"라고 생각합니다. 그러나 이것이 잘못 될 수있는 모든 방법에 대해 생각해 봅시다.

첫째, 호출자가 리소스에 대한 액세스를 이미 비활성화했을 수 있습니다. 이 경우이 코드는 아마 조기에 다시 활성화합니다.

둘째, DoSomethingToTheResource에서 예외가 발생하면 리소스에 대한 액세스를 활성화하는 것이 옳은 일입니까 ??? 리소스를 관리하는 코드가 예기치 않게 손상되었습니다 . 이 코드는 사실상 "관리 코드가 손상된 경우 다른 코드가 가능한 한 빨리 해당 코드를 호출 할 수 있는지 확인하여 끔찍하게 실패 할 수 있도록합니다 ."라고 말합니다. 이것은 나쁜 생각 인 것 같습니다.

셋째, DoSomethingToTheResource에서 예외가 발생하면 EnableAccessToTheResource도 예외를 발생시키지 않는다는 것을 어떻게 알 수 있습니까? 리소스 사용으로 인한 끔찍한 일이 무엇이든간에 정리 코드에 영향을 미칠 수 있으며,이 경우 원래 예외가 손실되고 문제를 진단하기가 더 어려워집니다.

try-finally 블록을 사용하지 않고 다음과 같은 코드를 작성하는 경향이 있습니다.

bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
    DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
    EnableAccessToTheResource();

이제 상태는 필요하지 않는 한 변경되지 않습니다. 이제 발신자의 상태가 엉망이되지 않습니다. 이제 DoSomethingToTheResource가 실패하면 액세스를 다시 활성화하지 않습니다. 우리는 무언가가 심하게 손상되었다고 가정하고 코드를 계속 실행하여 상황을 악화시킬 위험이 없습니다. 가능한 경우 호출자가 문제를 처리하도록하십시오.

그렇다면 finally 블록을 실행하는 것이 좋은 생각은 언제입니까? 첫째, 예외가 예상되는 경우. 예를 들어, 다른 사람이 파일을 잠 갔기 때문에 파일을 잠 그려는 시도가 실패 할 것으로 예상 할 수 있습니다. 이 경우 예외를 포착하여 사용자에게보고하는 것이 좋습니다. 이 경우 무엇이 깨 졌는지에 대한 불확실성이 줄어 듭니다. 청소로 상황을 악화시킬 가능성은 거의 없습니다.

둘째, 정리하는 리소스가 부족한 시스템 리소스 일 때. 예를 들어 finally 블록에서 파일 핸들을 닫는 것이 좋습니다. ( "사용"은 물론 try-finally 블록을 작성하는 또 다른 방법입니다.) 파일의 내용이 손상되었을 수 있지만 지금은 할 수있는 일이 없습니다. 파일 핸들은 결국 닫힐 것이므로 나중에보다는 더 빠를 수도 있습니다.


7
오류 처리와 관련하여 우리가 아직 업계로서의 행동을 취하지 않은 방법에 대한 더 많은 예가 있습니다. 제가 예외보다 더 좋은 제안이 있다는 것은 아니지만, 미래가 합리적으로 쉽게 적절한 조치를 취할 수있는 가능성이 더 있기를 바랍니다.
Jon Skeet

vb.net에서는 마지막으로 블록의 코드가 어떤 예외가 보류 중인지 알 수 있습니다. "하지 않은 상태, 그렇지 않으면 괜찮은 상태"와 "상태가 손상됨"을 구분하기 위해 예외 계층 구조를 설정하면 보류중인 예외가 나쁜 예외가 아닌 경우에만 마지막 블록이 실행될 수 있습니다. 저는 disposer / cleanup 루틴이 예외를 잡아 내고 DisposerFailedException을 던지는 것을 좋아합니다 ( "나쁜"범주에 있고 원래 예외를 InnerException으로 포함). 이를 용이하게하기 위해 Dispose (Ex as Exception)가있는 표준 iDisposableEx 인터페이스를보고 싶습니다.
supercat

개체가 잘못된 상태에있는 동안 예외가 발생할 수 있고 정리를 시도하면 상황이 악화되는 경우가 있다는 점을 이해하지만 이러한 문제는 정리 코드에서 처리해야하며 아마도 대리자의 도움을 받아야한다고 생각합니다. 예를 들어, 잠금 래퍼는 잠긴 개체가 잘못된 상태에 있고 그렇지 않으면 지워질 때 설정 될 수있는 "CheckIfSafeToUnlock (Ex as Exception)"함수 대리자를 노출 할 수 있습니다. 잠금을 해제하기 전에 래퍼는 대리자를 확인할 수 있습니다. 비어 있지 않으면 실행하고 true를 반환하는 경우에만 잠금을 해제 할 수 있습니다.
supercat

래퍼가 이러한 델리게이트를 사용하면 객체 상태를 조작 한 코드가 중단 된 경우 적절한 정리 작업을 선택할 수 있습니다. 이러한 작업에는 데이터 구조 복구 및 True 반환, 원래 예외를 래핑하는 또 다른 "더 심각한"예외 발생 또는 False를 반환하는 동안 원래 예외가 스며들게하는 것이 포함될 수 있습니다.
supercat

2
에릭, 훌륭한 답변에 감사드립니다. 나는 당신과 Jon이 모두 맞기 때문에 두 가지 질문을 했어야한다고 생각합니다. 어쨌든, 당신은 찬성됩니다. 질문에 관심을 가져 주셔서 감사합니다.
Seth Spearman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.