[c#] Debug.Assert 대 예외 발생

어설 션을 사용하는 방법과시기에 대한 많은 기사 (및 StackOverflow에 게시 된 몇 가지 다른 유사한 질문)를 읽었으며 이를 잘 이해했습니다. 그러나 여전히 나는 Debug.Assert평범한 예외를 던지는 대신 어떤 종류의 동기가 나를 사용 해야하는지 이해하지 못합니다 . 내 말은 .NET에서 실패한 어설 션에 대한 기본 응답은 “세계를 중지”하고 사용자에게 메시지 상자를 표시하는 것입니다. 이런 종류의 동작은 수정 될 수 있지만 그렇게하는 것이 매우 성 가시고 중복 적이라는 것을 알지만 대신 적절한 예외를 던질 수 있습니다. 이렇게하면 예외가 발생하기 직전에 응용 프로그램 로그에 오류를 쉽게 기록 할 수 있으며 응용 프로그램이 반드시 정지되는 것은 아닙니다.

그렇다면 왜 내가 Debug.Assert평범한 예외 대신 사용해야 할까요? 어설 션을 안되는 곳에 배치하는 것은 모든 종류의 “원치 않는 동작”을 유발할 수 있기 때문에 제 관점에서 예외를 던지는 대신 어설 션을 사용하여 아무것도 얻지 못합니다. 동의하십니까, 아니면 여기에 뭔가 빠졌나요?

참고 : “이론상”(디버그 대 릴리스, 사용 패턴 등)의 차이점이 무엇인지 완전히 이해하지만, 내가보기에 어설 션을 수행하는 대신 예외를 던지는 것이 낫습니다. 프로덕션 릴리스에서 버그가 발견되면 여전히 “어설 션”이 실패하기를 원하므로 (결국 “오버 헤드”가 엄청나게 작습니다) 대신 예외를 던지는 것이 좋습니다.


편집 : 어설 션이 실패하면 응용 프로그램이 일종의 손상되고 예상치 못한 상태에 들어 갔음을 의미합니다. 그렇다면 왜 계속 실행하고 싶습니까? 응용 프로그램이 디버그 또는 릴리스 버전에서 실행되는지는 중요하지 않습니다. 둘 다 똑같아요



답변

당신의 추론이 타당 하다는 데 동의하지만 , 주장이 예기치 않게 위반되는 경우 던짐으로써 실행을 중단하는 것이 합리적입니다. 개인적으로 주장 대신 예외를 사용하지 않을 것입니다. 그 이유는 다음과 같습니다.

다른 사람들이 말했듯이 주장은 불가능하다고 주장되는 상황이 발생하면 개발자에게이를 알리는 방식으로 불가능한 상황을 문서화 합니다 . 대조적으로, 예외는 예외적이거나 가능성이 없거나 오류가있는 상황에 대한 제어 흐름 메커니즘을 제공하지만 불가능한 상황은 아닙니다. 저에게있어 주요 차이점은 다음과 같습니다.

  • 주어진 throw 문을 실행하는 테스트 케이스를 생성하는 것이 항상 가능해야합니다. 이러한 테스트 케이스를 생성 할 수없는 경우 프로그램에 실행되지 않는 코드 경로가있는 것이므로 데드 코드로 제거해야합니다.

  • 어설 션을 발생시키는 테스트 케이스를 생성하는 것은 절대 불가능합니다. 어설 션이 발생하면 코드가 잘못되었거나 어설 션이 잘못되었습니다. 어느 쪽이든 코드에서 무언가를 변경해야합니다.

그것이 내가 주장을 예외로 바꾸지 않는 이유입니다. 어설 션이 실제로 실행될 수없는 경우 예외대체하면 프로그램에 테스트 할 수없는 코드 경로가 있음을 의미합니다 . 테스트 할 수없는 코드 경로를 싫어합니다.


답변

어설 션은 프로그래머의 세계 이해를 확인하는 데 사용됩니다. 어설 션은 프로그래머가 잘못한 경우에만 실패해야합니다. 예를 들어 어설 션을 사용하여 사용자 입력을 확인하지 마십시오.

“발생할 수없는”조건에 대한 테스트를 어설 션합니다. 예외는 “발생하지 않아야하지만 발생하는”조건에 대한 것입니다.

어설 션은 빌드시 (또는 런타임에) 동작을 변경할 수 있기 때문에 유용합니다. 예를 들어, 종종 릴리스 빌드에서 어설 션은 불필요한 오버 헤드를 유발하기 때문에 확인조차되지 않습니다. 이것은 또한주의해야 할 사항입니다. 테스트가 실행되지 않을 수도 있습니다.

어설 션 대신 예외를 사용하면 값이 손실됩니다.

  1. 예외를 테스트하고 던지는 것은 적어도 두 줄이고 어설 션은 하나뿐이기 때문에 코드는 더 장황합니다.

  2. 테스트 및 throw 코드는 항상 실행되지만 어설 션은 컴파일되지 않습니다.

  3. 어설 션은 확인하고 던지는 제품 코드와 다른 의미를 갖기 때문에 다른 개발자와의 의사 소통을 잃게됩니다. 실제로 프로그래밍 어설 션을 테스트하는 경우 어설 션을 사용하십시오.

추가 정보 : http://nedbatchelder.com/text/assert.html


답변

편집 :
게시물에서 작성한 편집 / 노트에 대한 응답으로 : 예외를 사용하는 것이 달성하려는 유형에 대한 주장을 사용하는 것보다 사용하는 것이 옳은 것처럼 들립니다. 나는 당신이 똑같은 목적을 달성하기 위해 예외와 ​​주장을 고려하고 있으므로 어떤 것을 사용하기에 ‘올바른’지 파악하려고 노력하고 있다는 것이 정신적 걸림돌이라고 생각합니다. 어설 션과 예외를 사용할 수있는 방법에 약간의 겹침이있을 수 있지만 동일한 문제에 대한 서로 다른 솔루션이라는 점을 혼동하지 마십시오. 그렇지 않습니다. 주장과 예외에는 각각 고유 한 목적, 강점 및 약점이 있습니다.

나는 내 자신의 말로 답을 입력하려고했지만 이것은 내가 가진 것보다 더 나은 개념을 정의합니다.

C # 스테이션 : 어설 션

assert 문을 사용하면 런타임에 프로그램 논리 오류를 효과적으로 포착 할 수 있지만 프로덕션 코드에서 쉽게 필터링 할 수 있습니다. 개발이 완료되면 컴파일 중에 전 처리기 기호 NDEBUG [모든 어설 션을 비활성화 함]을 정의하여 코딩 오류에 대한 이러한 중복 테스트의 런타임 비용을 제거 할 수 있습니다. 그러나 어설 션 자체에 배치 된 코드는 프로덕션 버전에서 생략된다는 점을 기억하십시오.

어설 션은 다음이 모두 유지되는 경우에만 조건을 테스트하는 데 가장 적합합니다.

* the condition should never be false if the code is correct,
* the condition is not so trivial so as to obviously be always true, and
* the condition is in some sense internal to a body of software.

어설 션은 소프트웨어가 정상적으로 작동하는 동안 발생하는 상황을 감지하는 데 거의 사용되지 않아야합니다.
예를 들어 일반적으로 사용자 입력의 오류를 확인하는 데 어설 션을 사용해서는 안됩니다. 그러나 호출자가 이미 사용자 입력을 확인했는지 확인하기 위해 어설 션을 사용하는 것이 합리적 일 수 있습니다.

기본적으로 프로덕션 애플리케이션에서 포착 / 처리해야하는 항목에는 예외를 사용하고, 개발에는 유용하지만 프로덕션에서는 해제되는 논리적 검사를 수행하기 위해 어설 션을 사용합니다.


답변

나는 (기고 된) 실용적인 예가 차이점을 밝히는 데 도움이 될 것이라고 생각합니다.

( MoreLinq의 Batch 확장 에서 수정 됨 )

// 'public facing' method
public int DoSomething(List<string> stuff, object doohickey, int limit) {

    // validate user input and report problems externally with exceptions

    if(stuff == null) throw new ArgumentNullException("stuff");
    if(doohickey == null) throw new ArgumentNullException("doohickey");
    if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0");

    return DoSomethingImpl(stuff, doohickey, limit);
}

// 'developer only' method
private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) {

    // validate input that should only come from other programming methods
    // which we have control over (e.g. we already validated user input in
    // the calling method above), so anything using this method shouldn't
    // need to report problems externally, and compilation mode can remove
    // this "unnecessary" check from production

    Debug.Assert(stuff != null);
    Debug.Assert(doohickey != null);
    Debug.Assert(limit > 0);

    /* now do the actual work... */
}

