예외 또는 오류 코드에 대한 규칙


118

어제 저는 선호하는 오류보고 방법에 대해 동료와 열띤 토론을 벌였습니다. 주로 우리는 응용 프로그램 계층 또는 모듈 간의 오류보고를위한 예외 또는 오류 코드의 사용에 대해 논의했습니다.

오류보고를 위해 예외를 발생 시키거나 오류 코드를 반환할지 여부를 결정하기 위해 어떤 규칙을 사용합니까?

답변:


81

높은 수준의 항목에서는 예외입니다. 낮은 수준의 오류 코드에서.

예외의 기본 동작은 스택을 풀고 프로그램을 중지하는 것입니다. 스크립트를 작성하고 사전에없는 키를 찾으면 오류 일 수 있으며 프로그램을 중지하고 그것에 대해 모두 알고 있습니다.

그러나 가능한 모든 상황에서 동작을 알아야 하는 코드를 작성하고 있다면 오류 코드를 원합니다. 그렇지 않으면 내 함수의 모든 줄에서 발생할 수있는 모든 예외를 알아야합니다 (이게 얼마나 까다로운 지 알아 보려면 항공사접지 한 예외를 읽어보십시오 ). 모든 상황 (불행한 상황 포함)에 적절하게 반응하는 코드를 작성하는 것은 지루하고 어렵지만, 오류 코드를 전달하기 때문이 아니라 오류없는 코드를 작성하는 것이 지루하고 어렵 기 때문입니다.

모두 레이몬드 첸 조엘은 모든 것에 예외를 사용에 대한 몇 가지 설득력있는 주장을 만들었습니다.


5
+1은 컨텍스트가 오류 처리 전략과 관련이 있음을 나타냅니다.
alx9r 2012-08-28

2
@Tom, 좋은 지적이지만 예외는 잡힐 것이 보장됩니다. 오류 코드가 포착 되고 실수로 인해 조용히 무시되지 않도록하려면 어떻게해야 합니까?
Pacerier 2014-07-25

13
따라서 예외에 대한 유일한 주장은 예외를 포착하는 것을 잊었을 때 나쁜 일이 발생할 수 있지만 오류에 대해 반환 된 값을 확인하는 것을 잊는 상황을 고려하지 않는다는 것입니다. 예외를 포착하는 것을 잊었을 때 스택 추적을 얻는 것은 말할 것도없고 오류 코드를 확인하는 것을 잊었을 때 아무것도 얻지 못하는 동안에 스택 추적을 얻습니다.
Esailija

3
C ++ 17 nodiscard은 함수의 반환 값이 저장되지 않은 경우 컴파일러 경고를 제공하는 속성을 도입합니다 . 잊혀진 오류 코드 검사를 포착하는 데 도움이됩니다. 예 : godbolt.org/g/6i6E0B
Zitrax

5
@Esailija의 의견은 정확히 여기에 있습니다. 여기서 예외에 대한 주장은 모든 오류 코드가 문서화되어있는 가상의 오류 코드 기반 API와 문서를 읽고 애플리케이션에서 논리적으로 가능한 모든 오류 사례를 올바르게 식별하고 각각을 처리하는 코드를 작성하는 가상의 프로그래머를 사용합니다. , 그런 다음 해당 시나리오를 가상의 예외 기반 API 및 프로그래머와 비교하여 어떤 이유로 이러한 단계 중 하나가 잘못 되는 경우 예외 기반 API에서 모든 단계를 올바르게 가져 오는 것이 똑같이 쉬울지라도 (논란의 여지없이 더 쉽습니다 ).
Mark Amery 2011

62

나는 일반적으로 예외를 선호한다. 왜냐하면 그것들은 더 많은 문맥 정보를 가지고 있고 프로그래머에게 더 명확한 방식으로 오류를 전달할 수 있기 때문이다.

반면에 오류 코드는 예외보다 가볍지 만 유지하기가 더 어렵습니다. 오류 검사는 실수로 생략 될 수 있습니다. 오류 코드는 모든 오류 코드가 포함 된 카탈로그를 유지 한 다음 결과를 켜서 어떤 오류가 발생했는지 확인해야하므로 유지 관리하기가 더 어렵습니다. 여기에서 오류 범위가 도움이 될 수 있습니다. 왜냐하면 우리가 관심있는 유일한 것이 오류가 있는지 여부뿐이라면 확인하는 것이 더 간단하기 때문입니다 (예 : 0보다 크거나 같은 HRESULT 오류 코드는 성공이고 0보다 작 으면 실패). 개발자가 오류 코드를 확인하는 프로그래밍 강제가 없기 때문에 실수로 생략 될 수 있습니다. 반면에 예외는 무시할 수 없습니다.

