빈 캐치 명령문을 갖는 것이 괜찮습니까?


58

나는 그것에 대해 생각했고 예를 내놓을 수 없었습니다. 누군가 예외를 잡아서 아무 일도하지 않는 이유는 무엇입니까? 예를 들어 줄 수 있습니까? 어쩌면 절대로해서는 안되는 것일 수도 있습니다.


마침내 어떻습니까?
Chris

2
btw-작년에 요청되었습니다 : stackoverflow.com/questions/1234343/…
warren

17
비어있는 이유 는 적어도 주석을 포함해야합니다 .
peterchen

답변:


77

나는 D에서 변환 오류와 같은 것들로 항상 그것을합니다.

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

즉 , 예외가 무시되는 이유 를 설명하는 주석 일지라도 모든 catch 블록에 무언가 가 있어야한다고 생각합니다 .

편집 : 또한 이와 같은 작업을 수행하려는 경우 무시하려는 특정 예외 만 잡아야한다는 것을 강조하고 싶습니다. 평범한 옛날을하는 catch {}것은 거의 항상 나쁜 일입니다.


15
설명 설명은 +1입니다.
Michael K

일부 IDE에서는 예외에 대한 특정 이름 (일반적으로 "무시")이 의도적으로 예외를 무시하고 있음을 나타내며, 그렇지 않으면 표시되는 경고를 억제합니다. 이것은 의견을 대신합니다.
Alex Feinman

나는 익숙하지 않은 D이지만 bool TryParse (string line, out double valToParse)와 같은 것을 수행하는 것이 가능하다고 가정합니다.
ysolik

4
와, 실제로 D를 사용하는 사람! - P
제임스 McNellis

4
@Michael 말한다처럼 - 그렇지 않아 확실히 당신이 거기에 주석을 가지고 있기 때문에 빈; 성명서가 없으면 "이 어획량을 의도적으로 비워 둔"설명을 추가해야합니다.
STW

50

아무것도 기록하지 않고 예외를 삼킬 수 있다고 생각하는 한 가지 예는 예외를 로깅하더라도 로깅 코드 자체입니다.

무언가를 기록하려고 시도했지만 예외가 발생하면 할 수있는 일이 많지 않습니다.

  • 물론 기록 할 수는 없습니다.
  • 예약 로깅 메커니즘으로 대체 할 수 있지만 대부분의 응용 프로그램은 그렇게 정교하지 않으며, 충분히 정교한 응용 프로그램의 경우 예약 로깅 메커니즘에서 동일한 디자인 문제가 발생합니다.
  • 빠른 실패-대부분의 응용 프로그램에는 너무 급진적 인 해결책이 될 것입니다.
  • 예외를 던지면 거의 특정 로그를 기록하려고 시도하는 catch 문으로 끝나기 때문에 거의 효과가 없습니다 ...

그러면 응용 프로그램이 주요 작업을 계속 수행 할 수 있지만 문제 해결 기능이 손상 될 수있는 "최악의 악의"옵션을 사용하여 조용히 삼킬 수 있습니다.


10
좋은 지적입니다. 디버깅 코드를 디버깅하는 것은 항상 어려운 일입니다.
DevSolo

7
이것은 백만 번입니다. 특히 로깅하는 경우 끔찍한 상태 일 수 있습니다. 당신은 메모리 부족, 충돌, 무엇이든 될 수 있습니다. 이 시점에서 오류를 기록 할 때 실패하면 할 일은 책임이 없습니다. 시도하는 것이 더 나 빠지지 않을 것이라는 보장은 없습니다.
GWLlosa

좋은 지적입니다. 로깅 코드에서 예외에 대한 InnerMessage를 추가하고 싶습니다. 여러 번 이것이 null이므로 추가하려고하면 로깅 자체가 손상됩니다.
Tangurena

@Tangurena 종종 null 인 경우 처리하지 마십시오. C #에서 나는 종종 그런 것들을 지키고 ?? "";
Loren Pechtel

8

어쨌든 선택적인 작업으로 인해 예외가 발생하면 수행 할 작업이 없습니다. 기록하는 것이 유용하지 않을 수 있습니다. 이 경우에는 그냥 잡고 아무 것도하지 않을 수 있습니다.