따라서 Eric Lippert 등이 말했듯이, 여러분 (개발자)이 실수로 다른 곳에서 잘못 사용한 경우 대비하여 정확할 것으로 예상되는 내용 만 주장 하므로 코드를 수정할 수 있습니다. 예를 들어 사용자 입력 과 같이 들어오는 것을 제어 할 수 없거나 예상 할 수없는 경우 기본적으로 예외를 발생 시켜 잘못된 데이터를 제공 한 모든 것이 적절하게 응답 할 수 있습니다 (예 : 사용자).


답변

Code Complete의 또 다른 너겟 :

“어설 션은 가정이 사실이 아닌 경우 큰 소리로 불평하는 함수 또는 매크로입니다. 코드에서 만들어진 가정을 문서화하고 예기치 않은 조건을 제거하려면 어설 션을 사용하십시오. …

“개발 중 단언은 모순 된 가정, 예상치 못한 조건, 루틴에 전달 된 잘못된 값 등을 제거합니다.”

그는 계속해서 주장해야 할 것과해서는 안되는 것에 대한 몇 가지 지침을 추가합니다.

반면에 예외 :

“예외 처리를 사용하여 예상치 못한 경우에주의를 기울이십시오. 예외적 인 경우는 개발 중에 명확하게 만들고 프로덕션 코드가 실행 중일 때 복구 할 수있는 방식으로 처리해야합니다.”