요약하면 거의 모든 상황에서 오류 코드보다 예외를 선호합니다.


4
"오류 코드는 예외보다 가볍습니다"는 측정 대상과 측정 방법에 따라 다릅니다. 예외 기반 API가 훨씬 빠를 수 있음을 보여주는 테스트를 만드는 것은 매우 쉽습니다.
Mooing Duck 2014 년

1
@smink, 좋은 지적이지만 예외의 오버 헤드를 어떻게 해결합니까? 오류 코드는 가벼운 것이 아니라 기본적으로 무중력입니다 . 예외는 단순히 중간 정도의 무게가 아니라 스택 정보와 우리가 어쨌든 사용하지 않는 기타 항목을 포함하는 무거운 개체입니다.
Pacerier 2014

3
@Pacerier : 예외가 발생하는 테스트 만 고려하고 있습니다. C ++ 예외를 던지는 것이 오류 코드를 반환하는 것보다 훨씬 느리다는 것은 100 % 맞습니다. 손 내려 놓고 토론하지 마세요. 우리 다른 점은 코드의 다른 99.999 %입니다. 예외를 제외하고는 각 문 사이에 오류 코드 반환을 확인할 필요가 없으므로 해당 코드가 컴파일러에 따라 약 1-50 % 더 빨라집니다. 즉, 코드 작성 방법과 예외 발생 빈도에 따라 전체 코드가 더 빠르거나 느려질 수 있습니다.
Mooing Duck 2014-07-25

1
@Pacerier : 방금 작성한이 인공 테스트를 통해 예외 기반 코드는 GCC는 아니지만 MSVC 및 Clang의 오류 코드만큼 빠릅니다 : coliru.stacked-crooked.com/a/e81694e5c508945b (하단 타이밍). 내가 사용한 GCC 플래그는 다른 컴파일러에 비해 예외적으로 느린 예외를 생성 한 것으로 보입니다. 편견이 있으므로 내 테스트를 비판하고 다른 변형을 시도해보세요.
Mooing Duck 2014

4
@Mooing Duck, 오류 코드와 예외를 동시에 테스트했다면 예외를 활성화해야합니다. 이 경우 결과는 예외 처리 오버 헤드가있는 오류 코드를 사용하는 것이 예외 만 사용하는 것보다 느리지 않음을 나타냅니다. 의미있는 결과를 얻으려면 예외를 비활성화 한 상태에서 오류 코드 테스트를 실행해야합니다.
Mika Haarahiltunen 2014 년

24

나는 예외를 선호한다.

  • 논리의 흐름을 방해한다
  • 더 많은 기능 / 기능을 제공하는 클래스 계층 구조의 이점
  • 적절하게 사용되면 광범위한 오류를 나타낼 수 있습니다 (예 : InvalidMethodCallException도 LogicException입니다. 둘 다 런타임 전에 감지 할 수 있어야하는 코드에 버그가있을 때 발생하기 때문입니다).
  • 오류를 개선하는 데 사용할 수 있습니다 (예 : FileReadException 클래스 정의는 파일이 있는지 또는 잠겨 있는지 여부를 확인하는 코드를 포함 할 수 있습니다).

2
귀하의 네 번째 점은 공정의 하나없는 :는 오류 상태 또한 파일 그것은 단지의 변형이다 등의 존재, 또는 잠겨 있는지 여부를 확인하는 코드를 포함 할 수있는 객체로 변환 stackoverflow.com/a/3157182/632951
Pacerier 2014

1
"그들은 논리의 흐름을 방해한다". 예외는 다소 영향을 미칩니다goto
peterchaula

22

함수 호출자는 오류 코드를 무시할 수 있습니다. 예외는 최소한 어떤 방식 으로든 오류를 처리하도록 강제합니다. 그들의 버전이 빈 캐치 핸들러를 갖는 것이더라도 (한숨).


17

