언제 Debug.Assert ()를 사용해야합니까?


220

CS 전문가로 졸업 한 후 약 1 년 동안 전문 소프트웨어 엔지니어로 일했습니다. 나는 C ++과 C에서 한동안 주장에 대해 알고 있었지만 최근까지 C #과 .NET에 존재한다는 것을 전혀 몰랐다.

우리의 생산 코드에는 어떤 주장도 포함되어 있지 않으며 내 질문은 이것입니다 ...

프로덕션 코드에서 Asserts를 사용해야합니까? 그렇다면 언제 사용하는 것이 가장 적합합니까? 하는 것이 더 합리적일까요?

Debug.Assert(val != null);

또는

if ( val == null )
    throw new exception();

2
설정 한 이분법이 실마리입니다. 그것은 예외 코드와 주장 코드, 방어 코드 모두에 대한 문제가 아닙니다. 언제해야하는지 이해하려고합니다.
캐스퍼 레온 닐슨

5
한 번은 누군가 예외 또는 기타 충돌 방식이 "이것으로부터 현명하게 복구 할 수있는 방법이 없다"는 조건에 적합하고 추가로 "이것은 절대로 일어나지 않아야하는"조건에 적합하다는 제안을 읽었습니다. 그러나 어떤 현실적 환경이 전자를 만족시키지 않고 후자의 조건을 만족 시키는가? 어설 션이 프로덕션 환경에 머무르는 Python 배경에서 나는 프로덕션에서 일부 유효성 검사를 해제하는 Java / C # 접근 방식을 이해하지 못했습니다. 내가 실제로 볼 수있는 유일한 경우는 유효성 검사가 비싼 경우입니다.
Mark Amery


2
개인적으로 나는 공개 메소드에 대한 예외를 사용하고 개인 메소드에 대한 주장을 사용합니다.
Fred

답변:


230

에서 디버깅 마이크로 소프트 .NET 2.0 응용 프로그램 존 로빈스의 주장에 큰 부분을 가지고있다. 그의 요점은 다음과 같습니다.

  1. 자유롭게 주장하십시오. 너무 많은 어설 션을 가질 수 없습니다.
  2. 어설 션은 예외를 대체하지 않습니다. 코드에서 요구하는 사항은 예외입니다. 주장은 가정하는 것을 포함합니다.
  3. 잘 작성된 어설 션은 무슨 일이 있었는지와 예외 (예 : 예외)뿐만 아니라 그 이유를 알려줄 수 있습니다.
  4. 예외 메시지는 종종 암호가 될 수 있으므로 오류를 일으킨 컨텍스트를 다시 작성하기 위해 코드를 거꾸로 작업해야합니다. 어설 션은 오류가 발생한 시점의 프로그램 상태를 보존 할 수 있습니다.
  5. 어설 션은 문서로 두 배가되어 다른 개발자에게 코드가 의존하는 가정을 알려줍니다.
  6. 어설 션이 실패 할 때 나타나는 대화 상자를 사용하면 프로세스에 디버거를 연결할 수 있으므로 중단 점을 배치 한 것처럼 스택 주위를 찌를 수 있습니다.

추신 : Code Complete가 마음에 드 셨다면이 책을 참조하십시오. WinDBG 및 덤프 파일 사용에 대해 배우기 위해 구입했지만 전반부는 처음에 버그를 피하는 데 도움이되는 팁으로 가득합니다.


3
간결하고 유용한 요약은 +1입니다. 매우 직접 적용 가능합니다. 그러나 누락 된 주요 사항은 Trace.Assert와 Trace.Assert를 사용하는 시점입니다. 즉, 프로덕션 코드에서 원하지 않는 경우에 관한 것입니다.
Jon Coombs

2
JonCoombs는 "Trace.Assert vs. Trace.Assert"오타입니까?
thelem

1
@thelem 아마도 존은 의미 Debug.Assert대를 Trace.Assert. 후자는 릴리스 빌드 및 디버그 빌드에서 실행됩니다.
DavidRR

예외를 던지는 것보다 Debug.Assert를 선호하는 이유는 무엇입니까?
Barış Akkurt

86

Debug.Assert()불변성을 보장하기 위해 위생 검사를 원하는 코드의 모든 곳에 넣습니다 . 릴리스 빌드를 컴파일 할 때 (즉, DEBUG컴파일러 상수 없음 ) 호출 Debug.Assert()은 제거되어 성능에 영향을 미치지 않습니다.

를 호출하기 전에 여전히 예외를 처리해야합니다 Debug.Assert(). 어설 션은 개발하는 동안 모든 것이 예상대로 이루어 지도록합니다.