이 작업을 수행하는 경우 의견을 포함하십시오. 항상 버그 (위해 fallthrough A의 모양 아무것도 언급 switch문, 빈 catch상태에서 블록 할당).

이것이 예외를 올바르게 사용하는 것이 아니라 다른 질문을위한 것이라고 주장 할 수 있습니다.


6

catch블록은 대부분의 언어에서 코드 냄새입니다. 주요 아이디어는 예외 상황에 예외를 사용하고 논리 제어에 예외를 사용하지 않는 것입니다. 모든 예외는 어딘가에서 처리해야합니다.

이 방법을 사용하면 하나의 특정 응용 프로그램 계층을 프로그래밍 할 때 몇 가지 선택 사항이 있습니다.

  • 예외에 대해서는 아무 것도하지 마십시오. 다른 레이어에서 잡히고 처리합니다.
  • 잡아서 시정 조치를 수행하십시오.
  • 잡아서 뭔가를했지만 다른 레이어가 처리 할 수 ​​있도록 다시 던지십시오.

이것은 아무것도하지 않고 빈 catch블록을 위한 공간을 남기지 않습니다 .

추가 편집 : 예외를 던지는 것이 프로그램 로직을 제어하는 ​​일반적인 방법 인 언어로 프로그래밍한다고 가정합니다 (의 대안 중 하나 goto). 그런 다음 catch이 언어로 작성된 프로그램의 빈 else블록은 전통적인 언어 의 빈 블록 과 매우 비슷 합니다 (또는 전혀 else블록이 없음 ). 그러나 이것이 C #, Java 또는 C ++ 개발 커뮤니티에서 권장하는 프로그래밍 스타일이 아니라고 생각합니다.


논리 제어로 예외를 사용하는 것이 상당히 일반적이라고 생각하므로 비어 있거나 거의 비어있는 캐치 블록이 자주 있습니다.
whatsisname

빈 캐치 블록 플로우 제어에 예외 사용을 표시 할 수 있음 을 인식하기 위해 +1 .
John M Gant

프로그래밍 로직에 예외를 사용하는 것은 일반적으로 나쁜 습관으로 간주됩니다. 그러나 놀랍게도 (예외에 대한 나의 원래 주장) 예외는 눈에 띄는 성능 저하를 일으키지 않습니다. codeproject.com/KB/exception/ExceptionPerformance.aspx를 참조하십시오 .
Evan Plaice

6

어떤 경우에는 Java가 어떤 상황에서도 절대 발생할 수없는 예외를 처리해야합니다. 이 코드를 고려하십시오.

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

다음과 같은 표준 문자 집합을 지원하려면 모든 Java 플랫폼 구현이 필요합니다.

...

UTF-8

Java 플랫폼을 중단하지 않으면이 예외를 발생시킬 수있는 방법이 없습니다. 그래서 왜 처리를 귀찮게합니까? 그러나 try-catch-clause를 생략 할 수는 없습니다.


1
이와 같은 경우에는 내가 throw new Error(e)아무것도 놓치지 않았다는 것을 확신하기 위해 추가하고 싶은 유혹이 있습니다 (또는 실제로 깨진 플랫폼을보고합니다).
할리 크나 스트

@HalleKnast 네. 이에 대해서는 여기에서 자세히 설명되어 있습니다 : softwareengineering.stackexchange.com/questions/122233/…
user281377

5

일반적으로 아닙니다. 스택에 예외를 던지거나 발생한 일을 기록하려고합니다.

그러나 문자열을 구문 분석하고 숫자로 변환 할 때 (특히 한 번만 사용하고 다양성을 버리는 프로젝트를 매핑 할 때) 종종이 작업을 수행합니다.

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;

3

어떤 경우에는 예외가 전혀 예외가 아닙니다. 실제로 예상됩니다. 이 예제를 고려하십시오.

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

java.lang.Process ()에는 isAlive () 메소드가 없기 때문에 아직 살아 있는지 확인하는 유일한 방법은 exitValue ()를 호출하는 것입니다. 프로세스가 여전히 실행 중이면 IllegalThreadStateException이 발생합니다.