오류 코드에 대한 예외는 의심 할 여지가 없습니다. 오류 코드와 마찬가지로 예외에서 많은 이점을 얻을 수 있지만 오류 코드의 단점없이 훨씬 더 많은 이점을 얻을 수 있습니다. 예외에 대한 유일한 노크는 오버 헤드가 약간 더 많다는 것입니다. 그러나 오늘날에는 거의 모든 응용 프로그램에서 이러한 오버 헤드를 무시할 수있는 수준으로 간주해야합니다.

다음은 두 가지 기술을 논의, 비교 및 ​​대조하는 몇 가지 기사입니다.

더 많은 정보를 얻을 수있는 좋은 링크가 있습니다.


16

나는 두 모델을 절대 혼합하지 않을 것입니다. 오류 코드를 사용하는 스택의 한 부분에서 예외를 사용하는 상위 부분으로 이동할 때 하나에서 다른 모델로 변환하기가 너무 어렵습니다.

예외는 "메서드 또는 서브 루틴이 요청한 작업을 수행하는 것을 중지하거나 방해하는 모든 것"에 대한 것입니다 ... 불규칙성 또는 비정상적인 상황 또는 시스템 상태 등에 대한 메시지를 다시 전달하지 마십시오. 반환 값 또는 참조를 사용하십시오. (또는 출력) 매개 변수.

예외는 메서드의 기능에 의존하는 의미론으로 메서드를 작성 (활용) 할 수있게합니다. 즉, Employee 객체 또는 직원 목록을 반환하는 메서드를 입력하여이를 수행 할 수 있으며 호출하여 사용할 수 있습니다.

Employee EmpOfMonth = GetEmployeeOfTheMonth();

오류 코드를 사용하면 모든 메서드가 오류 코드를 반환하므로 호출 코드에서 사용할 다른 것을 반환해야하는 메서드의 경우 해당 데이터로 채워질 참조 변수를 전달하고 해당 데이터에 대한 반환 값을 테스트해야합니다. 모든 함수 또는 메서드 호출에서 오류 코드를 처리하고 처리합니다.

Employee EmpOfMonth; 
if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR)
    // code to Handle the error here

각 메서드가 한 가지 간단한 작업 만 수행하도록 코딩하는 경우 메서드가 원하는 목적을 달성 할 수 없을 때마다 예외를 throw해야합니다. 예외는 이러한 방식으로 오류 코드보다 훨씬 풍부하고 사용하기 쉽습니다. 코드가 훨씬 깔끔합니다. "일반적인"코드 경로의 표준 흐름은 메서드가 원하는 작업을 수행 할 수있는 경우에만 엄격하게 적용 할 수 있습니다. 그런 다음 코드를 정리하거나 처리 할 수 ​​있습니다. 메서드가 성공적으로 완료되지 못하게하는 나쁜 일이 발생하는 "예외적 인"상황은 일반 코드에서 격리 될 수 있습니다. 또한 예외가 발생한 곳에서 예외를 처리 할 수없고 스택을 UI로 전달해야하는 경우 (더 나쁜 경우에는 중간 계층 구성 요소에서 UI로 연결) 예외 모델을 사용하여


좋은 대답입니다! 즉시 사용 가능한 솔루션!
Cristian E.

11

과거에는 오류 코드 캠프에 참여했습니다 (C 프로그래밍을 너무 많이 했음). 그러나 이제 나는 빛을 보았다.

예, 예외는 시스템에 약간의 부담이됩니다. 그러나 코드를 단순화하여 오류 (및 WTF) 수를 줄입니다.

따라서 예외를 사용하되 현명하게 사용하십시오. 그리고 그들은 당신의 친구가 될 것입니다.

참고로. 어떤 방법으로 어떤 예외를 던질 수 있는지 문서화하는 법을 배웠습니다. 불행히도 이것은 대부분의 언어에서 필요하지 않습니다. 그러나 적절한 수준에서 적절한 예외를 처리 할 가능성이 높아집니다.


1
yap C는 우리 모두에게 몇 가지 습관을 남깁니다;)
Jorge Ferreira

11

깨끗하고 명확하며 올바른 방법으로 예외를 사용하는 것이 번거로운 몇 가지 상황이있을 수 있지만 대부분의 경우 예외가 확실한 선택입니다. 예외 처리가 오류 코드에 비해 가장 큰 이점은 실행 흐름을 변경한다는 것입니다. 이는 두 가지 이유로 중요합니다.