35
호출하기 전에 여전히 예외를 던지면 어설 션을 넣은 이유를 분명히 알 수 있습니까? 아니면 당신의 대답을 오해 했습니까?
Roman Starkov 2016 년

2
@romkyns 프로젝트를 릴리스 모드로 빌드하지 않으면 모든 유효성 검사 / 오류 검사가 사라 지기 때문에 여전히 포함해야합니다 .
Oscar Mederos

28
@Oscar 처음부터 어설 션을 사용하는 것이 요점이라고 생각했습니다. 그렇다면 OK. 그러면 예외를 앞에 두십시오. 왜 어설 션을 넣었습니까?
Roman Starkov

4
@ superjos : 동의하지 않습니다 : MacLeod의 답변에서 2 번 포인트는 실제로 주장과 예외가 필요하지만 같은 장소에는 필요하지 않다는 것을 나타냅니다. 변수에 NullRefEx를 던지고 Assert를 한 직후에는 쓸모가 없습니다 (어설 션 메소드는 어설 션의 전체 지점 인 대화 상자를 표시하지 않습니다). MacLeod의 의미는 어떤 곳에서는 예외가 필요하고 다른 곳에서는 Assert로 충분하다는 것입니다.
David

1
당신이 둘 다 필요합니다, 당신은해야합니다 : 그것은 다음에 당신과 다른 사람의 어쨌든 답 : 나는 나의 해석을 해석하는 지저분한 될 수 없는 예외를 넣어 전에 어설. 나는 "같은 장소에 있지 않다"는 의미를 확신하지 못한다. 다시 말하지만, 해석을 거부하면서, 나는 단지 내 생각 / 선호를 언급 할 것입니다 : 어떤 수술이 시작되기 전에 전제 조건을 점검하거나 수술 후 사후 조건을 점검하기 위해 하나 이상의 주장을합니다. 어설 션 외에도 어쨌든 문제가 발생하여 예외가 발생하는지 확인하십시오.
superjos

52

에서 코드 완성

8 방어 프로그래밍

8.2 주장

어설 션은 개발 중에 사용되는 코드 (일반적으로 루틴 또는 매크로)로 프로그램이 실행될 때 자체적으로 확인할 수 있습니다. 어설 션이 true이면 모든 것이 예상대로 작동한다는 의미입니다. False이면 코드에서 예기치 않은 오류가 감지되었음을 의미합니다. 예를 들어, 시스템에서 고객 정보 파일에 레코드가 50,000 개를 넘지 않는다고 가정하면 프로그램에 레코드 수가 50,000보다 작거나 같다는 주장이 프로그램에 포함될 수 있습니다. 레코드 수가 50,000보다 작거나 같은 한 어설 션은 자동입니다. 그러나 50,000 개가 넘는 레코드가 발견되면 프로그램에 오류가 있다고 크게 "어설트"합니다.

어설 션은 특히 크고 복잡한 프로그램과 높은 안정성의 프로그램에서 유용합니다. 이를 통해 프로그래머는 불일치 한 인터페이스 가정, 코드 수정시 발생하는 오류 등을보다 신속하게 제거 할 수 있습니다.

어설 션은 일반적으로 두 가지 인수, 즉 참이라고 가정하는 가정을 설명하는 부울 식과 그렇지 않은 경우 표시 할 메시지를받습니다.

(…)

일반적으로 사용자는 프로덕션 코드에서 어설 션 메시지를 보지 않기를 원합니다. 어설 션은 주로 개발 및 유지 관리 중에 사용됩니다. 어설 션은 일반적으로 개발시 코드로 컴파일되고 프로덕션 코드에서 컴파일됩니다. 개발 중에 어설 션은 모순 된 가정, 예기치 않은 조건, 잘못된 값이 루틴에 전달되는 등을 제거합니다. 생산하는 동안 어설 션이 시스템 성능을 저하시키지 않도록 코드에서 컴파일됩니다.


7
생산 환경에서 고객 정보 파일에 50,000 개가 넘는 레코드가 포함되어 있으면 어떻게됩니까? 어설 션이 프로덕션 코드에서 컴파일되고이 상황이 다르게 처리되지 않으면이 철자가 문제가되지 않습니까?
DavidRR

1
@DavidRR 그렇습니다. 그러나 프로덕션에서 문제를 알리고이 코드를 잘 모르는 일부 개발자가 문제를 디버깅하면 어설 션이 실패하고 개발자는 시스템이 의도 한대로 사용되지 않음을 즉시 알게됩니다.
Marc

48

FWIW ... 공개 메소드가 if () { throw; }패턴을 사용하여 메소드가 올바르게 호출되도록하는 경향이 있습니다 . 내 개인적인 방법은 사용하는 경향이 있습니다Debug.Assert() .

