[exception] 예외를 규칙적인 제어 흐름으로 사용하지 않는 이유는 무엇입니까?

Googled에 대한 모든 표준 답변을 피하기 위해 모든 사람이 마음대로 공격 할 수있는 예를 제공합니다.

C #과 Java (및 너무 많은 다른 것)에는 내가 좋아하지 않는 ‘오버플로’동작이 많이 있습니다 (예 type.MaxValue + type.SmallestValue == type.MinValue: int.MaxValue + 1 == int.MinValue:).

그러나 내 악의적 인 성격을 보았을 때이 행동을 확장 하여이 부상에 모욕을 더할 것 DateTime입니다. (나는 DateTime.NET에서 봉인 된 것을 알고 있지만이 예제를 위해 DateTime이 봉인되지 않았다는 점을 제외하고는 C #과 유사한 의사 언어를 사용하고 있습니다.)

재정의 된 Add방법 :

/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan. 
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and 
/// continue from DateTime.MinValue. 
/// </returns>
public DateTime override Add(TimeSpan ts) 
{
    try
    {                
        return base.Add(ts);
    }
    catch (ArgumentOutOfRangeException nb)
    {
        // calculate how much the MaxValue is exceeded
        // regular program flow
        TimeSpan saldo = ts - (base.MaxValue - this);
        return DateTime.MinValue.Add(saldo)                         
    }
    catch(Exception anyOther) 
    {
        // 'real' exception handling.
    }
}

물론 if는 이것을 쉽게 해결할 수는 있지만 예외를 사용할 수없는 이유를 알 수 없다는 사실은 여전히 ​​유지됩니다 (논리적으로, 성능이 특정 경우 예외를 피해야하는 문제가 있음을 알 수 있습니다) ).

나는 많은 경우에 그것들이 if 구조보다 명확하고 방법이 만드는 계약을 깨뜨리지 않는다고 생각합니다.

IMHO“정기 프로그램 흐름에는 절대로 사용하지 마십시오”라는 반응은 그 반응의 강도가 정당화 될 수 있기 때문에 제대로 구축되지 않은 것 같습니다.

아니면 내가 착각합니까?

나는 모든 종류의 특별한 경우를 다루는 다른 게시물을 읽었지만, 내 요점은 당신이 둘 다라면 아무런 문제가 없다는 것입니다.

  1. 명확한
  2. 방법의 계약을 존중하십시오

날 쏴



답변

정상적인 작동 과정에서 초당 5 개의 예외를 발생시키는 프로그램을 디버그하려고 시도한 적이 있습니까?

나는 가지고있다.

이 프로그램은 상당히 복잡했으며 (분산 계산 서버) 프로그램의 한 쪽을 약간 수정하면 완전히 다른 곳에서 무언가를 쉽게 깨뜨릴 수 있습니다.

프로그램을 시작하고 예외가 발생할 때까지 기다릴 수 있기를 원하지만 정상적인 작업 과정에서 시작하는 동안 약 200 개의 예외가있었습니다.

내 요점 : 정상적인 상황에서 예외를 사용하는 경우 비정상적인 상황 (예 : 예외 )을 어떻게 찾 습니까?

물론, 특히 성능 측면에서 예외를 너무 많이 사용하지 않는 다른 강력한 이유가 있습니다.


답변

예외는 기본적으로 goto후자의 모든 결과를 가진 로컬이 아닌 진술입니다. 흐름 제어에 예외를 사용하면 놀랍게도 되는 원칙을 위반하고 프로그램을 읽기 어렵게 만듭니다 (프로그램은 프로그래머를 위해 먼저 작성되어야 함을 기억하십시오).

게다가 이것은 컴파일러 벤더들이 기대하는 것이 아닙니다. 예외가 거의 발생하지 않기를 기대하며 일반적으로 throw코드가 상당히 비효율적입니다. 예외를 던지는 것은 .NET에서 가장 비싼 작업 중 하나입니다.

그러나 일부 언어 (특히 Python)는 예외를 흐름 제어 구문으로 사용합니다. 예를 들어, 반복자 StopIteration가 더 이상 항목이 없으면 예외를 발생시킵니다. 표준 언어 구문 (예 for:)도 이에 의존합니다.


답변