예외가 발생하면 응용 프로그램은 더 이상 '정상'실행 경로를 따르지 않습니다. 이것이 그토록 중요한 첫 번째 이유는 코드 작성자가 제대로 작동하지 않는 한 프로그램이 중단되고 예측할 수없는 일을 계속하지 않기 때문입니다. 오류 코드가 확인되지 않고 잘못된 오류 코드에 대한 응답으로 적절한 조치가 취해지지 않으면 프로그램은 수행중인 작업을 계속 수행하고 해당 조치의 결과를 누가 알 수 있습니다. 프로그램이 '무엇이든'하는 것이 매우 비쌀 수있는 상황이 많이 있습니다. 회사가 판매하는 다양한 금융 상품에 대한 성과 정보를 검색하고 그 정보를 중개인 / 도매업자에게 전달하는 프로그램을 생각해보십시오. 뭔가 잘못되고 프로그램이 계속 진행된다면 오류가있는 성능 데이터를 중개인 및 도매업자에게 전달할 수 있습니다. 나는 다른 사람에 대해 잘 모르지만, 내 코드로 인해 회사가 7 자리 수의 규제 벌금을 부과하는 이유를 설명하는 부사장 사무실에 앉아있는 사람이되고 싶지 않습니다. 고객에게 오류 메시지를 전달하는 것이 일반적으로 '실제'로 보일 수있는 잘못된 데이터를 전달하는 것보다 바람직하며, 후자의 상황은 오류 코드와 같은 훨씬 덜 공격적인 접근 방식을 사용하여 실행하기가 훨씬 쉽습니다.

내가 예외를 좋아하고 정상적인 실행을 깨뜨리는 두 번째 이유는 '정상적인 일이 일어나고있는'로직을 '뭔가 잘못 된 로직'과 분리하여 유지하는 것이 훨씬 더 쉬워지기 때문입니다. 나에게 이것은 :

try {
    // Normal things are happening logic
catch (// A problem) {
    // Something went wrong logic
}

... 이보다 바람직합니다.

// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}

좋은 예외에 대한 다른 사소한 것도 있습니다. 함수에서 호출되는 메서드에 오류 코드가 반환되었는지 여부를 추적하고 해당 오류 코드를 상위에 반환하는 조건부 논리가 많은 것은 많은 보일러 플레이트입니다. 사실, 잘못 될 수있는 것은 많은 보일러 플레이트입니다. 저는 대부분의 언어의 예외 시스템에 대해 프레드가 쓴 '신입생'프레드가 쓴 if-else-if-else 문장의 쥐 둥지를하는 것보다 훨씬 더 많은 믿음을 가지고 있습니다. 코드 검토보다 시간을 들여 쥐의 둥지를 말 했어요.


8

둘 다 사용해야합니다. 문제는 각각을 언제 사용할지 결정하는 것입니다 .

예외가 확실한 선택 인 몇 가지 시나리오 가 있습니다 .

  1. 어떤 상황에서 당신은 오류 코드와 함께 아무것도 할 수 없어 , 당신은 단지 호출 스택의 상위 레벨에서 그것을 처리 할 필요가 대개 오류, 사용자 또는 가까운 프로그램 디스플레이 뭔가를 기록. 이러한 경우 오류 코드를 사용하려면 오류 코드를 수준별로 수동으로 버블 링해야합니다. 이는 예외로 수행하기 훨씬 더 쉽습니다. 요점은 이것이 예상치 못한 처리 불가능한 상황에 대한 것입니다.

  2. 그러나 상황 1 (예기치 않고 처리 할 수없는 일이 발생하는 경우 기록하고 싶지 않음) 에 대해서는 상황에 맞는 정보를 추가 할 수 있으므로 예외가 도움이 될 수 있습니다 . 예를 들어 하위 수준 데이터 도우미에서 SqlException이 발생하면 하위 수준 (오류를 일으킨 SQL 명령을 알고 있음)에서 해당 오류를 포착하여 해당 정보를 캡처하고 추가 정보로 다시 던질 수 있습니다. . 여기에 마법의 단어가 있습니다 : 다시 던지고 삼키지 마십시오 . 예외 처리의 첫 번째 규칙 : 예외를 삼키지 마십시오 . 또한 외부 캐치는 전체 스택 추적을 갖고이를 로깅 할 수 있기 때문에 내 내부 캐치는 아무것도 기록 할 필요가 없습니다.

  3. 일부 상황에서는 일련의 명령이 있으며, 명령 중 하나라도 실패 하면 복구 할 수없는 상황 (폐기해야 함)인지 또는 복구 할 수있는 상황 (이 경우에는 할 수 있는지) 여부에 관계없이 리소스 (*)를 정리 / 삭제 해야합니다. 로컬 또는 호출자 코드에서 처리하지만 예외는 필요하지 않습니다). 각 방법 후에 오류 코드를 테스트하고 finally 블록에서 정리 / 처리하는 대신 이러한 모든 명령을 한 번에 시도하는 것이 훨씬 쉽습니다. 노트는 것을 제발 당신이 (당신이 원하는 아마 인) 거품까지 오류를 원한다면, 당신도 그것을 잡을 필요가 없습니다 - 당신은 정리 / 처분에 대한 마지막을 사용 - 당신이 원하는 경우에만 캐치 / retrow를 사용한다 컨텍스트 정보를 추가합니다 (글 머리 기호 2 참조).

    한 가지 예는 트랜잭션 블록 내부의 일련의 SQL 문입니다. 다시 말하지만, 이것은 또한 "처리 할 수없는"상황입니다. 비록 당신이 그것을 조기에 잡기로 결정하더라도 (상단까지 버블 링하는 대신 로컬로 처리하기로 결정한 경우) 여전히 모든 것을 중단하거나 적어도 큰 것을 중단하는 것이 최선의 결과 인 치명적인 상황 입니다. 과정의 일부.
    (*) 이것은 on error goto이전 Visual Basic에서 사용한 것과 같습니다.

  4. 생성자에서는 예외 만 던질 수 있습니다.