아이디어는 내 개인 메서드를 사용하여 제어하는 ​​방법이므로 잘못된 매개 변수로 자체 개인 메서드 중 하나를 호출하기 시작하면 어딘가에서 내 자신의 가정을 어겼습니다. 그 상태로. 프로덕션 환경에서 이러한 개인 주장은 내부 상태를 유효하고 일관되게 유지해야하므로 이상적으로 불필요한 작업이어야합니다. 런타임에 누구나 호출 할 수있는 공용 메소드에 제공된 매개 변수와 대조 : 여전히 예외를 발생시켜 매개 변수 제약 조건을 적용해야합니다.

또한 런타임에 무언가 작동하지 않는 경우 (네트워크 오류, 데이터 액세스 오류, 타사 서비스에서 검색 한 잘못된 데이터 등) 내 개인 메서드는 여전히 예외를 throw 할 수 있습니다. 내 주장은 그 물체의 상태에 대한 내 자신의 가정을 깨뜨리지 않도록하기 위해 거기에 있습니다.


3
이것은 모범 사례에 대한 매우 명확한 설명이며 질문에 대한 합리적인 답변을 제공합니다.
캐스퍼 레온 닐슨

42

어설 션을 사용하여 개발자 가정을 확인하고 예외를 환경 가정을 확인하십시오.


31

내가 당신이라면 내가 할 것입니다 :

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

또는 반복적 인 상태 점검을 피하기 위해

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}

5
이것이 어떻게 문제를 해결합니까? 이것으로 debug.assert는 무의미해진다.
Quibblesome

43
아닙니다. 예외가 발생하기 직전에 코드로 침입합니다. 코드 어딘가에 시도 / catch가 있으면 예외를 알지 못할 수도 있습니다!
Mark Ingram

2
+1 나는 사람들이 아무 것도하지 않고 단순히 예외를 시도 / 잡기 위해 버그를 추적하는 것이 문제인 많은 문제를 겪었다
dance2die

5
나는 당신이 이것을하고 싶을 수도있는 경우가 있다고 생각하지만, 일반적인 예외를 잡아서는 안됩니다!
Casebash

8
@MarkIngram -1은 대답에, +1은 당신의 의견에 대답합니다. 이것은 특정 상황에 대한 좋은 트릭이지만 모든 유효성 검사에 일반적으로 기괴한 것처럼 보입니다.
Mark Amery

24

프로덕션 코드 (예 : 릴리스 빌드)에 Asserts를 원하는 경우 Debug.Assert 대신 Trace.Assert를 사용할 수 있습니다.

이것은 물론 프로덕션 실행 파일에 오버 헤드를 추가합니다.

또한 응용 프로그램이 사용자 인터페이스 모드에서 실행중인 경우 어설 션 대화 상자가 기본적으로 표시되어 사용자에게 약간의 혼란을 줄 수 있습니다.

DefaultTraceListener를 제거하여이 동작을 무시할 수 있습니다. MSDN의 Trace.Listeners 설명서를 참조하십시오.

요약해서 말하자면,

  • Debug.Assert를 자유롭게 사용하여 디버그 빌드에서 버그를 잡을 수 있습니다.

  • 사용자 인터페이스 모드에서 Trace.Assert를 사용하는 경우 사용자가 당황하지 않도록 DefaultTraceListener를 제거 할 수 있습니다.

  • 테스트중인 조건이 앱이 처리 할 수없는 조건 인 경우 예외가 발생하여 실행이 계속되지 않도록하는 것이 좋습니다. 사용자는 어설 션을 무시하도록 선택할 수 있습니다.


1
OP가 프로덕션 코드에 대해 구체적으로 요청했기 때문에 Debug.Assert와 Trace.Assert의 중요한 차이점을 지적한 +1
Jon Coombs

21

어설 션은 사용자 오류가 아닌 프로그래머 오류를 포착하는 데 사용됩니다. 사용자가 어설 션을 발생시킬 가능성이없는 경우에만 사용해야합니다. 예를 들어 API를 작성하는 경우 API 사용자가 호출 할 수있는 메소드에서 인수가 널이 아닌지 확인하기 위해 어설 션을 사용해서는 안됩니다. 그러나 API의 일부로 노출되지 않은 개인 메서드에서 사용되어 코드가 의도하지 않은 경우 null 인수를 전달하지 않도록 할 수 있습니다.

확실하지 않은 경우 일반적으로 주장보다 예외를 선호합니다.


11

한마디로