2

내 겸손한 경험에서이 좋은 일이 거의 없습니다. 마일리지는 다를 수 있습니다.

코드베이스에서 이것을 볼 때마다 거의 예외가 숨겨져있는 버그를 추적했기 때문입니다. 달리 말하면, 코드를 제공하지 않음으로써 애플리케이션은 오류를 표시하거나 다른 조치를 수행 할 방법이 없습니다.

예를 들어 API 계층에서 예외를 지나치게하거나 원하지 않을 수 있으므로이 경우 가장 일반적인 것을 시도한 다음 기록하는 경향이 있습니다. 다시 한번, 적어도 디버그 / 진단 세션에 기회를주기 위해.

일반적으로 비어 있어야하는 경우 최소한 내가 일종의 어설 션을 작성하므로 디버그 세션 중에 최소한 무언가가 발생합니다. 즉, 처리 할 수 ​​없으면 완전히 생략하는 경향이 있습니다.


2

IMO는 빈 catch 문이 괜찮은 곳입니다. 예외가 예상되는 테스트 사례 :

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}

+1, 나는 이것을하는 것을 싫어하지만 JUnit에서 작동합니다.
sal

@sal : @Test (expected = Exception.class)는 어떻습니까?
ysolik

@ ysolik, 나쁜 습관

1
@sal : 왜 나쁜 습관입니까?
Adam Lear

음. 단위 테스트 프레임 워크를 사용하여이를 수행 할 수있는 방법이 있습니다. 전의. NUnit. 예상치 못한 테스트가 도움이 될 수있는 특정 사례가 있습니다. 그러나 프로덕션 코드에서는 절대 그렇게하지 않습니다.
Evan Plaice 1

2

빈 catch 절이있는 것이 정당한 경우가 있다고 생각합니다. 특정 작업이 실패 할 때 코드가 계속 진행되도록하려는 경우입니다. 그러나이 패턴을 쉽게 과도하게 사용할 수 있다고 생각하므로 더 나은 처리 방법이 없는지 각 사용을 면밀히 조사해야합니다. 특히, 프로그램이 일관성이없는 상태로 두거나 나중에 코드의 다른 부분이 실패 할 수있는 경우에는 수행하지 않아야합니다.


1

예외가 발생했을 때 어느 정도의 경고가 표시되지 않는다고 생각하지 않습니다. 프로그래밍 목표 중 하나가 코드를 개선하지 않아야합니까? 예외가 10 %의 시간에 발생하고이를 알지 못하면 예외는 항상 그렇게 나쁘거나 나빠질 수 있습니다.

그러나 코드 처리에서 예외가 실제로 해를 끼치 지 않으면 사용자를 코드에 노출시키는 이유는 다른 쪽입니다. 내가 일반적으로 구현하는 솔루션은 내 로거 클래스 (직장에서 쓰는 모든 단일 클래스에 있음)에 의해 로깅되는 것입니다.

양성 예외 인 경우 Logger의 .Debug () 메서드를 호출합니다. 그렇지 않으면 Logger.Error () (및 (아마도) throw).

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

그건 그렇고, 내 로거 구현에서 .Debug () 메서드를 호출하면 App.Config 파일에서 프로그램 실행 로그 수준이 디버그 모드임을 지정하는 경우에만 기록합니다.


_log.Debug에서 예외가 발생하면 어떻게됩니까?
JohnL

그런 다음 .Debug ()는 예외를 먹고 결코 알지 못합니다. 그러나 종종 예외를 던지는 코드에 대해 생각하는 것보다 내 로거에 대해 훨씬 자신감이 있습니다. 내가 그것에 대해 염려한다면, catch 블록에 들어가는 것을 감시하고 나중에 저장하는 일종의 패턴을 구현할 수 있다고 가정합니다. 어쨌든-점 점
Tim Claason

1

존재 확인은 좋은 사용 사례입니다.

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

또 다른 경우는 finally절이 사용될 때 입니다.

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

