#if DEBUG vs. Conditional (“DEBUG”)


432

큰 프로젝트에서 사용하는 것이 더 나은 이유는 무엇입니까?

#if DEBUG
    public void SetPrivateValue(int value)
    { ... }
#endif

또는

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value)
{ ... }

18
이 질문에 대한 생각 은 blogs.msdn.com/b/ericlippert/archive/2009/09/10/… 을 참조하십시오 .
Eric Lippert

2
if (Debugger.IsAttached) {...}
sofsntp

Unity 개발자를위한 참고 사항 : DEBUG는 편집기 또는 개발 빌드에서 의미합니다. forum.unity.com/threads/…
KevinVictor

답변:


578

그것은 당신이 무엇을하려고하는지에 달려 있습니다 :

  • #if DEBUG: 여기의 코드는 릴리스시 IL에 도달하지도 않습니다.
  • [Conditional("DEBUG")]:이 코드는 IL에 도달하지만 호출자가 컴파일 될 때 DEBUG를 설정하지 않으면 메소드 호출 이 생략됩니다.

개인적으로 상황에 따라 두 가지를 모두 사용합니다.

Conditional ( "DEBUG") 예제 : 이 코드를 사용하여 릴리스 중에 나중에 코드를 다시 편집하지 않아도되지만 디버깅하는 동안 오타가 발생하지 않도록하고 싶습니다. 이 함수는 INotifyPropertyChanged 항목에서 속성 이름을 사용하려고 할 때 속성 이름을 올바르게 입력했는지 확인합니다.

[Conditional("DEBUG")]
[DebuggerStepThrough]
protected void VerifyPropertyName(String propertyName)
{
    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        Debug.Fail(String.Format("Invalid property name. Type: {0}, Name: {1}",
            GetType(), propertyName));
}

#if DEBUG해당 함수에 대한 모든 호출을 동일하게 래핑하지 않는 한 사용하여 함수를 만들고 싶지 않습니다 #if DEBUG.

#if DEBUG
    public void DoSomething() { }
#endif

    public void Foo()
    {
#if DEBUG
        DoSomething(); //This works, but looks FUGLY
#endif
    }

대:

[Conditional("DEBUG")]
public void DoSomething() { }

public void Foo()
{
    DoSomething(); //Code compiles and is cleaner, DoSomething always
                   //exists, however this is only called during DEBUG.
}

#if DEBUG 예제 : WCF 통신을 위해 다른 바인딩을 설정하려고 할 때 사용합니다.

#if DEBUG
        public const String ENDPOINT = "Localhost";
#else
        public const String ENDPOINT = "BasicHttpBinding";
#endif

첫 번째 예에서는 코드가 모두 존재하지만 DEBUG가 설정되어 있지 않으면 무시됩니다. 두 번째 예에서 const ENDPOINT는 DEBUG 설정 여부에 따라 "Localhost"또는 "BasicHttpBinding"으로 설정됩니다.


업데이트 : 중요하고 까다로운 요점을 명확히하기 위해이 답변을 업데이트하고 있습니다. 를 사용하기로 선택한 경우 에는 런타임이 아니라ConditionalAttribute 컴파일 중에 호출이 생략 됩니다 . 그건:

MyLibrary.dll

[Conditional("DEBUG")]
public void A()
{
    Console.WriteLine("A");
    B();
}

[Conditional("DEBUG")]
public void B()
{
    Console.WriteLine("B");
}

라이브러리가 릴리즈 모드 (예 : 디버그 기호)에 대해 컴파일 할 때, 그것은 영원히에 전화를해야합니다 B()내에서 A(), 생략해도에 전화 A()DEBUG 어셈블리 호출에 정의되어 있기 때문에 포함되어 있습니다.


13
DoSomething 용 #if 디버그에 #if DEBUG로 둘러싸인 모든 호출 명령문이있을 필요는 없습니다. 1 : DoSomething 내부에서 #if # DEBUG를 수행하거나 DoSomething의 빈 정의로 #else를 수행 할 수 있습니다. 여전히 귀하의 의견은 그 차이를 이해하는 데 도움이되었지만 #if DEBUG는 당신이 시연했던 것처럼 추악 할 필요는 없습니다.
Apeiron

3
내용을 #if 디버그 만하면 코드가 디버그가 아닌 빌드로 실행될 때 JIT에 함수 호출이 계속 포함될 수 있습니다. Conditional 속성을 사용한다는 것은 JIT가 비 디버그 빌드에있을 때 호출 사이트를 출력하지도 않음을 의미합니다.
Jeff Yates

