try-finally (캐치없이) vs 열거 상태 유효성 검사 사용


9

나는 예외가 가능한 곳에서 최대한 가깝게 처리되는 방법 에 대한 이 질문 에 대한 조언을 읽었 습니다.

모범 사례에 대한 나의 딜레마는 try / catch / finally를 사용하여 열거 형 (또는 값을 나타내는 int, 오류의 경우 0, 오류의 경우 1, 확인의 경우 2, 경고에 대한 2 등) 을 반환 해야하는지 여부 입니다. 응답은 항상 순서대로되어 있습니까? 아니면 호출 부분이 처리 할 수 ​​있도록 예외를 통과시켜야합니까?

내가 수집 할 수있는 것에서 이것은 사례에 따라 다를 수 있으므로 원래의 조언은 이상하게 보입니다.

예를 들어, 웹 서비스에서는 항상 상태를 반환하고 싶기 때문에 모든 예외를 즉시 처리해야하지만 http를 통해 일부 데이터를 게시 / 가져 오는 함수 내부에서는 말할 수 있습니다. 예외 (예 : 404의 경우)는 예외가 발생한 예외를 통과합니다. 그렇지 않은 경우 호출 자체에 결과의 품질 (오류 : 404) 및 결과 자체를 알리는 방법을 만들어야합니다.

데이터를 가져 오거나 게시하는 도우미 함수 내에서 404 예외를 시도하는 것이 가능하지만, 그래야합니까? smallint를 사용하여 프로그램의 상태를 표시하고 (물론 적절하게 문서화),이 정보를 온전한 유효성 검사 목적 (모든 ok / 오류 처리)으로 사용하는 것만입니까?

업데이트 : 주요 분류에 치명적 / 치명적이지 않은 예외가 예상되었지만 대답을 방해하지 않기 위해 이것을 포함하고 싶지 않았습니다. 질문이 무엇인지 명확히하겠습니다. 예외를 던지지 않고 예외를 처리하십시오. 원하는 효과 : 오류를 감지하고 복구하십시오. 복구가 불가능한 경우 가장 의미있는 피드백을 제공하십시오.

다시 http get / post 예제를 사용하면 원래 발신자에게 발생한 일을 설명하는 새 객체를 제공해야합니까? 이 헬퍼가 사용중인 라이브러리에있는 경우 조작에 대한 상태 코드를 제공하거나 try-catch 블록에 포함 시키겠습니까? 이를 설계 하는 경우 상태 코드를 제공하거나 예외를 던져서 상위 레벨에서 상태 코드 / 메시지로 대신 변환하도록 하시겠습니까?

개요 : 예외를 생성하는 대신 코드 조각이 생성 될 수있는 결과와 함께 상태 코드를 반환하는 경우 어떻게 선택 했습니까?


1
사용중인 오류 처리 형식을 변경하는 오류를 처리하지 않습니다. 404의 경우 처리 할 수 ​​없기 때문에 통과시킬 수 있습니다.
stonemetal

"값을 나타내는 int, 오류의 경우 0, 확인의 경우 1, 경고의 경우 2" OK를 의미하기 위해 0을 사용하는 것은 확실히 표준입니다 ...
anaximander

답변:


15

예외적 인 조건에는 예외를 사용해야합니다. 예외를 던지는 것은 기본적으로 "여기서이 조건을 처리 할 수 ​​없습니다. 콜 스택에서 더 높은 누군가가 이것을 잡아서 처리 할 수 ​​있습니까?"라고 진술합니다.

호출자가 해당 값을 가져 와서 의미있는 작업을 수행 할 것이 분명한 경우 값을 반환하는 것이 좋습니다. 이것은 예외를 던질 때 성능에 영향을 미치는 경우에 특히 그렇습니다. 예외를 던지면 값을 반환하는 것 (최소 2 배 이상)보다 훨씬 오래 걸립니다.

프로그램 로직을 구현하는 데 예외를 사용해서는 안됩니다. 다시 말해, 무언가를하기 위해 예외를 던지지 마십시오. 할 수 없다는 예외를 던지십시오.


답장을 보내 주셔서 감사합니다. 가장 유익하지만 제 예외는 예외 처리가 아니라 예외 처리에 있습니다. 404 예외를 수신하자마자 잡아야합니까, 아니면 스택 위로 올라 가야합니까?
Mihalis Bagos