Asserts 가드 및 계약 제한 조건에 의한 설계 확인에 사용됩니다.

  • Asserts디버그 및 비 프로덕션 빌드에만 사용해야합니다. 어설 션은 일반적으로 릴리스 빌드에서 컴파일러에 의해 무시됩니다.
  • Asserts 시스템을 제어하는 ​​버그 / 예기치 않은 상태를 확인할 수 있습니다
  • Asserts 사용자 입력 또는 비즈니스 규칙의 1 차 유효성 검증 메커니즘이 아님
  • Asserts해서는 안된다메모리 부족, 네트워크 장애, 데이터베이스 장애 등과 같은 예기치 않은 환경 조건 (코드가 제어 할 수없는 외부)을 감지하는 데 사용 . 드물기는하지만 이러한 조건은 예상되지만 (앱 코드는 하드웨어 고장 또는 리소스 고갈). 일반적으로 예외가 발생합니다. 응용 프로그램에서 수정 작업 (예 : 데이터베이스 또는 네트워크 작업 재시도, 캐시 된 메모리 확보 시도)을 수행하거나 예외를 처리 할 수없는 경우 정상적으로 중단 할 수 있습니다.
  • 실패한 어설 션은 시스템에 치명적이어야합니다. 예를 들어 예외와 달리 실패하거나 잡거나 처리하지 Asserts마십시오. 코드가 예기치 않은 영역에서 작동하고 있습니다. 스택 추적 및 크래시 덤프를 사용하여 무엇이 잘못되었는지 확인할 수 있습니다.

주장은 다음과 같은 큰 이점이 있습니다.

  • 누락 된 사용자 입력의 검증 또는 상위 코드의 업스트림 버그를 찾는 데 도움이됩니다.
  • 코드베이스의 주장은 코드에서 만들어진 가정을 독자에게 명확하게 전달합니다.
  • Debug빌드 시 Assert가 런타임에 확인됩니다 .
  • 코드가 철저하게 테스트되면 릴리스로 코드를 다시 빌드하면 가정을 확인하는 성능 오버 헤드가 제거됩니다 (그러나 이후 디버그 빌드는 필요한 경우 항상 검사를 되돌릴 수 있다는 이점이 있습니다).

... 자세한 세부 사항

Debug.Assert프로그램의 제어 내에서 나머지 코드 블록에 의해 상태에 대해 가정 된 조건을 표현한다. 여기에는 제공된 매개 변수의 상태, 클래스 인스턴스 멤버의 상태 또는 메소드 호출의 리턴이 계약 / 설계된 범위에있을 수 있습니다. 일반적으로 Assert는 의도하지 않은 버그 또는 고려되지 않은 조건이 있음을 나타내므로 필요한 모든 정보 (스택 추적, 크래시 덤프 등)로 스레드 / 프로세스 / 프로그램을 중단해야합니다 (예 : 어설 션 자체가 버그보다 더 많은 피해를 유발할 수있는 경우를 제외하고 어설 션 실패 처리) (예 : 항공기가 잠수함을 타는 경우 항공 교통 관제사는 YSOD를 원하지 않을 것입니다. 생산 ...)

사용시기 Asserts? -시스템의 기능 또는 클래스 상태에 대한 입력이 유효한 것으로 간주되는 시스템 또는 라이브러리 API 또는 서비스의 어느 시점 (예 : 시스템의 프리젠 테이션 티어에서 사용자 입력에 대한 유효성 검사가 이미 수행 된 경우) 비즈니스 및 데이터 계층 클래스는 일반적으로 입력에서 널 확인, 범위 확인, 문자열 길이 확인 등이 이미 완료된 것으로 가정합니다. -공통 Assert점검에는 무효 가정이 귀무 객체의 역 참조, 제로 제수, 숫자 또는 날짜 산술 오버플로 및 일반적으로 대역 외 / 행동을 위해 설계되지 않은 곳 (예 : 32 비트 int가 인간의 연령을 모델링하는 데 사용 된 경우)이 포함됩니다. , 그것은 신중하다Assert 나이가 실제로 0에서 125 사이 인 경우 -100과 10 ^ 10의 값은 설계되지 않은 것이 좋습니다).

.Net 코드 계약
.Net 스택에서 코드 계약 은을 사용 하거나 그 대신 사용할 수 Debug.Assert있습니다. 코드 계약은 상태 검사를 더욱 공식화 할 수 있으며 ~ 컴파일 시간 (또는 IDE에서 백그라운드 검사로 실행되는 경우 곧)에 가정 위반을 감지하는 데 도움을 줄 수 있습니다.

사용 가능한 계약 별 설계 (DBC) 검사는 다음과 같습니다.

  • Contract.Requires -계약 전제 조건
  • Contract.Ensures -계약 후 조건
  • Invariant -수명의 모든 시점에서 객체의 상태에 대한 가정을 표현합니다.
  • Contract.Assumes -계약이 아닌 장식 된 메소드를 호출 할 때 정적 검사기를 진정시킵니다.