2
@ JeffYates : 나는 당신이 쓰고있는 것이 내가 설명한 것과 어떻게 다른지 알지 못합니다.

1
@Apeiron #if 디버그에 함수 내용 만있는 경우 함수 호출은 여전히 ​​호출 스택에 추가되지만 일반적으로 그렇게 중요하지는 않지만 선언과 함수 호출을 #if에 추가하면 컴파일러가 다음과 같이 작동합니다. 함수가 존재하지 않으면 내 방법은 #if를 사용하는보다 "올바른"방법입니다. 두 방법 모두 정상적인 사용에서 서로 구별 할 수없는 결과를 생성하지만
MikeT

5
사람의 궁금 경우, IL = 중간 언어 - en.wikipedia.org/wiki/Common_Intermediate_Language은
jbyrd

64

글쎄, 그것들이 전혀 같은 것을 의미하지 않는다는 점은 주목할 가치가 있습니다.

DEBUG 기호가 정의되지 않은 경우 첫 번째 경우에는 SetPrivateValue자체 호출되지 않지만 두 번째 경우에는 존재하지만 DEBUG 기호없이 컴파일 된 호출자 는 해당 호출을 생략합니다.

코드와 모든 호출자가 동일한 어셈블리에있는 경우이 차이는 중요합니다. 그러나 첫 번째 경우 에도 호출 코드 #if DEBUG주위 에 있어야 합니다.

개인적으로 두 번째 방법을 권장하지만 머리의 차이점을 명확하게 유지해야합니다.


5
호출 코드 +1에는 #if 문도 있어야합니다. 이는 #if 문이 확산 될 것임을 의미합니다 ...
Lucas B

두 번째 옵션 (조건부 속성)이 더 좋고 깔끔한 경우도 있지만 컴파일하는 동안 어셈블리에서 메서드 호출이 제거된다는 사실을 알려야 할 수도 있습니다 (예 : 명명 규칙).
lysergic-acid

45

나는 많은 의견에 동의하지 않을 것이라 확신하지만, 빌드 녀석으로서 "하지만 내 컴퓨터에서 작동합니다!" 테스트 및 디버깅에 실제로 필요한 것이 있으면 테스트 가능성을 실제 프로덕션 코드와 분리하는 방법을 찾으십시오.

단위 테스트에서 조롱하여 시나리오를 추상화하고 테스트하려는 하나의 오프 시나리오에 대해 일회성 버전을 작성하지만 프로덕션 릴리스를 위해 테스트하고 작성한 바이너리의 코드에는 디버그 테스트를 넣지 마십시오. 이 디버그 테스트는 개발자로부터 가능한 버그를 숨기므로 나중에 프로세스에서 찾을 수 없습니다.


4
나는 당신에게 지미에 전적으로 동의합니다. 테스트에 DI 및 조롱을 사용하는 경우 #if debug코드에 왜 또는 유사한 구성 이 필요 합니까?
Richard Ev

@RichardEv 이것을 처리하는 더 좋은 방법이있을 수 있지만 현재는 쿼리 문자열을 통해 다른 사용자의 일부를 재생할 수 있도록 사용하고 있습니다. 프로덕션에서는 이것을 원하지 않지만 디버깅을 위해 필요하므로 여러 사용자를 만들고 두 계정에 로그인하여 흐름을 진행하지 않고도 단계별 워크 플로를 제어 할 수 있습니다. 이것이 실제로 사용한 것은 이번이 처음입니다.
Tony

4
테스트를 위해서가 아니라 디버그 빌드 #if DEBUG에서 프로세스의 일부로 이메일을 전송해야하는 시스템을 테스트하는 동안 실수로 다른 사람을 스팸하지 않도록하기 위해 디버그 빌드에서 자신에게 기본 수신자 이메일을 설정하는 것과 같은 작업을 종종 수행 합니다. 때로는 이러한 작업에 적합한 도구가 있습니다 :)
코딩 사라짐

6
나는 일반적으로 당신에게 동의하지만 성능이 가장 중요한 상황이라면 외부 로깅 및 사용자 출력으로 코드를 어지럽히고 싶지 않지만 100 %는 변경에 사용해서는 안된다는 것에 100 % 동의합니다 기본 행동
MikeT

5
-1 이들 중 하나를 사용하는 데 아무런 문제가 없습니다. 주장하는 단위 테스트와 DI는 어떻게 든 디버그 가능 제품 빌드를 대체하는 것은 순진합니다.
Ted Bigham

15