편집 내용이 표시되지만 답변이 변경되지 않습니다. 호출자에게 의미있는 예외를 오류 코드로 변환하십시오. 그렇지 않은 경우 예외를 처리하십시오. 스택 위로 올리십시오. 그것을 잡거나 그것을 로그에 쓰는 것과 같이 무언가를하거나 다시 던지십시오.
Robert Harvey

1
+1 : 합리적이고 균형 잡힌 설명. 예외적 인 경우를 처리하기 위해 예외를 사용해야합니다. 그렇지 않으면 함수 또는 메서드가 의미있는 값 (열 또는 옵션 유형)을 반환해야하며 호출자는이 값을 올바르게 처리해야합니다. 아마도 함수형 프로그래밍, 즉 연속에서 널리 사용되는 세 번째 대안을 언급 할 수 있습니다.
조르지오

4

내가 한 번 읽은 좋은 충고는 다루고있는 데이터의 상태가 주어지면 진행할 수 없을 때 예외를 던지지 만 예외를 던질 수있는 방법이 있다면 가능한 경우 데이터가 실제로 있는지 여부를 주장하는 방법을 제공하는 것입니다 메소드가 호출되기 전에 유효합니다.

예를 들어, 제공된 파일이 존재하지 않으면 System.IO.File.OpenRead ()는 FileNotFoundException을 throw하지만, 파일이 존재하는지 여부를 나타내는 부울 값을 반환하는 .Exists () 메서드도 제공합니다. 예기치 않은 예외를 피하기 위해 OpenRead () 호출

질문의 "예외를 처리해야 할 때"부분에 대답하기 위해 실제로 어디에서 할 수 있는지 말할 것입니다. 메소드가 호출 한 메소드에 의해 발생 된 예외를 처리 할 수없는 경우이를 포착하지 마십시오. 콜 체인을 처리 할 수있는 수준으로 높이십시오. 경우에 따라 Application.UnhandledException을 듣는 로거 일 수도 있습니다.


많은 사람들, 특히 파이썬 프로그래머들은 EAFP를 선호합니다. "허가를받는 것보다 용서를 구하는 것이 더 쉽습니다"
Mark Booth

1
.Exists ()와 같이 예외를 피하는 것에 대한 설명은 +1입니다.
codingoutloud 2019

+1 이것은 여전히 ​​좋은 조언입니다. 필자가 작성 / 관리하는 코드에서 예외는 "예외적"이며, 개발자가 볼 수있는 예외의 9/10 배는 개발자가 방어해야합니다. 다른 1 회는 처리 할 수없는 것이므로 기록한 후 최대한 종료합니다. 예를 들어 컨벤션은 중요합니다. 예를 들어, 컨벤션은 일반적으로 실제 오답과 표준 요금 / 처리에 대한 내부 메시지를 갖습니다. 모든 것에 대한 예외를 던지는 것처럼 보이는 타사 API는 전화로 처리 할 수 ​​있으며 표준 합의 된 프로세스를 사용하여 반환 할 수 있습니다.
Gavin Howden

3

try / catch / finally를 사용하여 열거 형 (또는 값을 나타내는 int, 오류의 경우 0, 오류의 경우 1, 확인의 경우 2, 경고 등에 대한 2 등)을 반환하여 응답이 항상 순서대로 이루어 지도록하십시오.

끔찍한 디자인입니다. 숫자 코드로 변환하여 예외를 "마스크"하지 마십시오. 적절하고 명확한 예외로 두십시오.

또는 호출 부분이 처리 할 수 ​​있도록 예외를 통과시켜야합니까?

이것이 예외입니다.

가능한 곳에서 발생하는 곳에서 처리

보편적 인 진실 이 아닙니다 . 좋은 생각입니다. 다른 경우에는 도움이되지 않습니다.


끔찍한 디자인이 아닙니다. Microsoft는 여러 곳, 즉 기본 asp.NET 멤버 자격 공급자에서이를 구현합니다. 이 대답은 대화에 무엇을 기여하는 방법을 내가 볼 수없는 그 외에는
Mihalis Bagos을