하지만 발신자가 조치를 취할 수 있는 일부 정보를 반환하는 다른 모든 상황에서는 반환 코드를 사용하는 것이 더 나은 대안 일 수 있습니다. 여기에는 예상되는 모든 "오류"가 포함됩니다 . 왜냐하면 이러한 오류 는 즉각적인 호출자에 의해 처리되어야하고 스택에서 너무 많은 수준으로 버블 링 될 필요가 거의 없기 때문입니다.

물론 예상되는 오류를 예외로 처리하고 즉시 한 단계 위의 코드를 포착 할 수 있으며 try catch에 모든 코드 줄을 포함하고 가능한 각 오류에 대해 조치를 취하는 것도 가능합니다. IMO, 이것은 훨씬 더 장황하기 때문일뿐만 아니라 발생할 수있는 예외가 소스 코드를 읽지 않고는 명확하지 않고 예외가 발생하여 보이지 않는 gotos를 생성 할 수 있기 때문에 잘못된 설계입니다 . 코드를 읽고 검사하기 어렵게 만드는 보이지 않는 여러 종료 지점을 만들어 코드 구조를 깨뜨립니다. 즉, 예외를 흐름 제어 사용해서는 안됩니다., 다른 사람들이 이해하고 유지하기 어려울 것이기 때문입니다. 테스트를 위해 가능한 모든 코드 흐름을 이해하는 것도 어려울 수 있습니다.
다시 : 올바른 정리 / 처리를 위해 아무것도 잡지 않고 try-finally를 사용할 수 있습니다 .

반환 코드에 대한 가장 인기있는 비판은 "누군가 오류 코드를 무시할 수 있지만 같은 의미에서 누군가 예외를 삼킬 수 있습니다. 두 가지 방법 모두에서 잘못된 예외 처리가 쉽습니다 . 그러나 좋은 오류 코드 기반 프로그램을 작성하는 것은 여전히 ​​훨씬 쉽습니다. 예외 기반 프로그램을 작성하는 것보다 어떤 이유에서든 모든 오류 (이전 오류)를 무시하기로 결정한 경우 on error resume next반환 코드로 쉽게 수행 할 수 있으며 많은 try-catchs 상용구 없이는 수행 할 수 없습니다.

반환 코드에 대한 두 번째로 인기있는 비판은 "버블 업하기 어렵다"는 것입니다.하지만 예외가 복구 불가능한 상황에 대한 것이고 오류 코드는 그렇지 않다는 것을 사람들이 이해하지 못하기 때문입니다.

예외와 오류 코드 사이의 결정은 회색 영역입니다. 재사용 가능한 비즈니스 방법에서 오류 코드를 가져 와서이를 예외 (정보 추가 가능)로 래핑하고 버블 링되도록 할 수도 있습니다. 그러나 모든 오류가 예외로 던져 져야한다고 가정하는 것은 설계상의 실수입니다.

