{…} 시도가 마지막으로 {…} 좋은 이유는 무엇입니까? {…} 잡기 {}를 해보십시오.


201

나는 사람들이 인수없이 catch를 사용하는 것이 좋지 않은 형태라고 말한 것을 보았습니다. 특히 catch가 아무것도하지 않으면 :

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

그러나 이것은 좋은 형태로 간주됩니다.

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

내가 알 수있는 한, finally 블록에 정리 코드를 넣는 것과 try..catch 블록 뒤에 정리 코드를 넣는 것의 유일한 차이점은 try 블록에 return 문이있는 경우입니다 (이 경우 정리 코드는 실행하지만 try..catch 이후의 코드는 실행되지 않습니다).

그렇지 않으면, 마지막으로 특별한 점은 무엇입니까?


7
처리 할 수없는 호랑이를 잡으려면 마지막으로 원하는 것을 기록해야합니다.

문서의 예외 주제는 좋은 통찰력을 줄 수 있습니다. 또한 마지막 블록 예제를 살펴보십시오 .
Athafoud

답변:


357

가장 큰 차이점은 try...catch오류가 발생했다는 사실을 숨기고 예외를 삼킨다 는 것 입니다. try..finally정리 코드를 실행 한 다음 예외가 계속 진행되어 처리 방법을 알고있는 것으로 처리됩니다.


11
캡슐화를 염두에두고 작성된 코드는 예외가 발생한 시점에서만 예외를 처리 할 수 ​​있습니다. 다른 것을 통해 임의의 예외를 처리 할 수 ​​있다는 희망에 부딪 치면 콜 스택을 다시 전달하는 것만으로도 재난의 요리법이됩니다.
David Arno

3
대부분의 경우 클래스 librray 수준보다 응용 프로그램 수준 (예 : 특정 구성 설정)에서 특정 예외가 발생하는 이유가 더 분명합니다.
Mark Cidade 2018 년

88
David-프로그램이 빨리 실패하는 것을 선호하므로 프로그램을 알 수없는 상태로 유지하는 대신 문제를 알 수 있습니다.
Erik Forbes

6
예외 후에 프로그램이 알 수없는 상태에 있으면 코드가 잘못되었습니다.
Zan Lynx

41
@DavidArno, 캡슐화를 염두에두고 작성된 모든 코드는 해당 범위 내에서만 예외를 처리해야합니다. 다른 사람이 처리 할 수 ​​있도록 다른 것을 전달해야합니다. 사용자로부터 파일 이름을 얻는 응용 프로그램이 있고 파일을 읽고 파일 판독기가 파일을 여는 예외를 얻는 경우 응용 프로그램이 말할 수 있도록 파일을 전달하거나 예외를 소비하고 새 것을 던져야합니다. , 야-파일이 열리지 않았습니다. 사용자에게 다른 것을 물어 보자. 파일 판독기는 사용자에게 프롬프트하거나 다른 조치를 취할 수 없습니다. 유일한 목적은 파일을 읽는 것입니다.
iheanyi

62

"마침내"는 "프로그램 상태가 정상인지 확인하기 위해 항상 수행해야하는 것"의 진술입니다. 따라서 예외로 인해 프로그램 상태가 중단 될 가능성이있는 경우 항상 하나를 갖는 것이 좋습니다. 컴파일러는 또한 최종 코드가 실행되도록하기 위해 많은 노력을 기울입니다.

"캐치"는 "이 예외에서 복구 할 수 있습니다"라는 문구입니다. 당신은 당신이 정말로 고칠 수있는 예외들로부터 만 회복해야합니다. 논란없이 catch는 "이봐, 나는 무엇이든 복구 할 수 있습니다!"라고 말합니다. 이것은 거의 항상 사실이 아닙니다.

모든 예외에서 회복 할 수 있다면 , 의도를 선언 한 것에 대한 의미 론적 문제 일 것입니다. 그러나 그렇지 않으며 거의 ​​확실하게 특정 프레임을 처리하여 특정 예외를 처리하는 것이 좋습니다. 따라서 마지막으로 사용하여 정리 코드를 무료로 실행하지만 지식이 풍부한 처리기가 문제를 처리하도록하십시오.


1
당신의 감정은 널리 퍼져 있지만 불행히도 다른 중요한 경우를 무시합니다. 불변이 더 이상 유지되지 않는 객체를 명시 적으로 무효화하는 것입니다. 일반적인 패턴은 코드가 잠금을 획득하고 객체를 변경 한 후 잠금을 해제하는 것입니다. 일부만 변경 한 후 예외가 발생하면 개체가 유효하지 않은 상태로 남아있을 수 있습니다. IMHO의 더 나은 대안 존재 하더라도 객체 상태가 유효하지 않고 명시 적으로 상태를 무효화하고 다시 던질 수있는 동안 발생하는 예외를 포착하는 것보다 더 나은 방법은 없습니다.
supercat