내 경험 법칙은 다음과 같습니다.

  • 오류에서 복구하기 위해 무엇이든 할 수 있다면 예외를 잡아라.
  • 오류가 매우 일반적인 경우 (예 : 사용자가 잘못된 비밀번호로 로그인을 시도한 경우) returnvalues를 사용하십시오.
  • 오류를 복구하기 위해 아무 것도 할 수없는 경우, 잡히지 말고 그대로 두십시오 (또는 응용 프로그램을 약간 정상적으로 종료하기 위해 주 캐처에서 잡으십시오)

예외로 볼 때 문제는 순전히 구문 관점에서입니다 (성능 오버 헤드가 최소화되어 있다고 확신합니다). 나는 모든 곳에서 try-blocks를 좋아하지 않는다.

이 예제를 보자 :

try
{
   DoSomeMethod();  //Can throw Exception1
   DoSomeOtherMethod();  //Can throw Exception1 and Exception2
}
catch(Exception1)
{
   //Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}

.. 또 다른 예는 팩토리를 사용하여 핸들에 무언가를 할당해야하는 경우에 해당 팩토리에서 예외가 발생할 수 있습니다.

Class1 myInstance;
try
{
   myInstance = Class1Factory.Build();
}
catch(SomeException)
{
   // Couldn't instantiate class, do something else..
}
myInstance.BestMethodEver();   // Will throw a compile-time error, saying that myInstance is uninitalized, which it potentially is.. :(

개인적으로, 드문 오류 조건 (메모리 부족 등)에 대한 예외를 유지하고 대신 반환 값 (값 클래스, 구조체 또는 열거 형)을 사용하여 오류 검사를 수행해야한다고 생각합니다.

나는 당신의 질문이 올바른 것을 이해하기를 바랍니다 🙂


답변

많은 답변에 대한 첫 번째 반응 :

프로그래머를 위해 쓰고 있으며 가장 놀랍게도하는 원리

물론이야! 그러나 if는 항상 더 명확하지 않습니다.

그것은 놀랍지 않아야합니다. 예 : 나누기 (1 / x) 캐치 (divisionByZero)는 나에게 (Conrad 및 다른 사람들에게) 나보다 더 분명합니다. 이러한 종류의 프로그래밍이 예상되지 않는다는 사실은 순전히 전통적이며 실제로는 여전히 관련이 있습니다. 어쩌면 내 예에서 if가 더 명확 할 것입니다.

그러나 그 문제에 대한 DivisionByZero와 FileNotFound는 if보다 명확합니다.

물론 성능이 떨어지고 초당 zillion 시간이 필요한 경우 당연히 피해야하지만 과도한 디자인을 피해야 할 이유를 읽지 못했습니다.

놀랍게도라는 원칙이 진행되는 한 여기에 순환 추론의 위험이 있습니다. 공동체 전체가 나쁜 디자인을 사용한다고 가정하면,이 디자인은 예상 될 것입니다! 그러므로 원칙은 성배가 될 수 없으며 신중하게 고려해야합니다.

정상적인 상황에 대한 예외, 비정상적인 (즉, 예외적 인) 상황을 어떻게 찾습니까?

많은 반응에서 sth. 이처럼 여물통이 빛납니다. 그냥 잡아라 귀하의 방법은 명확하고 잘 문서화되어 있으며 계약을 존중해야합니다. 인정해야 할 질문이 없습니다.

모든 예외에 대한 디버깅 : 동일합니다. 예외를 사용하지 않는 디자인이 일반적이기 때문에 때때로 수행됩니다. 내 질문은 : 왜 처음에 공통입니까?


답변

예외 전에 C에서 거기 setjmplongjmp그는 스택 프레임의 유사한 풀림을 달성하기 위해 사용될 수있다.

그런 다음 동일한 구성에 “예외”라는 이름이 지정되었습니다. 그리고 대부분의 답변은 예외적 인 조건에서 예외가 사용된다고 주장 하면서이 이름의 의미에 의존하여 사용법에 대해 논쟁합니다. 그것은 원래의 의도가 아니 었습니다 longjmp. 많은 스택 프레임에서 제어 흐름을 중단해야하는 상황이있었습니다.

동일한 스택 프레임 내에서도 예외를 사용할 수 있다는 점에서 예외가 약간 더 일반적입니다. 이것은 goto내가 틀렸다고 믿는 것과의 유사점을 높 입니다. Gotos는 (되도록하고 단단히 결합 된 쌍인 setjmp하고 longjmp). 느슨하게 연결된 발행 / 구독은 훨씬 더 깔끔합니다. 따라서 동일한 스택 프레임 내에서 사용하는 것은 gotos 를 사용하는 것과 거의 동일하지 않습니다 .

세 번째 혼란의 원인은 확인 된 예외인지 확인되지 않은 예외와 관련이 있습니다. 물론, 검사되지 않은 예외는 제어 흐름 및 기타 많은 것들에 사용하기에 특히 끔찍한 것 같습니다.

그러나 점검 한 예외는 빅토리아 행 아웃을 모두 극복하고 조금만 살면 제어 흐름에 좋습니다.

내가 가장 좋아하는 사용법은 원하는 throw new Success()코드를 찾을 때까지 한 번에 하나씩 시도하는 긴 코드 조각 시퀀스입니다 . 각각의 논리 조각마다 break임의의 중첩이있을 수 있으므로 모든 종류의 조건 테스트도 가능합니다. if-else패턴은 취성이다. else다른 방법으로 구문을 편집 하거나 엉망으로 만들면 털이 많은 버그가 있습니다.

를 사용 하면 코드 흐름이 throw new Success() 선형화 됩니다. 로컬로 정의 된 Success클래스 (물론 확인 됨)를 사용 하므로이를 잊어 버린 경우 코드가 컴파일되지 않습니다. 그리고 다른 방법을 찾지 못했습니다 Success.

때로는 내 코드가 다른 것을 차례로 확인하고 모든 것이 정상인 경우에만 성공합니다. 이 경우을 사용하는 비슷한 선형화가 throw new Failure()있습니다.

별도의 기능을 사용하면 자연스러운 수준의 구획화가 어려워집니다. 따라서 return솔루션이 최적이 아닙니다. 인지적인 이유로 한 페이지에 한두 페이지의 코드를 사용하는 것을 선호합니다. 나는 극도로 분할 된 코드를 믿지 않습니다.

핫스팟이 없으면 JVM 또는 컴파일러의 기능이 관련성이 떨어집니다. 컴파일러가 로컬에서 발생하고 잡은 예외를 감지하지 않고 goto기계 코드 수준에서 매우 효율적인 것으로 처리 해야하는 근본적인 이유가 있다고 생각할 수 없습니다 .

제어 흐름 (예 : 예외적 인 경우가 아닌 일반적인 경우)을 위해 여러 기능을 사용하는 한 여러 복원, 조건 테스트, 복원 만하는 것이 아니라 3 개의 스택 프레임을 통과하는 것보다 효율성이 떨어지는 방법을 알 수 없습니다. 스택 포인터

나는 개인적으로 스택 프레임에서 패턴을 사용하지 않으며 우아하게 디자인 정교함이 필요한 방법을 알 수 있습니다. 그러나 드물게 사용하면 괜찮을 것입니다.

마지막으로, 놀라운 처녀 프로그래머에 대해서는 설득력있는 이유가 아닙니다. 연습에 부드럽게 소개하면, 사랑하는 법을 배우게됩니다. C 프로그래머가 놀랍고 놀라게하는 데 사용 된 C ++을 기억합니다.


답변

표준 답변은 예외가 규칙적이지 않으며 예외적 인 경우에 사용해야한다는 것입니다.

나에게 중요한 한 가지 이유는 try-catch내가 유지 관리하거나 디버깅하는 소프트웨어에서 제어 구조를 읽을 때 원래 코더가 if-else구조 대신 예외 처리를 사용한 이유를 찾으려고하기 때문입니다. 그리고 나는 좋은 대답을 찾을 것으로 기대합니다.

컴퓨터뿐만 아니라 다른 코더에도 코드를 작성해야합니다. 기계가 신경 쓰지 않기 때문에 버릴 수없는 예외 처리기와 관련된 의미가 있습니다.


답변

성능은 어떻습니까? .NET 웹 앱을로드 테스트하는 동안 일반적으로 발생하는 예외를 수정하고 그 수를 500 명으로 늘릴 때까지 웹 서버 당 100 명의 시뮬레이션 사용자를 처리했습니다.