그것을 요 ​​약하기:

  • 할 일이 많지 않은 예기치 않은 상황이 발생했을 때 예외를 사용하는 것을 좋아하며 일반적으로 큰 코드 블록이나 전체 작업 또는 프로그램을 중단하고 싶습니다. 이것은 예전의 "on error goto"와 같습니다.

  • 호출자 코드가 조치를 취할 수 있거나 취해야하는 상황이 예상 될 때 리턴 코드를 사용하고 싶습니다. 여기에는 대부분의 비즈니스 방법, API, 유효성 검사 등이 포함됩니다.

예외와 오류 코드 간의 이러한 차이는 GO 언어의 설계 원칙 중 하나이며, 예상치 못한 치명적인 상황에 "패닉"을 사용하는 반면 일반적인 예상 상황은 오류로 반환됩니다.

그러나 GO에 대해서는 여러 반환 값을 허용 하는데, 이는 오류와 다른 것을 동시에 반환 할 수 있기 때문에 반환 코드를 사용하는 데 많은 도움이됩니다. C # / Java에서는 매개 변수, 튜플 또는 (내가 가장 좋아하는) 제네릭을 사용하여이를 달성 할 수 있으며, 열거 형과 결합되어 호출자에게 명확한 오류 코드를 제공 할 수 있습니다.

public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
    ....
    return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");

    ...
    return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}

var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
    // do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
    order = result.Entity; // etc...

메서드에 새로운 가능한 반환을 추가하면 예를 들어 switch 문에서 새 값을 다루는 모든 호출자를 확인할 수도 있습니다. 예외가 있으면 정말 그렇게 할 수 없습니다. 반환 코드를 사용할 때 일반적으로 가능한 모든 오류를 미리 알고 테스트합니다. 예외를 제외하고는 일반적으로 무슨 일이 일어날 지 모릅니다. Generics 대신 예외 내부에 열거 형을 래핑하는 것은 대안이지만 (각 메서드가 throw 할 예외 유형이 명확하다면) IMO는 여전히 나쁜 설계입니다.


4

내 추론은 실제로 성능이 필요한 저수준 드라이버를 작성하고 오류 코드를 사용하는 것입니다. 그러나 더 높은 수준의 애플리케이션에서 해당 코드를 사용하고 약간의 오버 헤드를 처리 할 수 ​​있다면 해당 코드를 해당 오류 코드를 확인하고 예외를 발생시키는 인터페이스로 래핑하십시오.

다른 모든 경우에는 예외가있을 수 있습니다.


4

여기 울타리에 앉아 있을지도 모르지만 ...

  1. 언어에 따라 다릅니다.
  2. 어떤 모델을 선택하든 사용 방법에 대해 일관성을 유지하십시오.

파이썬에서 예외의 사용은 표준 관행이며, 제 자신의 예외를 정의하게되어 매우 기쁩니다. C에서는 예외가 전혀 없습니다.

C ++ (적어도 STL에서는)에서 예외는 일반적으로 예외적 인 오류에 대해서만 발생합니다 (실제로 직접 볼 수 없습니다). 내 코드에서 다른 것을 할 이유가 없습니다. 예, 반환 값을 무시하기 쉽지만 C ++는 예외를 포착하도록 강요하지 않습니다. 나는 당신이 그것을하는 습관을 가져야한다고 생각합니다.

내가 작업하는 코드베이스는 대부분 C ++이고 거의 모든 곳에서 오류 코드를 사용하지만 예외적 인 오류를 포함하여 모든 오류에 대해 예외를 발생시키는 모듈이 하나 있고 해당 모듈을 사용하는 모든 코드는 매우 끔찍합니다. 하지만 예외와 오류 코드가 혼합되어 있기 때문일 수 있습니다. 지속적으로 오류 코드를 사용하는 코드는 작업하기가 훨씬 쉽습니다. 코드가 지속적으로 예외를 사용했다면 그렇게 나쁘지는 않을 것입니다. 두 가지를 섞는 것이 잘 작동하지 않는 것 같습니다.


4

C ++로 작업하고 RAII를 사용하여 안전하게 사용하기 때문에 거의 독점적으로 예외를 사용합니다. 정상적인 프로그램 흐름에서 오류 처리를 가져와 의도를 더 명확하게 만듭니다.