불행히도 MS가 코드 개발을 중단 한 이후로 코드 계약은 거의 끝났습니다.
Mike Lowery 2016 년

10

내 책에는 거의 없었습니다. 대부분의 경우 모든 것이 제정신인지 확인하고 그렇지 않은 경우 던져보십시오.

내가 싫어하는 것은 디버그 빌드가 릴리스 빌드와 기능적으로 다르다는 사실입니다. 디버그 어설 션이 실패하지만 기능이 릴리스에서 작동하는 경우 어떻게 의미가 있습니까? 어설 터가 오랫동안 회사를 떠났고 아무도 그 코드의 부분을 알지 못하면 더 좋습니다. 그런 다음 실제로 문제가 있는지 확인하기 위해 문제를 탐색하면서 시간을 낭비해야합니다. 그것이 문제라면 왜 사람이 처음에 던지지 않습니까?

나에게 이것은 Debug.Asserts를 사용하여 제안합니다. 다른 사람에게 문제를 연기하고 문제를 직접 처리하십시오. 무언가가 예상되는 경우 던져지지 않습니다.

어설 션을 최적화하려는 성능에 중요한 시나리오가있을 수 있으며 거기에서 유용하지만 아직 그러한 시나리오가 발생하지 않았습니다.


4
대답은 디버깅 세션을 방해한다는 사실과 오 탐지 가능성에 대해 자주 제기되는 관심사를 강조 할 때 가치가 있습니다. 그러나 약간의 미묘함이 빠져 있고 "어설 션 최적화"를 작성하고 있습니다. 예외를 던지고 debug.assert를 수행하는 것이 동일하다는 생각에 근거 할 수 있습니다. 그것은 당신이 찬성 한 답변 중 일부에서 볼 수 있듯이, 다른 목적과 특성을 제공하지는 않습니다. Dw
캐스퍼 레온 닐슨

+1 : "제가 싫어하는 점은 디버그 빌드가 릴리스 빌드와 기능적으로 다르다는 사실입니다. 디버그 어설 션이 실패하지만 기능이 릴리스에서 작동하는 경우 어떤 의미가 있습니까?" .NET에서는 System.Diagnostics.Trace.Assert()릴리스 빌드 및 디버그 빌드에서 실행됩니다.
DavidRR

7

에 따르면 IDesign의 표준 을 수행해야

모든 가정을 주장하십시오. 평균적으로 모든 5 번째 줄은 어설 션입니다.

using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

면책 조항으로 IRL을 구현하는 것이 실용적이지 않다는 것을 언급해야합니다. 그러나 이것이 그들의 표준입니다.


Juval Lowy가 자신을 인용하는 것처럼 보입니다.
devlord

6

릴리스 빌드에 대한 검사를 제거하려는 경우에만 어설 션을 사용하십시오. 디버그 모드에서 컴파일하지 않으면 어설 션이 실행되지 않습니다.

널 확인 예제를 고려할 때 이것이 내부 전용 API 인 경우 어설 션을 사용할 수 있습니다. 공용 API에 있으면 명시 적 검사 및 던져를 사용합니다.


.NET에서는 System.Diagnostics.Trace.Assert()릴리스 (프로덕션) 빌드에서 어설 션을 실행하는 데 사용할 수 있습니다 .
DavidRR

코드 분석 규칙 CA1062 : 공공 방법의 유효성 인수는 필요 에 대한 인수 검사를 null할 때 : "외부에서 볼 방법은 그 인수가 있는지 여부를 확인하지 않고 해당 참조 인수 중 하나를 역 참조 는 null를 ." 이러한 상황에서는 메소드 또는 속성이 발생해야합니다 ArgumentNullException.
DavidRR

6

모든 어설 션은 다음에 최적화 될 수있는 코드 여야합니다.

Debug.Assert(true);

이미 가정 한 것을 확인하고 있기 때문에 사실입니다. 예 :

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

위에서 null 매개 변수에 대한 세 가지 접근 방식이 있습니다. 첫 번째는 그것을 허용 가능한 것으로 받아들입니다 (아무것도하지 않습니다). 두 번째는 호출 코드에서 처리 할 예외 (또는 오류 메시지가 발생하지 않음)를 발생시킵니다. 세 번째는 일어날 수 없다고 가정하고 그렇게한다고 주장합니다.

첫 번째 경우에는 문제가 없습니다.

두 번째 경우, 호출 코드에 문제가 있습니다. GetFirstAndConsumenull로 호출해서는 안되므로 예외가 발생합니다.