ECMAScript에서 이와 유사한 사용 사례와 관련된 catch 바인딩생략 할 수있는 제안 이 있습니다.

참고 문헌


또 다른 예 : nodejs에서 fs.exists 메소드 (true 또는 false를 반환)는 fs.access 를 선호하여 더 이상 사용되지 않습니다 ( nodejs.org/dist/latest-v10.x/docs/api/… ). 파일이 존재하지 않는 경우 파일이 존재하는 경우에만 무언가를 원한다면 catch 절은 완전히 불필요합니다.
systemovich

0

내가 실제로 이와 같은 일을해야 할 유일한 시간은 콘솔 응용 프로그램의 로깅 클래스였습니다. STDOUT에 오류를 발생시키고 파일에 오류를 기록한 다음> 0 오류 코드로 앱을 닫은 최상위 전역 catch 처리기가있었습니다.

여기서 문제는 때때로 파일에 대한 로깅에 실패했다는 것입니다. 디렉토리 권한으로 인해 파일 쓰기가 중단되었으며 파일이 독점적으로 잠겼습니다. 이 경우 예외 세부 정보를 기록하면 로거가 동일한 작업을 수행했기 때문에 다른 곳에서와 같은 방식으로 예외를 처리 할 수 ​​없었습니다 (캐치, 관련 세부 정보 기록, 더 나은 예외 유형으로 랩 등). 이미 실패한 파일에 쓰려고합니다. 그들이 다시 실패하면 ... 내가가는 곳을 봅니다. 무한 루프로 들어갑니다.

try {} 블록 중 일부를 삭제하면 메시지 상자에 예외가 표시 될 수 있습니다 (자동 구성 앱에서는 원하는 것이 아님). 따라서 로그 파일에 쓰는 방법에는 빈 캐치 핸들러가 있습니다. 이것은 여전히 ​​0보다 큰 오류 코드를 얻으므로 오류를 묻지 않으며 무한 루프를 중지하고 앱이 정상적으로 종료되도록합니다.

물론 비어있는 이유를 설명하는 주석이 있습니다.

(이것은 이전에 게시하려고했지만 망각에 타오르는 것을 두려워했습니다. 비록 내가 유일한 사람은 아니기 때문에 ...)


0

가장 중요한 부분이 무엇이든 끝에 순서에 관계없이 일부 시퀀스를 실행 해야하는 상황에서는 괜찮습니다.

예를 들어. 호스트와 컨트롤러의 모션 제어 통신. 비상 상황에서는 이동을 중단하고, 궤도 생성을 중지하고, 모터를 감속 시키며, 브레이크를 활성화하고, 앰프를 비활성화하기위한 준비 단계가 많이 있습니다.이 후 버스 전원을 차단해야합니다. 모든 단계는 순차적으로 이루어져야하며 단계 중 하나가 실패하면 나머지 단계는 하드웨어 손상의 위험이 허용되는 경우에도 실행되어야합니다. 위의 모든 사항이 실패하더라도 킬 버스 전원은 어쨌든 발생해야합니다.


그것은 당신이 아무것도하지 않는다는 것을 의미하지 않으며, 당신이하는 일이 흐름을 중단시키지 않는다는 것을 의미합니다. 예외 를 잡아서 메모리에 저장 (디스크 쓰기 지연 없음) 한 다음 finally 문에서 전원을 차단하십시오. 그런 다음 저장된 정보로 원하는 작업을 수행하십시오.
Loren Pechtel

0

오류로부터 복구하기 위해 시스템 리소스를 정상적으로 종료

예를 들어. 당신이 사용자에게 피드백을 제공하지 않는 백그라운드에서 서비스를 실행하는 경우 사용자에게 오류의 표시를 밀어 싶지 않을 수도 있지만 않는 우아한 방식으로 사용되는 리소스를 닫아야합니다.

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

이상적으로는 디버그 모드에서 catch를 사용하여 오류를 잡아 테스트를 위해 콘솔이나 로그 파일에 인쇄합니다. 프로덕션 환경에서 백그라운드 서비스는 일반적으로 오류가 발생할 때 사용자를 방해하지 않으므로 오류 출력을 억제해야합니다.

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