하지만 예외적 인 상황에 대해서는 예외를 남겨 둡니다. 나는 어떤 오류가 나는 작업이를 수행하기 전에 성공, 또는 그 대신 오류 코드를 사용하는 기능의 버전을 호출하는지 확인합니다 많은 일이 일어날 것을 (처럼 기대하고있어 경우 TryParse())


3

메서드 서명은 메서드가 수행하는 작업을 사용자에게 전달해야합니다. long errorCode = getErrorCode (); 괜찮을 수 있지만 long errorCode = fetchRecord (); 혼란 스럽습니다.


3

내 접근 방식은 예외와 오류 코드를 동시에 사용할 수 있다는 것입니다.

저는 여러 유형의 예외 (예 : DataValidationException 또는 ProcessInterruptExcepion)를 정의하는 데 익숙하며 각 예외 내에서 각 문제에 대한 자세한 설명을 정의합니다.

자바의 간단한 예 :

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

이런 식으로 예외를 사용하여 범주 오류를 정의하고 오류 코드를 사용하여 문제에 대한 자세한 정보를 정의합니다.


2
음 ... throw new DataValidationException("The input is too small")? 예외의 장점 중 하나는 자세한 정보를 허용한다는 것입니다.
에바

2

예외는 예외적 인 상황 (예 : 정상적인 코드 흐름의 일부가 아닌 경우)입니다.

예외와 오류 코드를 혼합하는 것은 매우 합법적입니다. 여기서 오류 코드는 코드 실행 자체의 오류 (예 : 자식 프로세스의 반환 코드 확인)가 아닌 무언가의 상태를 나타냅니다.

하지만 예외적 인 상황이 발생하면 예외가 가장 표현력있는 모델이라고 생각합니다.

예외 대신 오류 코드를 사용하는 것을 선호하거나 갖고있는 경우가 있으며, 이러한 경우는 이미 적절하게 다루었습니다 (컴파일러 지원과 같은 다른 명백한 제약 제외).

그러나 다른 방향으로 가면 Exceptions를 사용하면 오류 처리에 대한 더 높은 수준의 추상화를 구축하여 코드를 훨씬 더 표현적이고 자연스럽게 만들 수 있습니다. C ++ 전문가 Andrei Alexandrescu가 "집행"이라고 부르는 주제에 대해 훌륭하지만 과소 평가 된이 기사를 읽는 것이 좋습니다. http://www.ddj.com/cpp/184403864 . C ++ 기사이지만 원칙은 일반적으로 적용 할 수 있으며 시행 개념을 C #으로 꽤 성공적으로 번역했습니다.


2

첫째, 나는 서비스 지향 아키텍처 (SOA)가 아닌 한 높은 수준의 항목에는 예외를 사용하고 낮은 수준의 항목에는 오류 코드를 사용한다는 Tom의 대답에 동의합니다 .