세 번째 경우,이 코드에는 문제가 있습니다. 코드 en != null가 호출되기 전에 이미 검사되어 버그가 아닌 것이 사실이기 때문입니다. 즉, 이론적으로 최적화 될 수있는 코드 여야하고 Debug.Assert(true), sicne en != null은 항상 있어야합니다 true!


1
세 번째 경우, en == null생산중인 경우 어떻게됩니까 ? (프로그램이 철저히 디버깅되었으므로) 프로덕션에서는 절대 일어날 en == null없다고 말할 수 있습니까? 그렇다면 Debug.Assert(en != null)최소한 의견에 대한 대안으로 사용됩니다. 물론, 향후 변경이 이루어질 경우, 가능한 회귀를 탐지하기위한 가치도 계속 유지합니다.
DavidRR

@DavidRR, 나는 그것이 결코 null이 될 수 없다고 주장하고 있으며, 따라서 코드에서의 주장 또한 이름입니다. 물론 틀릴 ​​수도 있고, 변경에 의해 틀릴 수도 있는데, 이것이 주장의 가치입니다.
존 한나

1
Debug.Assert()릴리스 빌드에서는 호출 이 제거됩니다. 당신이 경우에 따라서, 이다 잘못, 세 번째 경우에 , 당신은 (생산의 릴리스 빌드의 사용을 가정) 생산을 알 수 없습니다. 그러나 첫 번째와 두 번째 경우의 동작은 디버그 및 릴리스 빌드에서 동일합니다.
DavidRR

@DavidRR은 내가 확인할 수없는 상태를 유지할 때만 적절하게 만듭니다. 다시 말하면 상태를 확인하는 것이 아니라 사실의 주장입니다. 물론 어설 션이 있고, 잡을 버그가 있지만 테스트에서 그 사건을 치르지 않는 경우에도 의미가 없습니다.
존 한나

4

Debug.Assert가 올바른 선택이 될 수있는 경우를 4 개 더 추가 할 것이라고 생각했습니다.

1) 여기에 언급되지 않은 내용은 자동화 된 테스트 중에 Asserts가 제공 할 수 있는 추가 개념 적용 범위 입니다. 간단한 예로서 :

추가 시나리오를 처리하기 위해 코드 범위를 확장했다고 생각하는 작성자가 상위 레벨 발신자를 수정하면 이상적으로는 (!)이 새로운 조건을 다루기 위해 단위 테스트를 작성합니다. 그러면 완전히 통합 된 코드가 제대로 작동하는 것처럼 보일 수 있습니다.

그러나 실제로 미묘한 결함이 도입되었지만 테스트 결과에서는 감지되지 않았습니다. 호출 수신자는이 경우 비 결정적되고, 오직 한 일이 예상 된 결과를 제공 할 수 있습니다. 또는 눈에 띄지 않는 반올림 오류가 발생했을 수 있습니다. 또는 다른 곳에서 똑같이 오프셋 된 오류가 발생했습니다. 또는 요청 된 액세스뿐만 아니라 부여되어서는 안되는 추가 권한이 부여됩니다. 기타.

이 시점에서 단위 테스트로 구동되는 새로운 사례 (또는 엣지 케이스)와 결합 된 수신자에 포함 된 Debug.Assert () 문은 테스트 중에 원래 작성자의 가정이 무효화되었으며 코드가 추가 검토없이 릴리스됩니다. 단위 테스트를 통한 주장은 완벽한 파트너입니다.

2) 또한 일부 테스트는 작성이 간단하지만 초기 가정을 고려할 때 비용이 많이 들고 불필요합니다 . 예를 들면 다음과 같습니다.

특정 보안 진입 점에서만 개체에 액세스 할 수있는 경우 모든 개체 메서드에서 네트워크 권한 데이터베이스에 대한 추가 쿼리를 작성하여 호출자에게 권한을 부여해야합니까? 분명히 아니다. 아마도 이상적인 솔루션에는 캐싱 또는 다른 기능 확장이 포함되지만 디자인에는 필요하지 않습니다. 개체가 안전하지 않은 진입 점에 연결되면 Debug.Assert ()가 즉시 표시됩니다.

3) 다음으로 경우에 따라 릴리스 모드로 배포 할 때 제품의 일부 또는 전체 작업에 유용한 진단 상호 작용이 없을 수도 있습니다 . 예를 들면 다음과 같습니다.