32

한 줄에서 예외가 발생하면 알 수 없기 때문입니다.

첫 번째 코드 블록을 사용하면 예외가 단순히 흡수 되고 프로그램 상태가 잘못되었을 때에도 프로그램은 계속 실행됩니다.

두 번째 블록으로, 예외가 될 것입니다 던져 최대 거품 하지만reader.Close()여전히 실행이 보장됩니다.

예외가 예상되지 않으면 try..catch 블록을 넣지 마십시오. 프로그램이 잘못된 상태가되었을 때 나중에 디버깅하기가 어려우며 그 이유를 알 수 없습니다.


21

결국 무엇이든 실행됩니다. 따라서 try 블록이 성공하면 try 블록이 실행되고 try 블록이 실패하면 catch 블록을 실행 한 다음 finally 블록을 실행합니다.

또한 다음 구문을 사용하는 것이 좋습니다.

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

using 문이 try / finally에 자동으로 래핑되므로 스트림이 자동으로 닫힙니다. (실제로 예외를 잡으려면 using 문 주위에 try / catch를 넣어야합니다).


5
이것은 정확하지 않습니다. 시도 / 캐치 코드를 포장하지 않고 사용하면, 시도는 / 마지막으로 말해야한다
pr0nin

8

다음 2 개의 코드 블록은 동일하지만 동일하지 않습니다.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. '마지막'은 의도를 밝히는 코드입니다. 컴파일러와 다른 프로그래머에게이 코드가 무엇이든 실행되어야한다고 선언합니다.
  2. 캐치 블록이 여러 개이고 정리 코드가 있으면 마지막으로 필요합니다. 마지막으로, 각 catch 블록에서 정리 코드를 복제합니다. (건조 원리)

마지막으로 블록은 특별하다. CLR은 catch 블록과 별도로 finally 블록을 사용하여 코드를 인식하고 처리하며 CLR은 finally 블록이 항상 실행되도록 보장합니다. 컴파일러의 구문 설탕이 아닙니다.


5

나는 여기에 합의 인 것처럼 보인다-빈 'catch'는 try 블록에서 발생했을 수있는 예외를 가리기 때문에 나쁘다.

또한 가독성 관점에서 'try'블록을 볼 때 해당 'catch'문이 있다고 가정합니다. 'finally'블록에서 자원이 할당 해제되도록하기 위해 'try'만 사용하는 경우 대신 'using'문을 고려할 수 있습니다 .

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

IDisposable을 구현하는 모든 개체에 'using'문을 사용할 수 있습니다. 객체의 dispose () 메서드는 블록 끝에서 자동으로 호출됩니다.


4

Try..Catch..Finally메소드가 예외를 로컬로 처리하는 방법을 알고있는 경우을 사용하십시오 . 예외는 Try, Handled in Catch에서 발생하며 정리 후에는 Final에서 수행됩니다.

메소드가 예외를 처리하는 방법을 모르지만 일단 발생하면 정리가 필요한 경우 사용 Try..Finally

이로 인해 예외는 호출 메소드로 전파되고 호출 메소드에 적합한 Catch 문이있는 경우 처리됩니다. 현재 메소드 또는 호출 메소드에 예외 핸들러가 없으면 애플리케이션이 충돌합니다.

으로 Try..Finally로컬 정리가 호출 방법에 예외를 전파하기 전에 수행되는 것을 보장한다.


1
이 답변의 기본은 절대적으로 최고입니다. 후자 중 하나가 비어있는 경우에도 시도 / 캐치 / 마지막 습관을 갖는 것이 좋습니다. catch 블록이 존재하고 비어있을 수있는 VERY RARE 환경이 있지만 최소한 try / catch / finally를 항상 쓰면 코드를 읽는 동안 빈 블록이 표시됩니다. 빈 finally 블록을 사용하는 것도 같은 방식으로 도움이됩니다. 나중에 정리해야하거나 예외시 상태를 디버깅해야하는 경우 매우 유용합니다.
Jesse Williams

3

try..finally 블록은 여전히 ​​발생하는 예외를 발생시킵니다. 모든 finally수행은 예외가 발생하기 전에 정리 코드가 실행되어 있는지 확인합니다.

빈 캐치가있는 try..catch는 모든 예외를 완전히 소비하고 발생한 사실을 숨 깁니다. 독자는 문을 닫지 만 올바른 일이 일어 났는지 알 수 없습니다. 파일에 i 를 쓰려는 의도가 있다면 ? 이 경우 코드의 해당 부분으로 만들지 않으며 myfile.txt 가 비어 있습니다. 모든 다운 스트림 방법이이를 올바르게 처리합니까? 빈 파일이 보이면 예외가 발생하여 파일이 비어 있다고 정확하게 추측 할 수 있습니까? 예외를 던지고 더 잘못하고 있다는 것을 알리는 것이 좋습니다.

