web-dev-qa-db-ja.com

#if DEBUG対条件付き( "DEBUG")

大規模プロジェクトではどちらを使用するのが良いのでしょうか。

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

または

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

それは本当にあなたが何をしようとしているかによって異なります。

  • #if DEBUG:ここのコードはリリースされてもILには届きません。
  • [Conditional("DEBUG")]:このコードはILに到達しますが、呼び出し側のコンパイル時にDEBUGが設定されていない限り、 メソッドへの呼び出し は省略されます。

個人的には状況に応じて両方を使います。

条件付き( "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がオンでない限り無視されます。 2番目の例では、DEBUGが設定されているかどうかに応じて、const ENDPOINTが "Localhost"または "BasicHttpBinding"に設定されます。


更新:私はこの答えを更新して、重要で扱いにくい点を明確にしています。 ConditionalAttributeを使用する場合は、コンパイル時に呼び出しは省略され、ランタイムではないが使用されることに注意してください。あれは:

MyLibrary.dll

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

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

ライブラリが解放モード(つまり、DEBUGシンボルなし)でコンパイルされている場合、呼び出し元のアセンブリでDEBUGが定義されているため、B()の呼び出しが含まれていても、A()内からのA()の呼び出しは永久に省略されます。

540
m-y

まあ、それはまったく同じことを意味しないことに注意する価値があります。

DEBUGシンボルが定義されていない場合、最初の場合はSetPrivateValue自体は呼び出されませんが、2番目の場合は存在しますが、callers who DEBUGシンボルなしでコンパイルすると、これらの呼び出しは省略されます。

コードとそのすべての呼び出し元が同じアセンブリ内にある場合、この違いはless重要です-ただし、最初のケースではalsoが必要です#if DEBUG呼び出しコードの周りにも。

個人的には、2番目のアプローチをお勧めしますが、頭の中でそれらの違いを明確に保つ必要があります。

61
Jon Skeet

私は賛成できないと確信していますが、ビルド担当者として「でもそれは私のマシンでは動作します」と聞いて時間を費やしてきたので、私はあなたがどちらも使うべきではないという立場を取ります。あなたが本当にテストとデバッグのために何かを必要とするならば、そのテスト容易性を実際の生産コードから切り離す方法を考え出してください。

単体テストでモックすることでシナリオを抽象化し、テストしたい1つのシナリオに対して1つのバージョンのものを作成します。ただし、実稼働リリース用にテストして作成するバイナリのコードにデバッグ用のテストを入れないでください。これらのデバッグテストは、潜在的なバグを開発者から隠すだけなので、プロセスの後半で発見されることはありません。

42
Jimmy Hoffa

これも便利です。

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

最初の例では、SetPrivateValueが定義されていない場合、DEBUGはビルドに存在しません。2番目の例では、SetPrivateValueが定義されていない場合、 呼び出し DEBUGに対して存在しません。

最初の例では、SetPrivateValueへの呼び出しを#if DEBUGでラップする必要があります。

2番目の例では、SetPrivateValueの呼び出しは省略されますが、SetPrivateValue自体はまだコンパイルされることに注意してください。これは、ライブラリを構築している場合に便利です。したがって、ライブラリを参照しているアプリケーションは、(条件が満たされていれば)それでも関数を使用できます。

呼び出しを省略して呼び出し先のスペースを節約したい場合は、2つの方法を組み合わせて使用​​できます。

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

Jon Skeetの要点の1つに対処するために、コードにnullスタブ関数を定義する#elseステートメントも含まれているとしましょう。 2つの間に2番目の重要な違いがあります。

メインプロジェクトの実行可能ファイルによって参照されるDLLに#if DEBUGまたはConditional関数が存在するとします。 #ifを使用して、条件付きの評価はライブラリのコンパイル設定に関して実行されます。 Conditional属性を使用して、呼び出し元のコンパイル設定に関して条件付きの評価が実行されます。

4
Kennet Belenky

カスタム[TraceExtension]を使用してネットワークトラフィックを記録するためのSOAP WebService拡張があります。これはデバッグビルドにのみ使用し、リリースビルドからは省略します。 #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(...)
2

通常、Program.csで必要になるでしょう。そこでは、Debug以外のコードでDebugを実行し、それをWindows Servicesで実行することを決定します。そこで、読み取り専用のフィールド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        
}
0
Yashwant Shukla