임베디드 실시간 장치라고 가정하십시오. 잘못된 패킷을 발견하면 예외를 던지고 다시 시작하는 것이 비생산적입니다. 대신, 장치는 출력에서 ​​렌더링 노이즈 지점까지 최선의 노력으로 이익을 얻을 수 있습니다. 또한 릴리스 모드에서 배포 할 때 휴먼 인터페이스, 로깅 장치가 없거나 사람이 물리적으로 액세스 할 수 없으며 동일한 출력을 평가하여 오류를 가장 잘 인식 할 수 있습니다. 이 경우 자유 주장 및 철저한 시험판 테스트가 예외보다 더 중요합니다.

4) 마지막으로, 수취인이 매우 신뢰할 수있는 것으로 인식되기 때문에 일부 테스트는 불필요합니다 . 대부분의 경우 재사용 가능한 코드가 많을수록 신뢰할 수있는 노력을 기울였습니다. 따라서 발신자의 예기치 않은 매개 변수는 예외이지만 수신자의 예기치 않은 결과는 Assert가 일반적입니다. 예를 들면 다음과 같습니다.

검색 조건을 찾을 수 없을 때 핵심 String.Find작업이 상태를 반환하면 -13이 아닌 하나의 작업을 안전하게 수행 할 수 있습니다. 그러나 실제로 반환 된 경우 -2합리적인 조치가 없을 수 있습니다. 더 간단한 계산을 -1값을 별도로 테스트하는 계산으로 바꾸는 것은 도움이되지 않으며 대부분의 릴리스 환경에서는 코드를 버리기 때문에 코어 라이브러리가 예상대로 작동하는지 테스트하지 않아도됩니다. 이 경우 어설 션이 이상적입니다.


4

실용 프로그래머 로부터 인용 : Journeyman에서 Master까지

어설 션을 켠 상태로 두십시오

컴파일러와 언어 환경을 작성하는 사람들이 발표 한 주장에 대한 일반적인 오해가 있습니다. 다음과 같이 진행됩니다.

어설 션은 코드에 약간의 오버 헤드를 추가합니다. 절대 발생하지 않아야하는 사항을 확인하기 때문에 코드의 버그에 의해서만 트리거됩니다. 코드를 테스트하고 출하 한 후에는 더 이상 필요하지 않으며 코드를 더 빨리 실행하기 위해 해제해야합니다. 어설 션은 디버깅 기능입니다.

여기에는 두 가지 특허 적으로 잘못된 가정이 있습니다. 먼저, 테스트에서 모든 버그가 발견된다고 가정합니다. 실제로 복잡한 프로그램의 경우 코드가 적용되는 순열의 작은 비율조차도 테스트하지 않을 것입니다 (무자비한 테스트 참조).

둘째, 낙관론자들은 여러분의 프로그램이 위험한 세상에서 운영된다는 사실을 잊고 있습니다. 테스트하는 동안 쥐는 아마도 통신 케이블을 아 먹지 않을 것이며, 게임을하는 사람은 메모리를 소모하지 않을 것이며 로그 파일은 하드 드라이브를 채우지 않을 것입니다. 이러한 상황은 프로그램이 프로덕션 환경에서 실행될 때 발생할 수 있습니다. 첫 번째 방어선은 가능한 오류가 있는지 확인하는 것이고, 두 번째 방어는 어설 션을 사용하여 누락 된 것을 감지하려고합니다.

프로그램을 프로덕션에 전달할 때 어설 션을 끄는 것은 실제로는 한 번 실행했기 때문에 그물없이 높은 와이어를 건너는 것과 같습니다. . 극적인 가치가 있지만 생명 보험을 받기는 어렵습니다.

성능 문제가 있더라도 실제로 영향을 미치는 어설 션 만 끕니다 .


2

항상 두 번째 접근 방식을 사용해야합니다 (예외 예외).

또한 프로덕션 환경에 있고 릴리스 빌드가있는 경우 유효하지 않은 값으로 작업하는 것보다 예외를 던지고 앱이 최악의 경우 충돌하는 것이 좋습니다. 달러).


1
아냐, 다른 답변과 동일합니다. 차이점을 실제로 이해하지 못하므로 오퍼링 중 하나를 선택하여 프로세스에서 그들 사이에 잘못된 이분법을 설정합니다. Dw
캐스퍼 레온 닐슨

3
이것이이 목록의 유일한 정답입니다. IMO. 캐스퍼를 너무 쉽게 무시하지 마십시오. 디버그 어설트는 안티 패턴입니다. 디버그시 변하지 않는 경우 런타임시 변하지 않습니다. 앱이 깨진 불변을 계속하도록 허용하면 결정적이지 않은 상태가되고 충돌보다 더 심각한 문제가 발생할 수 있습니다. IMO 두 계약에서 동일한 코드를 사용하여 계약이 깨져서 빠르게 실패한 다음 최상위 수준에서 강력한 오류 처리를 구현하는 것이 좋습니다. 예를 들어, 구성 요소를 분리하고 다시 시작하는 기능을 구현하십시오 (예 : 브라우저에서 탭이 충돌해도 전체 브라우저가 충돌하지 않음).
justin.m.chase

