program tip

Debug.Assert 대 예외 발생

radiobox 2020. 9. 7. 07:59
반응형

Debug.Assert 대 예외 발생


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

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

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


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


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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

C # 스테이션 : 어설 션

The use of assert statements can be an effective way to catch program logic errors at runtime, and yet they are easily filtered out of production code. Once development is complete, the runtime cost of these redundant tests for coding errors can be eliminated simply by defining the preprocessor symbol NDEBUG [which disables all assertions] during compilation. Be sure, however, to remember that code placed in the assert itself will be omitted in the production version.

An assertion is best used to test a condition only when all of the following hold:

* 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.

Assertions should almost never be used to detect situations that arise during software's normal operation. For example, usually assertions should not be used to check for errors in a user's input. It may, however, make sense to use assertions to verify that a caller has already checked a user's input.

Basically, use exceptions for things that need to be caught/dealt with in a production application, use assertions to perform logical checks that will be useful for development but turned off in production.


I think a (contrived) practical example may help illuminate the difference:

(adapted from MoreLinq's Batch extension)

// '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... */
}

So as Eric Lippert et al have said, you only assert stuff that you expect to be correct, just in case you (the developer) accidentally used it wrong somewhere else, so you can fix your code. You basically throw exceptions when you have no control over or cannot anticipate what comes in, e.g. for user input, so that whatever gave it bad data can respond appropriately (e.g. the user).


Another nugget from Code Complete:

"An assertion is a function or macro that complains loudly if an assumption isn't true. Use assertions to document assumptions made in code and to flush out unexpected conditions. ...

"During development, assertions flush out contradictory assumptions, unexpected conditions, bad values passed to routines, and so on."

He goes on to add some guidelines on what should and should not be asserted.

On the other hand, exceptions:

"Use exception handling to draw attention to unexpected cases. Exceptional cases should be handled in a way that makes them obvious during development and recoverable when production code is running."

If you don't have this book you should buy it.


Debug.Assert by default will only work in debug builds, so if you want to catch any sort of bad unexpected behavior in your release builds you'll need to use exceptions or turn the debug constant on in your project properties (which is considered in general not to be a good idea).


Use assertions for things which ARE possible but should not happen (if it were impossible, why would you put an assertion?).

Doesn't that sound like a case to use an Exception? Why would you use an assertion instead of an Exception?

Because there should be code that gets called before your assertion that would stop the assertion's parameter being false.

Usually there is no code before your Exception that guarantees that it won't be thrown.

Why is it good that Debug.Assert() is compiled away in prod? If you want to know about it in debug, wouldn't you want to know about it in prod?

You want it only during development, because once you find Debug.Assert(false) situations, you then write code to guarantee that Debug.Assert(false) doesn't happen again. Once development is done, assuming you've found the Debug.Assert(false) situations and fixed them, the Debug.Assert() can be safely compiled away as they are now redundant.


Suppose you are a member of a fairly large team and there are several people all working on the same general code base, including overlapping on classes. You may create a method that is called by several other methods, and to avoid lock contention you do not add a separate lock to it, but rather "assume" it was previously locked by the calling method with a specific lock. Such as, Debug.Assert(RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld); The other developers might overlook a comment that says the calling method must use the lock, but they cannot ignore this.

참고URL : https://stackoverflow.com/questions/1467568/debug-assert-vs-exception-throwing

반응형