이것도 유용 할 수 있습니다 :

if (Debugger.IsAttached)
{
...
}

1
개인적으로, 이것이 다른 두 대안과 비교하여 이것이 어떻게 유용한 지 알 수 없습니다. 이를 통해 전체 블록이 컴파일 Debugger.IsAttached되고 릴리스 빌드에서도 런타임에 호출되어야합니다.
Jai

9

첫 번째 예제로, SetPrivateValue경우 빌드에 존재하지 않습니다 DEBUG정의되지 않은 두 번째 예제로, 호출SetPrivateValue경우 빌드에 존재하지 않습니다 DEBUG정의되어 있지 않습니다.

첫 번째 예를 들어, 당신은 어떤 통화를 포장해야 SetPrivateValue와 함께 #if DEBUG뿐만 아니라.

두 번째 예에서는 호출 SetPrivateValue이 생략되지만 SetPrivateValue여전히 컴파일됩니다. 이것은 라이브러리를 빌드 할 때 유용하므로 라이브러리를 참조하는 응용 프로그램이 여전히 조건을 충족하는 경우 함수를 사용할 수 있습니다.

통화를 생략하고 수신자의 공간을 절약하려면 다음 두 가지 기술을 조합하여 사용할 수 있습니다.

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
    #if DEBUG
    // method body here
    #endif
}

@P 아빠 : 포장은 #if DEBUG주변에 Conditional("DEBUG")아직도 (컴파일 오류가) 존재하지 않는 함수를 호출하는 데 문제가있는, 그래서 그냥 alltogether IL에서 기능을 제거하고, 그 함수에 대한 호출을 제거하지 않습니다.

1
코드가 릴리스에 존재하지 않게하려면 메소드 본문을 "#if DEBUG"( "# else"스텁 (던지기 또는 더미 반환 값 포함)으로 랩핑하고 속성을 사용하여 제안해야 함) 발신자가 전화를 방해하지 않습니까? 그것은 두 세계의 최고로 보일 것입니다.
supercat

@myermian, @supercat : 네, 둘 다 맞습니다. 내 실수. 슈퍼 캣의 제안에 따라 편집하겠습니다.
P Daddy

5

코드 #else에 Jon Skeet의 요점 중 하나를 다루는 null 스텁 함수를 정의한 명령문이 있다고 가정합시다 . 두 가지 중요한 차이점이 있습니다.

기본 프로젝트 실행 파일에서 참조하는 DLL에 #if DEBUGor Conditional함수가 있다고 가정합니다 . 를 사용하면 #if조건부 평가가 라이브러리의 컴파일 설정과 관련하여 수행됩니다. 이 Conditional속성을 사용하면 호출자의 컴파일 설정과 관련하여 조건부 평가가 수행됩니다.


2

custom을 사용하여 네트워크 트래픽을 기록하는 SOAP WebService 확장이 [TraceExtension]있습니다. 디버그 빌드 에만 사용 하고 릴리스 빌드 에서는 생략 합니다. 를 사용하여 속성 #if DEBUG을 래핑하여 릴리스 빌드 [TraceExtension]에서 제거하십시오 .

#if DEBUG
[TraceExtension]
#endif
[System.Web.Service.Protocols.SoapDocumentMethodAttribute( ... )]
[ more attributes ...]
public DatabaseResponse[] GetDatabaseResponse( ...) 
{
    object[] results = this.Invoke("GetDatabaseResponse",new object[] {
          ... parmeters}};
}

#if DEBUG
[TraceExtension]
#endif
public System.IAsyncResult BeginGetDatabaseResponse(...)

#if DEBUG
[TraceExtension]
#endif
public DatabaseResponse[] EndGetDatabaseResponse(...)

0

일반적으로 디버그가 아닌 코드에서 디버그를 실행하고 대부분 Windows 서비스에서 실행하려는 경우 Program.cs에 필요합니다. 그래서 읽기 전용 필드 IsDebugMode를 만들고 아래와 같이 정적 생성자에 값을 설정했습니다.

static class Program
{

    #region Private variable
    static readonly bool IsDebugMode = false;
    #endregion Private variable

    #region Constrcutors
    static Program()
    {
 #if DEBUG
        IsDebugMode = true;
 #endif
    }
    #endregion

    #region Main

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main(string[] args)
    {

        if (IsDebugMode)
        {
            MyService myService = new MyService(args);
            myService.OnDebug();             
        }
        else
        {
            ServiceBase[] services = new ServiceBase[] { new MyService (args) };
            services.Run(args);
        }
    }

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