@MihalisBagos : 내가 할 수있는 것은 모든 프로그래밍 언어에 Microsoft의 접근 방식이 포함되어 있지 않다는 것입니다. 예외가없는 언어에서는 값을 반환해야합니다. C가 가장 주목할만한 예입니다. 예외가있는 언어에서 오류를 나타 내기 위해 "코드 값"을 반환하는 것은 끔찍한 디자인입니다. if더 간단한 예외 처리 대신 (때로는) 번거로운 문으로 이어집니다 . 대답 ( "선택 방법 ...")은 간단합니다. 하지 마십시오 . 이것이 대화에 도움이된다고 생각합니다. 그러나이 답변은 무시해도됩니다.
S.Lott

나는 당신의 의견이 중요하지 않다고 말하고 있지만 당신의 의견은 발전되지 않았다고 말하고 있습니다. 그 의견으로, 우리가 예외를 사용할 수있는 곳, 우리가 할 수 있기 때문에해야한다고 추론합니다. 차라리 코드 흐름 케이스의 분류 / 조직보다, 마스크로 상태 코드를 볼 수 없습니다
Mihalis Bagos

1
예외는 이미 분류 / 조직입니다. 명확하지 않은 예외를 "정상"또는 "예외되지 않은"반환 값과 쉽게 혼동 될 수있는 반환 값으로 대체하는 값을 볼 수 없습니다. 반환 값은 항상 예외가 아니 어야합니다 . 제 의견은 그다지 발전되지 않았습니다. 매우 간단합니다. 예외를 숫자 코드로 변환하지 마십시오. 그것은 이미 우리가 상상할 수있는 완벽하게 좋은 사용 사례를 모두 제공하는 완벽하게 좋은 데이터 객체였습니다.
S.Lott

3

S.Lott에 동의합니다 . 가능한 한 소스에 가깝게 예외를 잡아내는 것은 상황에 따라 좋은 생각이거나 나쁜 생각 일 수 있습니다. 예외를 처리하는 열쇠는 무언가를 할 수있을 때만 예외를 잡아내는 것입니다. 그것들을 잡아서 호출 함수에 숫자 값을 반환하는 것은 일반적으로 나쁜 디자인입니다. 당신은 당신이 복구 할 수있을 때까지 떠 다니게하고 싶습니다.

나는 항상 예외 처리가 내 응용 프로그램 논리에서 한 걸음 떨어진 것으로 간주합니다. 이 예외가 발생하면 응용 프로그램이 복구 가능한 상태가되기 전에 호출 스택을 얼마나 멀리 백업해야합니까? 많은 경우에 응용 프로그램 내에서 복구 할 수있는 일이 없다면 최상위 수준까지 잡아서 예외를 기록하고 작업을 실패하고 깨끗하게 종료하려고 시도 할 수 있습니다.

예외 처리를 언제 언제 어떻게 설정해야하는지에 대한 엄격한 규칙은 없습니다.


1

예외는 아름다운 것입니다. 불필요한 모호성에 의존하지 않고 런타임 문제에 대한 명확한 설명을 생성 할 수 있습니다. 예외는 유형, 하위 유형 및 유형별로 처리 될 수 있습니다. 다른 곳에서 처리하기 위해 전달 될 수 있으며, 처리 할 수없는 경우 응용 프로그램의 상위 계층에서 처리되도록 다시 올릴 수 있습니다. 또한 난독 화 된 오류 코드를 처리하기 위해 많은 미친 논리를 호출하지 않고도 메소드에서 자동으로 반환됩니다.

코드에서 유효하지 않은 데이터가 발생하면 즉시 예외를 처리해야합니다. throw 될 수있는 예외를 처리하기 위해 try..catch..finally에서 다른 메소드에 대한 호출을 랩핑하고 주어진 예외에 응답하는 방법을 모르는 경우 상위 계층에 표시하기 위해 다시 발생시킵니다. 다른 곳에서 처리해야 할 문제가 있습니다.