또 다른 이유는 이와 같은 try..catch가 완전히 올바르지 않기 때문입니다. 이렇게하면 "무슨 일이 있어도 처리 할 수 ​​있습니다."라는 말이 있습니다. 그 StackOverflowException후에는 정리할 수 있습니까? 무엇에 대해 OutOfMemoryException? 일반적으로, 예상하고 처리 방법을 알고있는 예외 만 처리해야합니다.


2

어떤 예외 유형을 잡아야하는지, 어떻게 처리해야하는지 모른다면 catch 문을 사용할 필요가 없습니다. 상황에 대한 자세한 정보가있을 수있는 상위 발신자를 위해해야 ​​할 일을 알 수 있습니다.

예외가있는 경우에도 finally 문을 작성하여 해당 예외가 호출자에게 전달되기 전에 리소스를 정리할 수 있습니다.


2

가독성의 관점에서, 미래의 코드 리더들에게 "이것은 중요합니다. 무슨 일이 있어도 반드시 수행해야합니다." 이거 좋다

또한 빈 캐치 명령문은 특정 "냄새"를 갖는 경향이 있습니다. 개발자가 발생할 수있는 다양한 예외와 처리 방법을 생각하지 않는다는 신호일 수 있습니다.


2

마지막으로 선택 사항입니다. 정리할 리소스가없는 경우 "최종"블록을 가질 이유가 없습니다.


2

에서 가져온 : 여기

메소드의 성공적인 실행의 일부로 예외 발생 및 포착은 일상적으로 발생해서는 안됩니다. 클래스 라이브러리를 개발할 때 예외를 발생시킬 수있는 작업을 수행하기 전에 클라이언트 코드에 오류 조건을 테스트 할 기회가 주어져야합니다. 예를 들어, System.IO.FileStream은 다음 코드 조각과 같이 Read 메서드를 호출하기 전에 확인할 수있는 CanRead 속성을 제공하여 잠재적 예외가 발생하지 않도록합니다.

str As Stream = GetStream () If (str.CanRead) 그런 다음 '스트림을 읽도록 코딩합니다.

예외를 발생시킬 수있는 특정 방법을 호출하기 전에 객체의 상태를 확인할지 여부는 객체의 예상 상태에 따라 결정됩니다. 존재해야하는 파일 경로와 파일을 읽기 모드로 반환해야하는 생성자를 사용하여 FileStream 객체를 만드는 경우 CanRead 속성을 확인할 필요가 없습니다. FileStream을 읽을 수 없으면, 메소드 호출의 예상 동작을 위반하게되므로 예외가 발생해야합니다. 반대로, 메서드가 읽을 수 있거나 읽을 수없는 FileStream 참조를 반환하는 것으로 문서화 된 경우 데이터를 읽기 전에 CanRead 속성을 확인하는 것이 좋습니다.

"예외로 실행"코딩 기술이 발생할 수있는 성능 영향을 설명하기 위해, 캐스트가 실패하면 InvalidCastException을 발생시키는 캐스트의 성능이 C # as 연산자와 비교되어 캐스트가 실패하면 널을 리턴합니다. 두 기술의 성능은 캐스트가 유효한 경우 (테스트 8.05 참조)의 경우와 동일하지만 캐스트가 유효하지 않고 캐스트를 사용하는 경우 예외가 발생하는 경우 캐스트를 사용하는 것이 연산자로 (테스트 8.06 참조). 예외 발생 기술의 고성능 영향에는 예외 할당, 발생 및 포착 비용과 예외 개체의 후속 가비지 수집 비용이 포함됩니다. 즉, 예외 발생으로 인한 즉각적인 영향은 그다지 높지 않습니다. 더 많은 예외가 발생하면


2
스캇-위에 인용 한 텍스트가 expertexchange.com의 월페이퍼 뒤에 있다면 여기에 게시해서는 안됩니다. 나는 이것에 틀릴 수도 있지만 좋은 생각이 아닌 것 같습니다.
오노 리오 카테 나치


2

프로그래머가 C #을 읽으면 finally 블록은 응용 프로그램을 최적화하고 메모리 누수를 방지하도록 설계되었습니다.

CLR은 누수를 완전히 제거하지 않습니다 ... 프로그램이 실수로 원치 않는 객체에 대한 참조를 유지하면 메모리 누수가 발생할 수 있습니다.

