예외를 규칙적인 제어 흐름으로 사용하지 않는 이유는 무엇입니까?
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“정기 프로그램 흐름에는 절대로 사용하지 마십시오”라는 반응은 그 반응의 강도가 정당화 될 수 있기 때문에 제대로 구축되지 않은 것 같습니다.아니면 내가 착각합니까?나는 모든 종류의 특별한 경우를 다루는 다른 게시물을 읽었지만, 내 요점은 둘 다라면 아무런 문제가 없다는 것입니다.
- 명확한
- 방법의 계약을 존중하십시오
날 쏴
정상적인 작동 과정에서 초당 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은) 나에게 경우 (콘래드 등의) 어떤보다 더 분명하다. 이러한 종류의 프로그래밍이 예상되지 않는다는 사실은 순전히 전통적이며 실제로는 여전히 관련이 있습니다. 어쩌면 내 예에서 if가 더 명확 할 것입니다.그러나 그 문제에 대한 DivisionByZero와 FileNotFound는 if보다 명확합니다.물론 성능이 떨어지고 초당 zillion 시간이 필요한 경우 당연히 피해야하지만 과도한 디자인을 피해야 할 이유를 읽지 못했습니다.최소한의 놀랍게도의 원칙은 여기에 순환 추론의 위험이 있습니다 : 전체 공동체가 나쁜 디자인을 사용한다고 가정하면,이 디자인은 예상 될 것입니다! 그러므로 원칙은 성배가 될 수 없으며 신중하게 고려해야합니다.
정상적인 상황에 대한 예외, 비정상적인 (즉, 예외적 인) 상황을 어떻게 찾습니까?
많은 반응에서 sth. 이처럼 여물통이 빛납니다. 그냥 잡아라 귀하의 방법은 명확하고 잘 문서화되어 있으며 계약을 존중해야합니다. 인정해야 할 질문이 없습니다.모든 예외에 대한 디버깅 : 동일합니다. 예외를 사용하지 않는 디자인이 일반적이기 때문에 때때로 수행됩니다. 내 질문은 : 왜 처음에 공통입니까?
예외 전에 C에서 거기
setjmp
및
longjmp
그는 스택 프레임의 유사한 풀림을 달성하기 위해 사용될 수있다.그런 다음 동일한 구성에 "예외"라는 이름이 지정되었습니다. 그리고 대부분의 답변은 예외적 인 조건에서 예외가 사용되어야한다고 주장 하면서이 이름의 의미에 의존하여 사용법에 대해 논쟁합니다. 그것은 원래의 의도가 아니 었습니다
longjmp
. 많은 스택 프레임에서 제어 흐름을 중단해야하는 상황이있었습니다.동일한 스택 프레임 내에서도 예외를 사용할 수 있다는 점에서 예외가 약간 더 일반적입니다. 이것은
goto
내가 틀렸다고 믿는 것과의 유사점을 높 입니다. Gotos는 (되도록하고 단단히 결합 된 쌍인
setjmp
하고
longjmp
). 느슨하게 연결된 발행 / 구독은 훨씬 더 깔끔합니다. 따라서 동일한 스택 프레임 내에서 사용하는 것은
goto
s 를 사용하는 것과 거의 동일하지 않습니다 .세 번째 혼란의 원인은 확인 된 예외인지 확인되지 않은 예외와 관련이 있습니다. 물론, 검사되지 않은 예외는 제어 흐름 및 기타 많은 것들에 사용하기에 특히 끔찍한 것 같습니다.모든 빅토리아 식 행 아웃을 극복하고 조금만 생활하면 확인 된 예외는 제어 흐름에 적합합니다.내가 가장 좋아하는 사용법은 원하는
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 명의 시뮬레이션 사용자를 처리했습니다.
Josh Bloch는 Effective Java에서이 주제를 광범위하게 다루고 있습니다. 그의 제안은 밝고 .Net에도 적용되어야합니다 (세부 사항 제외).특히 예외적 인 상황에서는 예외를 사용해야합니다. 그 이유는 주로 사용성 관련입니다. 주어진 방법을 최대로 사용할 수 있으려면 입력 및 출력 조건을 최대로 제한해야합니다.예를 들어, 두 번째 방법은 첫 번째 방법보다 사용하기 쉽습니다.
/**
* Adds two positive numbers.
*
* @param addend1 greater than zero
* @param addend2 greater than zero
* @throws AdditionException if addend1 or addend2 is less than or equal to zero
*/
int addPositiveNumbers(int addend1, int addend2) throws AdditionException{
if( addend1 <= 0 ){
throw new AdditionException("addend1 is <= 0");
}
else if( addend2 <= 0 ){
throw new AdditionException("addend2 is <= 0");
}
return addend1 + addend2;
}
/**
* Adds two positive numbers.
*
* @param addend1 greater than zero
* @param addend2 greater than zero
*/
public int addPositiveNumbers(int addend1, int addend2) {
if( addend1 <= 0 ){
throw new IllegalArgumentException("addend1 is <= 0");
}
else if( addend2 <= 0 ){
throw new IllegalArgumentException("addend2 is <= 0");
}
return addend1 + addend2;
}
두 경우 모두 호출자가 API를 적절하게 사용하고 있는지 확인해야합니다. 그러나 두 번째 경우에는 (암시 적으로) 필요합니다. 사용자가 javadoc을 읽지 않았지만 다음과 같은 소프트 예외가 계속 발생합니다.
- 문서화 할 필요가 없습니다.
- 단위 테스트 전략이 얼마나 공격적인 지에 따라 테스트 할 필요가 없습니다.
- 발신자가 세 가지 사용 사례를 처리하도록 요구하지 않습니다.
지상 수준의 포인트는 예외가해야한다는 것입니다
하지
당신은 당신의 API하지만 발신자의 API뿐만 아니라 복잡했습니다 주로하기 때문에, 반환 코드로 사용.올바른 일을하는 것은 물론 비용이 든다. 비용은 모든 사람이 문서를 읽고 따라야한다는 것을 이해해야한다는 것입니다. 잘만되면 그것이 사실이다.
흐름 제어에 예외를 사용할 수 있다고 생각합니다. 그러나이 기술의 단점이 있습니다. 예외를 만들려면 스택 추적을 만들어야하므로 비용이 많이 듭니다. 따라서 예외적 인 상황을 알리는 것보다 예외를 더 자주 사용하려면 스택 추적을 구축해도 성능에 부정적인 영향을 미치지 않아야합니다.예외를 만드는 비용을 줄이는 가장 좋은 방법은 다음과 같이 fillInStackTrace () 메서드를 재정의하는 것입니다.
public Throwable fillInStackTrace() { return this; }
이러한 예외에는 스택 추적이 채워지지 않습니다.
나는 당신이 인용 한 코드에서 프로그램 흐름을 어떻게 제어하고 있는지 알지 못합니다. ArgumentOutOfRange 예외 외에 다른 예외는 표시되지 않습니다. (따라서 두 번째 캐치 절은 절대로 맞지 않습니다). 당신이하고있는 일은 if 문을 모방하기 위해 비용이 많이 드는 던지기를 사용하는 것입니다.또한 흐름 제어를 수행하기 위해 다른 곳에서 예외를 포착하기 위해 예외를 던지는 더 불길한 작업을 수행하지 않습니다. 실제로 예외적 인 경우를 처리하고 있습니다.
코드를 읽기가 어렵 기 때문에 디버깅에 문제가있을 수 있으며 오랜 시간 후에 버그를 수정하면 새로운 버그가 발생하며, 리소스 및 시간 측면에서 더 비싸며, 코드를 디버깅하고 디버거는 모든 예외가 발생하면 정지합니다.)
언급 된 이유 외에도 플로우 제어에 예외를 사용하지 않는 한 가지 이유는 디버깅 프로세스를 크게 복잡하게 만들 수 있기 때문입니다. 예를 들어, VS에서 버그를 추적하려고 할 때 일반적으로 "모든 예외에서 중단"을 설정합니다. 흐름 제어에 예외를 사용하는 경우 정기적으로 디버거를 중단하고 실제 문제가 발생할 때까지 예외가 아닌 예외를 무시해야합니다. 이것은 누군가를 화나게 할 것입니다!
내
설명 된 모범 사례는 다음과 같습니다 .
- 소프트웨어에서 예기치 않은 상황을 알리려면 예외를 던지 십시오.
- 입력 유효성 검사에 반환 값을 사용하십시오 .
- 라이브러리에서 발생하는 예외를 처리하는 방법을 알고 있다면 가능한 가장 낮은 수준에서 잡아라 .
- 예기치 않은 예외가 발생하면 현재 작업을 완전히 삭제하십시오. 당신이 그들을 다루는 방법을 아는 척하지 마십시오 .
일부 계산을 수행하는 방법이 있다고 가정합니다. 유효성을 검사 한 다음 0보다 큰 숫자를 반환하려면 많은 입력 매개 변수가 있습니다.유효성 검사 오류를 알리기 위해 반환 값을 사용하면 간단합니다. method가 0보다 작은 수를 반환하면 오류가 발생합니다.
어떤
매개 변수가 유효성을 검사하지 않는지 어떻게 알 수 있습니까?나는 C 일에서 많은 함수가 다음과 같은 오류 코드를 반환했는지 기억합니다.
-1 - x lesser then MinX
-2 - x greater then MaxX
-3 - y lesser then MinY
기타읽기가 불가능하고 예외를 던지고 잡는가?
You can use a hammer's claw to turn a screw, just like you can use exceptions for control flow. That doesn't mean it is the intended usage of the feature. The if
statement expresses conditions, whose intended usage is controlling flow.
If you are using a feature in an unintended way while choosing to not use the feature designed for that purpose, there will be an associated cost. In this case, clarity and performance suffer for no real added value. What does using exceptions buy you over the widely-accepted if
statement?
Said another way: just because you can doesn't mean you should.
다른 사람들이 많이 언급했듯이,
제어 흐름 전용 목적으로 예외를 과도하게 사용하는 것을 금지합니다. 반면에, 어떤 규칙이 100 % 정확하지 않으며, 예외가 "딱 맞는 도구"입니다 이러한 경우 항상있다 - 같은 많은
goto
자체, 그런데,의 형태로 선박
break
및
continue
자바 같은 언어는 종종 자주 피할 수있는 것은 아니지만 크게 중첩 된 루프를 뛰어 넘는 완벽한 방법입니다.다음 블로그 게시물은
로컬
이 아닌 경우에 다소 복잡하지만 흥미로운 사용 사례를 설명합니다
ControlFlowException
.
또한
jOOQ (Java 용 SQL 추상화 라이브러리) 내부
에서 이러한 "예외"가 일부 "희귀 한"조건이 충족 될 때 초기에 SQL 렌더링 프로세스를 중단하는 데 사용되는 방법을 설명합니다 .
이러한 조건의 예는 다음과 같습니다.
- 바인드 값이 너무 많습니다. 일부 데이터베이스는 SQL 문에서 임의의 수의 바인드 값을 지원하지 않습니다 (SQLite : 999, Ingres 10.1.0 : 1024, Sybase ASE 15.5 : 2000, SQL Server 2008 : 2100). 이 경우 jOOQ는 SQL 렌더링 단계를 중단하고 인라인 된 바인드 값으로 SQL 문을 다시 렌더링합니다. 예:
매번 계산하기 위해 쿼리 AST에서 바인드 값을 명시 적으로 추출하면이 문제가 발생하지 않는 쿼리의 99.9 %에 대해 귀중한 CPU주기가 낭비됩니다.// Pseudo-code attaching a "handler" that will // abort query rendering once the maximum number // of bind values was exceeded: context.attachBindValueCounter(); String sql; try { // In most cases, this will succeed: sql = query.render(); } catch (ReRenderWithInlinedVariables e) { sql = query.renderWithInlinedBindValues(); }
- 일부 로직은 "부분적으로"만 실행하려는 API를 통해 간접적으로 만 사용할 수 있습니다. 이
메소드는 의 내부 플래그 에 따라
orINSERT
문을 생성합니다UPDATE
. "외부"에서 우리는 어떤 종류의 로직Record
(예 : 낙관적 잠금, 이벤트 리스너 처리 등) 이 포함되어 있는지 알지 못 하므로 배치 레코드에 여러 레코드를 저장할 때 해당 로직을 반복하고 싶지 않습니다. 여기서는store()
SQL 문만 생성하고 실제로 실행하지는 않습니다. 예:store()
// Pseudo-code attaching a "handler" that will // prevent query execution and throw exceptions // instead: context.attachQueryCollector(); // Collect the SQL for every store operation for (int i = 0; i < records.length; i++) { try { records[i].store(); } // The attached handler will result in this // exception being thrown rather than actually // storing records to the database catch (QueryCollectorException e) { // The exception is thrown after the rendered // SQL statement is available queries.add(e.query()); } }
선택적으로 SQL을 실행store()
하지 않도록
사용자 정의 할 수있는 "재사용 가능한"API로 로직을 외부화 한 경우 유지 관리하기 어렵고 재사용하기 어려운 API를 작성하려고합니다.
결론
In essence, our usage of these non-local goto
s is just along the lines of what [Mason Wheeler][5] said in his answer:
"I just encountered a situation that I cannot deal with properly at this point, because I don't have enough context to handle it, but the routine that called me (or something further up the call stack) ought to know how to handle it."
Both usages of ControlFlowExceptions
were rather easy to implement compared to their alternatives, allowing us to reuse a wide range of logic without refactoring it out of the relevant internals.
But the feeling of this being a bit of a surprise to future maintainers remains. The code feels rather delicate and while it was the right choice in this case, we'd always prefer not to use exceptions for local control flow, where it is easy to avoid using ordinary branching through if - else
.
Typically there is nothing wrong, per se, with handling an exception at a low level. An exception IS a valid message that provides a lot of detail for why an operation cannot be performed. And if you can handle it, you ought to.
In general if you know there is a high probability of failure that you can check for... you should do the check... i.e. if(obj != null) obj.method()
In your case, i'm not familiar enough with the C# library to know if date time has an easy way to check whether a timestamp is out of bounds. If it does, just call if(.isvalid(ts)) otherwise your code is basically fine.
So, basically it comes down to whichever way creates cleaner code... if the operation to guard against an expected exception is more complex than just handling the exception; than you have my permission to handle the exception instead of creating complex guards everywhere.
If you are using exception handlers for control flow, you are being too general and lazy. As someone else mentioned, you know something happened if you are handling processing in the handler, but what exactly? Essentially you are using the exception for an else statement, if you are using it for control flow.
If you don't know what possible state could occur, then you can use an exception handler for unexpected states, for example when you have to use a third-party library, or you have to catch everything in the UI to show a nice error message and log the exception.
However, if you do know what might go wrong, and you don't put an if statement or something to check for it, then you are just being lazy. Allowing the exception handler to be the catch-all for stuff you know could happen is lazy, and it will come back to haunt you later, because you will be trying to fix a situation in your exception handler based on a possibly false assumption.
If you put logic in your exception handler to determine what exactly happened, then you would be quite stupid for not putting that logic inside the try block.
Exception handlers are the last resort, for when you run out of ideas/ways to stop something from going wrong, or things are beyond your ability to control. Like, the server is down and times out and you can't prevent that exception from being thrown.
Finally, having all the checks done up front shows what you know or expect will occur and makes it explicit. Code should be clear in intent. What would you rather read?
You might be interested in having a look at Common Lisp's condition system which is a sort of generalization of exceptions done right. Because you can unwind the stack or not in a controlled way, you get "restarts" as well, which are extremely handy.
This doesn't have anything much to do with best practices in other languages, but it shows you what can be done with some design thought in (roughly) the direction you are thinking of.
Of course there are still performance considerations if you're bouncing up and down the stack like a yo-yo, but it's a much more general idea than "oh crap, lets bail" kind of approach that most catch/throw exception systems embody.
I don't think there is anything wrong with using Exceptions for flow-control. Exceptions are somewhat similar to continuations and in statically typed languages, Exceptions are more powerful than continuations, so, if you need continuations but your language doesn't have them, you can use Exceptions to implement them.
Well, actually, if you need continuations and your language doesn't have them, you chose the wrong language and you should rather be using a different one. But sometimes you don't have a choice: client-side web programming is the prime example – there's just no way to get around JavaScript.
An example: Microsoft Volta is a project to allow writing web applications in straight-forward .NET, and let the framework take care of figuring out which bits need to run where. One consequence of this is that Volta needs to be able to compile CIL to JavaScript, so that you can run code on the client. However, there is a problem: .NET has multithreading, JavaScript doesn't. So, Volta implements continuations in JavaScript using JavaScript Exceptions, then implements .NET Threads using those continuations. That way, Volta applications that use threads can be compiled to run in an unmodified browser – no Silverlight needed.
One aesthetic reason:
A try always comes with a catch, whereas an if doesn't have to come with an else.
if (PerformCheckSucceeded())
DoSomething();
With try/catch, it becomes much more verbose.
try
{
PerformCheckSucceeded();
DoSomething();
}
catch
{
}
That's 6 lines of code too many.
But you won't always know what happens in the Method/s that you call. You won't know exactly where the exception was thrown. Without examining the exception object in greater detail....
I feel that there is nothing wrong with your example. On the contrary, it would be a sin to ignore the exception thrown by the called function.
In the JVM, throwing an exception is not that expensive, only creating the exception with new xyzException(...), because the latter involves a stack walk. So if you have some exceptions created in advance, you may throw them many times without costs. Of course, this way you can't pass data along with the exception, but I think that is a bad thing to do anyway.
There are a few general mechanisms via which a language could allow for a method to exit without returning a value and unwind to the next "catch" block:
-
Have the method examine the stack frame to determine the call site, and use the metadata for the call site to find either information about a
try
block within the calling method, or the location where the calling method stored the address of its caller; in the latter situation, examine metadata for the caller's caller to determine in the same fashion as the immediate caller, repeating until one finds atry
block or the stack is empty. This approach adds very little overhead to the no-exception case (it does preclude some optimizations) but is expensive when an exception occurs. -
Have the method return a "hidden" flag which distinguishes a normal return from an exception, and have the caller check that flag and branch to an "exception" routine if it's set. This routine adds 1-2 instructions to the no-exception case, but relatively little overhead when an exception occurs.
-
Have the caller place exception-handling information or code at a fixed address relative to the stacked return address. For example, with the ARM, instead of using the instruction "BL subroutine", one could use the sequence:
adr lr,next_instr b subroutine b handle_exception next_instr:
To exit normally, the subroutine would simply do bx lr
or pop {pc}
; in case of an abnormal exit, the subroutine would either subtract 4 from LR before performing the return or use sub lr,#4,pc
(depending upon the ARM variation, execution mode, etc.) This approach will malfunction very badly if the caller is not designed to accommodate it.
A language or framework which uses checked exceptions might benefit from having those handled with a mechanism like #2 or #3 above, while unchecked exceptions are handled using #1. Although the implementation of checked exceptions in Java is rather nuisancesome, they would not be a bad concept if there were a means by which a call site could say, essentially, "This method is declared as throwing XX, but I don't expect it ever to do so; if it does, rethrow as an "unchecked" exception. In a framework where checked exceptions were handled in such fashion, they could be an effective means of flow control for things like parsing methods which in some contexts may have a high likelihood of failure, but where failure should return fundamentally different information than success. I'm unaware of any frameworks that use such a pattern, however. Instead, the more common pattern is to use the first approach above (minimal cost for the no-exception case, but high cost when exceptions are thrown) for all exceptions.
참고URL : https://stackoverflow.com/questions/729379/why-not-use-exceptions-as-regular-flow-of-control
'programing' 카테고리의 다른 글
jquery에서 모든 확인란을 선택하십시오. (0) | 2020.05.17 |
---|---|
IRB 콘솔을 지우려면 어떻게합니까? (0) | 2020.05.17 |
C ++에서 문자열의 문자 발생 수 계산 (0) | 2020.05.17 |
스팸 봇에서 이메일을 숨기는 효과적인 방법 (0) | 2020.05.17 |
대문자 앞에 공백을 추가하십시오 (0) | 2020.05.17 |