내 질문은 대부분의 개발자가 오류 처리, 예외 또는 오류 반환 코드에 대해 선호하는 것입니다. 특정 언어 (또는 언어 계열)를 지정하고 다른 언어보다 선호하는 이유를 기재하십시오.
나는 호기심에서 이것을 묻는 것입니다. 개인적으로 오류 반환 코드는 덜 폭발적이고 사용자 코드가 원하지 않는 경우 예외 성능 패널티를 지불하도록 강요하지 않기 때문에 선호합니다.
업데이트 : 모든 답변에 감사드립니다! 예외가있는 코드 흐름의 예측 불가능 성을 싫어하지만 말해야합니다. 반환 코드에 대한 대답 (및 형 핸들)은 코드에 많은 노이즈를 추가합니다.
답변
일부 언어 (예 : C ++)의 경우 리소스 누출이 원인이되어서는 안됩니다.
C ++는 RAII를 기반으로합니다.
실패, 반환 또는 던질 수있는 코드가있는 경우 (즉, 대부분의 일반적인 코드) 스마트 포인터 안에 포인터를 래핑 해야합니다 (객체를 스택에 만들지 않을 좋은 이유 가 있다고 가정 ).
리턴 코드가 더 상세 함
장황하고 다음과 같이 발전하는 경향이 있습니다.
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
결국 코드는 식별 된 명령의 모음입니다 (프로덕션 코드에서 이러한 종류의 코드를 보았습니다).
이 코드는 다음과 같이 잘 번역 될 수 있습니다.
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
어떤 깨끗하게 별도의 코드와 오류 처리 할 수 될 좋은의 일.
반환 코드가 더 부서지기 쉽습니다.
한 컴파일러의 모호한 경고가 아니라면 ( “phjr”의 주석 참조) 쉽게 무시할 수 있습니다.
위의 예를 통해 누군가가 가능한 오류를 처리하는 것을 잊는다 고 가정합니다 (이런 일이 발생합니다 …). 오류는 “반환”될 때 무시되며 나중에 폭발 할 가능성이 있습니다 (예 : NULL 포인터). 예외로 동일한 문제가 발생하지 않습니다.
오류는 무시되지 않습니다. 때로는 폭발하지 않기를 원하지만 … 그러니 신중하게 선택해야합니다.
반환 코드는 때때로 번역되어야합니다.
다음과 같은 기능이 있다고 가정 해 보겠습니다.
- NOT_FOUND_ERROR라는 정수를 반환 할 수있는 doSomething
- bool “false”를 반환 할 수있는 doSomethingElse (실패한 경우)
- doSomethingElseAgain : Error 객체를 반환 할 수 있습니다 (__LINE__, __FILE__ 및 스택 변수의 절반 포함).
- doTryToDoSomethingWithAllThisMess that, well … 위의 함수를 사용하고 유형의 오류 코드를 반환합니다 …
호출 된 함수 중 하나가 실패 할 경우 doTryToDoSomethingWithAllThisMess의 반환 유형은 무엇입니까?
반환 코드는 보편적 인 솔루션이 아닙니다.
연산자는 오류 코드를 반환 할 수 없습니다. C ++ 생성자도 그렇게 할 수 없습니다.
반환 코드는 표현식을 연결할 수 없음을 의미합니다.
위 요점의 결과. 작성하려면 어떻게해야합니까?
CMyType o = add(a, multiply(b, c)) ;
반환 값이 이미 사용 되었기 때문에 할 수 없습니다 (때로는 변경할 수없는 경우도 있음). 따라서 반환 값은 참조로 전송되는 첫 번째 매개 변수가됩니다.
예외가 입력되었습니다.
각 예외 유형에 대해 서로 다른 클래스를 보낼 수 있습니다. 리소스 예외 (즉, 메모리 부족)는 가벼워 야하지만 그 밖의 모든 것은 필요한만큼 무거울 수 있습니다 (전체 스택을 제공하는 Java 예외가 마음에 듭니다).
그런 다음 각 캐치를 특수화 할 수 있습니다.
다시 던지지 않고 catch (…)를 사용하지 마십시오.
일반적으로 오류를 숨기면 안됩니다. 다시 던지지 않으면 최소한 파일에 오류를 기록하고 메시지 상자를여십시오.
예외는 … NUKE
예외의 문제는 그것들을 남용하면 try / catches로 가득 찬 코드가 생성된다는 것입니다. 그러나 문제는 다른 곳에 있습니다. 누가 STL 컨테이너를 사용하여 코드를 시도 / 캐치합니까? 그래도 이러한 컨테이너는 예외를 보낼 수 있습니다.
물론 C ++에서는 예외가 소멸자를 종료하지 않도록합니다.
예외는 … 동기
스레드가 무릎을 꿇거나 Windows 메시지 루프 내부로 전파되기 전에이를 포착해야합니다.
해결책은 그것들을 혼합하는 것일 수 있습니까?
내가 추측 그래서 해결책은 무엇인가시기를 던질 것입니다 하지 일어난다. 그리고 어떤 일이 발생하면 반환 코드 나 매개 변수를 사용하여 사용자가 이에 반응 할 수 있도록합니다.
따라서 유일한 질문은 “발생해서는 안되는 일이 무엇입니까?”입니다.
그것은 당신의 기능의 계약에 달려 있습니다. 함수가 포인터를 받아들이지 만 포인터가 NULL이 아니어야한다고 지정하면 사용자가 NULL 포인터를 보낼 때 예외를 throw해도 괜찮습니다 (문제는 C ++에서 함수 작성자가 대신 참조를 사용하지 않았을 때) 하지만 …)
또 다른 해결책은 오류를 표시하는 것입니다.
때때로 문제는 오류를 원하지 않는다는 것입니다. 예외 나 오류 반환 코드를 사용하는 것은 멋지지만 … 그것에 대해 알고 싶습니다.
제 직업에서 우리는 일종의 “Assert”를 사용합니다. 디버그 / 릴리스 컴파일 옵션에 관계없이 구성 파일의 값에 따라 다음과 같이됩니다.
- 오류 기록
- “Hey, you have a problem”이있는 메시지 상자를 엽니 다.
- “문제가 있습니다. 디버그 하시겠습니까?”라는 메시지 상자를 엽니 다.
개발과 테스트 모두에서 사용자는 문제가 발견 된 후가 아니라 (일부 코드가 반환 값에 관심이 있거나 catch 내부에있을 때) 정확히 발견 된 시점에 문제를 정확히 찾아 낼 수 있습니다.
레거시 코드에 쉽게 추가 할 수 있습니다. 예를 들면 :
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
다음과 유사한 종류의 코드를 유도합니다.
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(디버그에서만 활성화되는 유사한 매크로가 있습니다).
프로덕션에서는 구성 파일이 존재하지 않으므로 클라이언트는이 매크로의 결과를 볼 수 없습니다. 그러나 필요할 때 활성화하는 것은 쉽습니다.
결론
반환 코드를 사용하여 코드를 작성할 때 실패에 대비하고 테스트 요새가 충분히 안전하기를 바랍니다.
예외를 사용하여 코드를 작성할 때 코드가 실패 할 수 있음을 알고 일반적으로 코드에서 선택한 전략적 위치에 반격을가합니다. 그러나 일반적으로 코드는 “해야 할 일”보다 “내가 두려워하는 일”에 대한 것입니다.
그러나 코드를 작성할 때는 최선의 도구를 사용해야하며 때로는 “오류를 숨기지 않고 가능한 한 빨리 표시”하는 경우가 있습니다. 위에서 언급 한 매크로는이 철학을 따릅니다.
답변
실제로 둘 다 사용합니다.
알려진 가능한 오류 인 경우 반환 코드를 사용합니다. 내가 아는 시나리오가 가능하고 일어날 수 있다면 다시 전송되는 코드가 있습니다.
예외는 내가 기대하지 않는 것들에만 사용됩니다.
답변
Framework Design Guidelines : Conventions, Idioms and Patterns for Reusable .NET Libraries의 7 장 “예외”에 따르면 C #과 같은 OO 프레임 워크에서 반환 값에 대한 예외를 사용해야하는 이유에 대한 많은 근거가 제공됩니다.
아마도 이것이 가장 설득력있는 이유 일 것입니다 (179 페이지).
“예외는 객체 지향 언어와 잘 통합됩니다. 객체 지향 언어는 비 OO 언어의 함수에 의해 부과되지 않는 멤버 서명에 제약을 부과하는 경향이 있습니다. 예를 들어 생성자, 연산자 오버로드 및 속성의 경우 개발자는 반환 값에 선택의 여지가 없습니다. 이러한 이유로 객체 지향 프레임 워크에 대한 반환 값 기반 오류보고를 표준화 할 수 없습니다. 예외와 같은 오류보고 방법은 메서드 서명 범위를 벗어납니다. 유일한 옵션입니다. “
답변
내 선호도 (C ++ 및 Python)는 예외를 사용하는 것입니다. 언어 제공 기능은 예외를 발생, 포착 및 (필요한 경우) 다시 던지는 잘 정의 된 프로세스를 만들어 모델을 쉽게보고 사용할 수 있도록합니다. 개념적으로는 특정 예외가 이름으로 정의 될 수 있고 추가 정보가 수반된다는 점에서 리턴 코드보다 깔끔합니다. 반환 코드를 사용하면 오류 값으로 만 제한됩니다 (ReturnStatus 객체 등을 정의하려는 경우 제외).
작성중인 코드가 시간이 중요하지 않은 경우 스택 해제와 관련된 오버 헤드는 걱정할만큼 중요하지 않습니다.
답변
예상하지 못한 일이 발생하는 경우에만 예외가 반환되어야합니다.
역사적으로 예외의 또 다른 점은 반환 코드가 본질적으로 독점적이라는 것입니다. 때로는 성공을 나타 내기 위해 C 함수에서 0이 반환 될 수 있고, 때로는 -1이 반환 될 수도 있고, 실패 할 경우에는 둘 중 하나가 반환되고 성공하면 1이 반환 될 수 있습니다. 열거 된 경우에도 열거가 모호 할 수 있습니다.
예외는 또한 더 많은 정보를 제공 할 수 있으며 특히 ‘뭔가 잘못되었습니다. 여기에 스택 추적 및 컨텍스트에 대한 일부 지원 정보’를 잘 설명합니다.
즉, 잘 열거 된 반환 코드는 알려진 결과 집합에 유용 할 수 있습니다. 간단한 ‘여기에 함수의 n 개의 결과가 있으며이 방식으로 실행되었습니다.’
답변
Java에서는 다음을 사용합니다 (다음 순서로).
-
계약 별 설계 ( 실패 할 수 있는 작업 을 시도하기 전에 사전 조건이 충족되는지 확인 ). 이것은 대부분의 것을 포착하고 이에 대한 오류 코드를 반환합니다.
-
작업을 처리하는 동안 오류 코드를 반환하고 필요한 경우 롤백을 수행합니다.
-
예외이지만 예상치 못한 일 에만 사용 됩니다 .
답변
반환 코드는 거의 매번 ” 성공의 구덩이 “테스트에 실패합니다 .
- 반환 코드를 확인하는 것을 잊고 나중에 red-herring 오류가 발생하는 것은 너무 쉽습니다.
- 반환 코드에는 호출 스택, 내부 예외와 같은 훌륭한 디버깅 정보가 없습니다.
- 반환 코드는 전파되지 않으며, 위의 요점과 함께 하나의 중앙 위치 (응용 프로그램 및 스레드 수준 예외 처리기)에 로깅하는 대신 과도하고 짜여진 진단 로깅을 유도하는 경향이 있습니다.
- 반환 코드는 중첩 된 ‘if’블록 형태로 복잡한 코드를 유도하는 경향이 있습니다.
- 명백한 예외 (성공의 구덩이) 였을 알려지지 않은 문제를 디버깅하는 데 소요 된 개발자 시간은 비용이 많이 듭니다.
- C # 팀이 제어 흐름을 제어하는 예외를 의도하지 않았다면 예외가 입력되지 않고 catch 문에 “when”필터가 없으며 매개 변수가없는 ‘throw’문이 필요하지 않습니다. .
성능 관련 :
- 예외는 전혀 던지지 않는 것에 비해 계산 비용이 많이들 수 있지만, 이유 때문에 EXCEPTIONS라고 불립니다. 속도 비교는 항상 100 % 예외 율을 가정하여 관리합니다. 예외가 100 배 더 느리더라도 1 %의 시간에만 발생하는 경우 실제로 얼마나 중요합니까?
- 그래픽 응용 프로그램이나 이와 유사한 것에 대한 부동 소수점 연산에 대해 이야기하지 않는 한 CPU주기는 개발자 시간에 비해 저렴합니다.
- 시간 관점에서의 비용은 같은 주장을합니다. 데이터베이스 쿼리, 웹 서비스 호출 또는 파일로드와 비교하여 일반적인 애플리케이션 시간은 예외 시간보다 작습니다. 예외는 2006 년에 거의 마이크로 초 미만 이었습니다.
- .net에서 일하는 모든 사람에게 감히 디버거가 모든 예외를 중단하고 내 코드 만 비활성화하도록 설정하고 알지도 못하는 예외가 이미 발생하고 있는지 확인합니다.