예를 들어 파일 또는 데이터베이스 연결을 열면 머신은 해당 트랜잭션을 처리하기 위해 메모리를 할당하며, 처리 또는 닫기 명령이 실행되지 않으면 해당 메모리가 유지되지 않습니다. 그러나 트랜잭션 중에 오류가 발생한 경우 try.. finally..블록 안에 있지 않으면 진행 명령이 종료되지 않습니다 .

catchfinallycatch는 자체 오류를 처리 / 관리 또는 해석하는 방법을 제공하는 디자인이라는 점에서 다릅니다 . "이봐 내가 나쁜 놈들을 붙 잡았어, 내가 뭘 원하니?"라고 말하는 사람이라고 생각하십시오. 동안finally 반드시 자원이 적절하게 배치 된 것을 확인하도록 설계되었습니다. 나쁜 사람이 있는지 여부에 관계없이 당신의 재산이 여전히 안전하다는 것을 누군가가 생각하십시오.

그리고 그 두 사람이 함께 일할 수 있도록해야합니다.

예를 들면 다음과 같습니다.

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}

1

catch 문으로 호출 프로그램에 예외가 발생하더라도 마지막으로 리소스를 정리할 수 있습니다. 빈 catch 문을 포함하는 예제에서는 거의 차이가 없습니다. 그러나 캐치에서 일부 처리를 수행하고 오류를 발생 시키거나 전혀 캐치하지 않으면 최종적으로 여전히 실행됩니다.


1

우선, 처리하지 않으려는 예외를 잡는 것은 나쁜 습관입니다. .NET 응용 프로그램 성능 및 확장 성 향상 에서 .Net 성능에 대한 5 장을 확인하십시오 . 참고로, try 블록 내부에 스트림을로드해야합니다. 그러면 실패하면 관련 예외를 잡을 수 있습니다. try 블록 외부에 스트림을 작성하면 목적이 무효화됩니다.


0

여러 가지 이유 중에서 예외는 실행 속도가 매우 느립니다. 이런 일이 많이 발생하면 실행 시간을 쉽게 약화시킬 수 있습니다.


0

모든 예외를 포착하는 try / catch 블록의 문제점은 알 수없는 예외가 발생하면 프로그램이 현재 결정되지 않은 상태에 있다는 것입니다. 이는 빠른 실패 규칙과 완전히 일치합니다. 예외가 발생하더라도 프로그램이 계속되는 것을 원하지 않습니다. 위의 try / catch는 OutOfMemoryExceptions를 잡을 수도 있지만 이는 분명히 프로그램이 실행되지 않는 상태입니다.

try / finally 블록을 사용하면 여전히 빠르게 실패하면서 정리 코드를 실행할 수 있습니다. 대부분의 경우 전역 수준의 모든 예외 만 포착하여 예외를 기록한 다음 종료 할 수 있습니다.


0

예외가 발생하지 않는 한 예제 간의 효과적인 차이는 무시할 수 있습니다.

그러나 'try'절에있는 동안 예외가 발생하면 첫 번째 예는이를 완전히 삼킨다. 두 번째 예는 다음 단계에서 콜 스택을 올릴 때 예외를 발생 시키므로, 언급 된 예의 차이점은 하나는 예외 (첫 번째 예)를 완전히 가리고 다른 하나 (두 번째 예)는 나중에 처리 할 수 ​​있도록 예외 정보를 유지한다는 것입니다. 여전히 'finally'절의 내용을 실행합니다.

예를 들어 예외를 발생시킨 첫 번째 예의 'catch'절에 코드를 넣으면 (처음 발생한 예외 또는 새로운 예외) 판독기 정리 코드가 실행되지 않습니다. 마지막으로 'catch'절에서 일어나는 일에 관계없이 실행 됩니다.

따라서 'catch'와 'finally'의 주요 차이점은 '최종'블록의 내용 (예외는 거의 예외가 있음) 이 예기치 않은 예외가 발생하더라도 실행 되도록 보장 될 수 있다는 것 입니다. 'catch'조항 ( 'finally'조항 외부)은 그러한 보증을하지 않습니다.

또한 Stream과 StreamReader는 모두 IDisposable을 구현하며 'using'블록으로 래핑 될 수 있습니다. 'Using'블록은 try / finally ( 'catch'없음)와 의미 적으로 동일하므로 예제는 다음과 같이 더 간결하게 표현 될 수 있습니다.

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... StreamReader 인스턴스가 범위를 벗어나면 닫히고 처리됩니다. 도움이 되었기를 바랍니다.


0

{…} catch {} 시도가 항상 나쁜 것은 아닙니다. 일반적인 패턴은 아니지만 스레드 끝에서 열린 소켓을 닫는 것과 같이 무엇이든 관계없이 리소스를 종료해야 할 때 사용하는 경향이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.