메서드가 다른 컴퓨터에서 호출 될 수있는 SOA에서는 예외가 유선으로 전달되지 않을 수 있으며 대신 아래와 같은 구조 (C #)로 성공 / 실패 응답을 사용합니다.

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

다음과 같이 사용하십시오.

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

이것이 서비스 응답에서 일관되게 사용되면 애플리케이션에서 성공 / 실패를 처리하는 매우 멋진 패턴을 생성합니다. 이를 통해 서비스 내 및 서비스 간 비동기 호출에서 오류를 더 쉽게 처리 할 수 ​​있습니다.


1

실패가 원시 데이터 유형을 반환하는 함수의 예상 가능한 버그없는 결과 인 경우를 제외하고 모든 오류 사례에 대해 예외를 선호합니다. 예를 들어 더 큰 문자열 내에서 하위 문자열의 인덱스를 찾는 것은 NotFoundException을 발생시키는 대신 찾을 수없는 경우 일반적으로 -1을 반환합니다.

역 참조 될 수있는 잘못된 포인터를 반환하는 것은 허용되지 않습니다 (예 : Java에서 NullPointerException 발생).

클라이언트가 "<0"대신 "== -1"검사를 수행 할 수 있으므로 동일한 함수에 대한 반환 값으로 여러 다른 숫자 오류 코드 (-1, -2)를 사용하는 것은 일반적으로 잘못된 스타일입니다.

여기서 명심해야 할 사항은 시간이 지남에 따라 API가 진화한다는 것입니다. 좋은 API를 사용하면 클라이언트를 중단하지 않고 여러 방법으로 실패 동작을 변경하고 확장 할 수 있습니다. 예를 들어 클라이언트 오류 핸들에서 4 개의 오류 사례를 확인하고 함수에 다섯 번째 오류 값을 추가하면 클라이언트 처리기가이를 테스트하지 않고 중단 될 수 있습니다. 예외를 발생 시키면 일반적으로 클라이언트가 최신 버전의 라이브러리로 마이그레이션하기가 더 쉬워집니다.

고려해야 할 또 다른 사항은 팀에서 작업 할 때 모든 개발자가 그러한 결정을 내릴 수 있도록 명확한 선을 그릴 위치입니다. 예 : "고수준 항목에 대한 예외, 하위 수준 항목에 대한 오류 코드"는 매우 주관적입니다.

어떤 경우에도 사소한 오류 유형이 둘 이상 가능한 경우 소스 코드는 오류 코드를 반환하거나 처리하기 위해 숫자 리터럴을 사용해서는 안됩니다 (x == -7 ... 인 경우 -7 반환). 항상 명명 된 상수 (x == NO_SUCH_FOO 인 경우 NO_SUCH_FOO 반환).


1

큰 프로젝트에서 작업하는 경우 예외 또는 오류 코드 만 사용할 수 없습니다. 다른 경우에는 다른 접근 방식을 사용해야합니다.

예를 들어 예외 만 사용하기로 결정합니다. 그러나 비동기 이벤트 처리를 사용하기로 결정하면. 이 상황에서 오류 처리를 위해 예외를 사용하는 것은 좋지 않습니다. 그러나 응용 프로그램의 모든 곳에서 오류 코드를 사용하는 것은 지루합니다.

따라서 예외와 오류 코드를 동시에 사용하는 것이 정상이라고 생각합니다.


0

대부분의 응용 프로그램에서는 예외가 더 좋습니다. 소프트웨어가 다른 장치와 통신해야하는 경우는 예외입니다. 내가 일하는 영역은 산업 제어입니다. 여기서 오류 코드가 선호되고 예상됩니다. 그래서 제 대답은 상황에 따라 다르다는 것입니다.


0

또한 결과에서 스택 추적과 같은 정보가 정말로 필요한지 여부에 달려 있다고 생각합니다. 그렇다면 문제에 대한 많은 정보로 가득 찬 객체를 제공하는 예외로 이동합니다. 그러나 결과에 관심이 있고 그 결과가 왜 그런지 신경 쓰지 않는다면 오류 코드로 이동하십시오.

예를 들어 파일을 처리하고 IOException에 직면 할 때 클라이언트는 파일을 열거 나 파일을 구문 분석하는 등 트리거 된 위치를 알고 싶어 할 수 있습니다. 따라서 IOException 또는 특정 하위 클래스를 더 잘 반환합니다. 그러나 로그인 방법이 있고 성공했는지 여부를 알고 싶은 시나리오는 부울을 반환하거나 올바른 메시지를 표시하고 오류 코드를 반환합니다. 여기에서 클라이언트는 논리의 어느 부분이 해당 오류 코드를 일으켰는지 아는 데 관심이 없습니다. 그는 Credential이 유효하지 않거나 계정 잠금 등인지 알고 있습니다.

제가 생각할 수있는 또 다른 사용 사례는 데이터가 네트워크를 통해 이동할 때입니다. 원격 메소드는 데이터 전송을 최소화하기 위해 예외 대신 오류 코드 만 리턴 할 수 있습니다.


0

내 일반적인 규칙은 다음과 같습니다.

  • 함수에는 하나의 오류 만 나타날 수 있습니다. 오류 코드 사용 (함수 매개 변수로)
  • 둘 이상의 특정 오류가 나타날 수 있습니다. 예외 발생

-1

메서드가 숫자 값 이외의 것을 반환하는 경우에도 오류 코드가 작동하지 않습니다.


3
음 .. 아니야. win32 GetLastError () 패러다임을 참조하십시오. 나는 그것을 옹호하는 것이 아니라 단지 당신이 틀렸다고 주장합니다.
Tim

4
실제로이를 수행하는 다른 많은 방법이 있습니다. 또 다른 방법은 오류 코드와 실제 반환 값을 포함하는 객체를 반환하는 것입니다. 또 다른 방법은 참조 전달을 수행하는 것입니다.
Pacerier 2014-07-25
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.