1
Trace.Assert를 토론에 포함시키는 것이 도움이 될 수 있습니다. 같은 주장으로 무시할 수 없기 때문입니다.
Jon Coombs

0

프로그램에서 논리적 오류를 테스트하려면 Debug.Assert를 사용해야합니다. 컴파일러는 구문 오류 만 알려줄 수 있습니다. 따라서 Assert 문을 정의하여 논리적 오류를 테스트해야합니다. 파란 자동차 BMW 만 15 % 할인을 받아야하는 자동차를 판매하는 프로그램을 테스트하는 것과 같습니다. 컴파일러는 이것을 수행하는 데 프로그램이 논리적으로 올바른지에 대해서는 아무것도 알 수 없지만 어설 션 문은 할 수 있습니다.


2
죄송하지만 예외는 모두 똑같은 일을 하므로이 답변은 실제 질문을 해결하지 못합니다.
로마 스타 코프

0

나는 여기서 답을 읽었으며 중요한 차이점을 추가해야한다고 생각했습니다. 어설 션을 사용하는 방법에는 두 가지가 있습니다. 하나는 프로그램이 계속 진행될 수있는 조건부 중단 점과 같은 일종의 "실제로 그렇게해서는 안된다면 어떻게해야할지 결정해야합니다"라는 임시 개발자 바로 가기입니다. 다른 하나는 코드에 유효한 프로그램 상태에 대한 가정을 두는 방법입니다.

첫 번째 경우 어설 션은 최종 코드에있을 필요도 없습니다. Debug.Assert개발 중에 사용해야하며 더 이상 필요하지 않은 경우 제거 할 수 있습니다. 릴리스 컴파일에 아무런 영향을 미치지 않으므로 그대로 두거나 문제를 제거하는 것을 잊어 버린 경우 문제가 없습니다.

그러나 두 번째 경우에는 어설 션이 코드의 일부입니다. 그들은 또한 당신의 가정이 사실이라고 주장하고 문서화합니다. 이 경우 코드에 그대로두기를 원합니다. 프로그램이 유효하지 않은 상태이면 계속 진행할 수 없습니다. 성능 저하를 감당할 수 없다면 C #을 사용하지 않는 것입니다. 한편으로 디버거가 발생할 경우이를 연결할 수 있으면 유용 할 수 있습니다. 다른 한편으로, 스택 추적이 사용자에게 팝업되는 것을 원하지 않으며 더 중요한 것은 사용자가이를 무시할 수 없도록하는 것입니다. 또한 서비스에 있으면 항상 무시됩니다. 따라서 프로덕션 환경에서 올바른 동작은 Exception을 발생시키고 프로그램의 일반적인 예외 처리를 사용하는 것입니다. 그러면 사용자에게 멋진 메시지가 표시되고 세부 정보가 기록 될 수 있습니다.

Trace.Assert이것을 달성하는 완벽한 방법이 있습니다. 프로덕션 환경에서는 제거되지 않으며 app.config를 사용하여 다른 리스너로 구성 할 수 있습니다. 따라서 개발을 위해 기본 핸들러는 괜찮으며 프로덕션을 위해 아래와 같은 간단한 TraceListener를 만들어 예외를 throw하고 프로덕션 구성 파일에서 활성화 할 수 있습니다.

using System.Diagnostics;

public class ExceptionTraceListener : DefaultTraceListener
{
    [DebuggerStepThrough]
    public override void Fail(string message, string detailMessage)
    {
        throw new AssertException(message);
    }
}

public class AssertException : Exception
{
    public AssertException(string message) : base(message) { }
}

그리고 프로덕션 구성 파일에서 :

<system.diagnostics>
  <trace>
    <listeners>
      <remove name="Default"/>
      <add name="ExceptionListener" type="Namespace.ExceptionTraceListener,AssemblyName"/>
    </listeners>
  </trace>
 </system.diagnostics>

-1

나는 그것이 C #과 .NET에 어떻게 있는지 모르겠지만 C에서는 -DDEBUG로 컴파일 된 경우에만 assert () 작동합니다-최종 사용자는 assert ()가 없으면 컴파일되지 않습니다. 개발자 전용입니다. 정말 자주 사용합니다. 때로는 버그를 추적하기가 더 쉽습니다.


-1

프로덕션 코드에서는 사용하지 않습니다. 예외를 처리하고 catch하고 기록하십시오.

ass.는 콘솔에 표시되어 요청을 고정시킬 수 있으므로 asp.net에서도주의해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.