이 책이 없다면 구입해야합니다.


답변

Debug.Assert는 기본적으로 디버그 빌드에서만 작동하므로 릴리스 빌드에서 예기치 않은 잘못된 동작을 포착하려면 예외를 사용하거나 프로젝트 속성에서 디버그 상수를 설정해야합니다 ( 일반적으로 좋은 생각이 아닙니다.)


답변

가능하지만 일어나서 는 안되는 일에 대해 단정을 사용하십시오 (불가능하다면 왜 단언을 하시겠습니까?).

를 사용하는 경우처럼 들리지 Exception않습니까? 대신 어설 션을 사용하는 이유는 무엇 Exception입니까?

어설 션의 매개 변수가 거짓이되는 것을 막는 어설 션 전에 호출되는 코드가 있어야하기 때문입니다.

일반적으로 이전에 코드가 없습니다. Exception 으로 던지지 않을 것이라는 것을 보장하는 .

Debug.Assert()찌르기에서 컴파일 된 것이 좋은 이유는 무엇 입니까? 디버그에서 그것에 대해 알고 싶다면 prod에서 알고 싶지 않습니까?

일단 Debug.Assert(false)상황 을 찾으면 Debug.Assert(false)다시 발생하지 않도록 코드를 작성 하기 때문에 개발 중에 만 원합니다 . 개발이 완료되면 Debug.Assert(false)상황을 발견 하고 수정 했다고 가정하면 Debug.Assert()이제 중복되므로 안전하게 컴파일 할 수 있습니다.