오류 코드 관리가 매우 어려울 수 있습니다. 일반적으로 코드에 불필요한 중복이 많이 발생하고 오류 상태를 처리하기위한 복잡한 논리가 생깁니다. 코드 자체가 의미가없고 사용자에게 오류에 대한 컨텍스트를 제공하지 않기 때문에 사용자가되고 오류 코드가 발생하는 것은 훨씬 더 나쁩니다. 반면 예외는 "값을 입력하지 않았습니다"또는 "유효하지 않은 값을 입력했습니다. 여기에 사용할 수있는 유효 범위가 있습니다 ..."또는 "하지 않습니다. 무슨 일이 있었는지 알고 기술 지원에 문의하여 방금 충돌했다고 말하고 다음과 같은 스택 추적을 제공하십시오 ... "

OP에 대한 나의 질문은 지구상에서 오류 코드를 반환하는 것보다 예외를 사용하고 싶지 않은 이유는 무엇 입니까?


0

모든 좋은 답변. 또한 예외를 던지는 대신 오류 코드를 반환하면 호출자의 코드가 더 복잡해질 수 있다고 덧붙이고 싶습니다. 메소드 A가 메소드 B를 호출하여 메소드 C를 호출하고 C에 오류가 발생한다고 가정하십시오. C가 오류 코드를 반환하면 B는 해당 오류 코드를 처리 할 수 ​​있는지 여부를 결정하는 논리를 가져야합니다. 그렇지 않으면 A로 되돌려 야합니다. A가 오류를 처리 할 수 ​​없으면 어떻게해야합니까? 예외를 던져? 이 예에서 C가 단순히 예외를 던지고 B가 예외를 포착하지 않으면 코드가 훨씬 깨끗해 지므로 추가 코드없이 자동으로 중단되며 A는 특정 유형의 예외를 포착하면서 다른 사람이 계속 전화를 걸 수 있습니다. 스택.

다음과 같은 방법으로 코딩하는 것이 좋습니다.

코드 줄은 황금 총알과 같습니다. 가능한 한 적게 사용하여 작업을 완료하려고합니다.


0

두 솔루션의 조합을 사용했습니다. 각 유효성 검사 기능마다 유효성 검사 상태 (오류 코드)로 채워진 레코드를 전달합니다. 함수가 끝나면 유효성 검사 오류가 발생하면 예외가 발생합니다.이 방법으로 각 필드에 대해 예외를 throw하지 않고 한 번만 예외를 throw합니다.

또한 데이터가 유효하지 않을 때 실행을 계속하고 싶지 않기 때문에 예외를 던지면 실행이 중지된다는 이점을 얻었습니다.

예를 들어

procedure Validate(var R:TValidationRecord);
begin
  if Field1 is not valid then
  begin
    R.Field1ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end; 
  if Field2 is not valid then
  begin
    R.Field2ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;
  if Field3 is not valid then
  begin
    R.Field3ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;

  if ErrorFlag then
    ThrowException
end;

부울에만 의존하는 경우 내 기능을 사용하는 개발자는 이것을 서면으로 고려해야합니다.

if not Validate() then
  DoNotContinue();

그러나 그는 잊고 전화 만 할 수 있습니다 Validate()(나는 그가해서는 안된다는 것을 알고 있지만 어쩌면 그는 할 수도 있음을 알고 있습니다).

위 코드에서 두 가지 장점을 얻었습니다.

  1. 유효성 검사 기능에는 하나의 예외 만 있습니다.
  2. 캐치되지 않은 예외도 실행을 중지하고 테스트 시간에 나타납니다.

0

여기에 하나의 대답이 없습니다-일종의 HttpException이없는 것과 같습니다.

기본 HTTP 라이브러리가 4xx 또는 5xx 응답을 받으면 예외가 발생한다는 것은 많은 의미가 있습니다. 지난번에 나는 HTTP 사양을 살펴 보았습니다.

그 예외를 던지거나 감싸고 다시 던지는 것에 대해서는 실제로 유스 케이스의 문제라고 생각합니다. 예를 들어, API에서 일부 데이터를 가져 와서 애플리케이션에 노출하기 위해 랩퍼를 작성하는 경우 HTTP 404를 리턴하는 존재하지 않는 자원에 대한 요청이 의미 적으로이를 파악하여 널을 리턴하는 것으로 판단 할 수 있습니다. 반면에 406 오류 (허용되지 않음)는 오류가 발생했을 수 있습니다. 즉, 무언가가 변경되어 앱이 충돌하여 타거나 비명을 지르는 것을 의미합니다.

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