#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、2010

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デバッグの例: 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シンボル)に対してコンパイルされたとき、それは永遠に電話を持つことになりますB()の中からA()、省略した場合でもにコールA()DEBUGは、アセンブリの呼び出しで定義されているので、含まれています。


13
DoSomethingの#ifデバッグでは、すべての呼び出しステートメントを#if DEBUGで囲む必要はありません。1:DoSomethingの内部を#if DEBUGするか、DoSomethingの空白の定義で#elseを実行します。それでもあなたのコメントは私が違いを理解するのに役立ちましたが、#if DEBUGはあなたが実証したほど醜い必要はありません。
Apeiron 2011年

3
#if内容をデバッグするだけの場合、非デバッグビルドでコードを実行するときに、JITに関数の呼び出しが含まれる場合があります。条件付き属性を使用すると、JITは非デバッグビルドのときにコールサイトを出力しないことも認識します。
ジェフイエーツ2013

2
@JeffYates:あなたが書いている内容が、私が説明した内容とどのように違うのかわかりません。
私の

1
@Apeironは、#ifデバッグに関数コンテンツのみがある場合、関数呼び出しは引き続きコールスタックに追加されますが、これは通常それほど重要ではありません。宣言と関数呼び出しを#ifに追加すると、コンパイラーは次のように動作しますif関数が存在しない場合、私の方法は#ifを使用するより「正しい」方法です。どちらの方法でも、通常の使用では互いに区別できない結果が生成されます
MikeT 2014年

5
誰のが不思議場合、IL =中間言語- en.wikipedia.org/wiki/Common_Intermediate_Languageは
jbyrd

64

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

DEBUGシンボルが定義されていない場合、最初のケースではSetPrivateValueそれ自体は呼び出されません... 2番目のケースではそれは存在しますが、DEBUGシンボルなしでコンパイルされた呼び出し元はそれらの呼び出しを省略します。

コードとそのすべての発信者が同じアセンブリ内にある場合、この違いはそれほど重要な-しかし、それは最初のケースでは、あなたがいることを意味持っている必要があります#if DEBUG周りの呼び出しにもコードを。

個人的には2番目のアプローチをお勧めしますが、両者の違いを頭の中で明確にしておく必要があります。


5
コードを呼び出すための+1には、#ifステートメントも必要です。これは、#ifステートメントが急増することを意味します...
Lucas B

2番目のオプション(条件属性)の方が優れている場合がありますが、(たとえば、命名規則によって)コンパイル中にメソッド呼び出しがアセンブリから削除されるという事実を伝える必要がある場合があります。
リゼルグ酸

45

私は多くの点で私に同意しないと確信していますが、ビルド担当者として時間を費やして「しかし、それは私のマシンで動作します!」テストとデバッグに何かが本当に必要な場合は、そのテスト容易性を実際の製品コードから分離する方法を考えてください。

単体テストでモックを使用してシナリオを抽象化し、テストする1回限りのシナリオの1回限りのバージョンを作成します。ただし、本番リリース用にテストおよび作成するバイナリのコードにデバッグ用のテストを挿入しないでください。これらのデバッグテストは、可能性のあるバグを開発者から隠すだけなので、プロセスの後半まで発見されません。


4
私はジミーに完全に同意します。テストにDIとモッキングを使用している場合、なぜ#if debugコードまたは同様の構成がコードに必要なのでしょうか。
Richard Ev

@RichardEvこれを処理するためのより良い方法があるかもしれませんが、私は現在、それを使用して、クエリ文字列を介してさまざまなユーザーの役割を果たすことができます。これは本番環境では必要ありませんが、デバッグ用にしたいので、複数のユーザーを作成し、両方のアカウントにログインしてフローをウォークスルーする必要なく、ステップ実行されるワークフローを制御できます。実際に使ったのは初めてですが。
トニー

4
テストの目的だけでなく、デバッグビルドでデフォルトの受信者の電子メールを自分で設定することもよくあり#if DEBUGます。これにより、プロセスの一部として電子メールを送信する必要があるシステムをテストするときに、誤って他人にスパムを送信しないようにします。時々これらは仕事のための適切なツールです:)
Gone Coding

6
私は一般的にあなたに同意しますが、パフォーマンスが最優先の状況にある場合は、無関係なロギングとユーザー出力でコードを混乱させたくありませんが、変更に使用してはならないことに100%同意します基本的な動作
MikeT 2014年

5
-1これらのどちらを使用しても問題はありません。単体テストとDIが何らかの方法で製品のデバッグ対応ビルドを置き換えると主張することは、素朴です。
Ted Bigham、2017

15

これも役に立ちます:

if (Debugger.IsAttached)
{
...
}

1
個人的には、他の2つの選択肢と比較してこれがどのように役立つかはわかりません。これにより、ブロック全体がコンパイルされることが保証され、Debugger.IsAttachedリリースビルドでも実行時に呼び出す必要があります。
ジェイ

9

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

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

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

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

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

@P Daddy:折り返してもその関数の呼び出しは削除さ#if DEBUGConditional("DEBUG")ません。ILから関数が完全に削除されるだけなので、存在しない関数の呼び出しが残っています(コンパイルエラー)。
私の

1
コードがリリースに存在したくない場合は、メソッド本体を「#if DEBUG」でラップし、おそらく「#else」スタブ(スローまたはダミーの戻り値を使用)でラップし、属性を使用して提案する必要があります。呼び出し元は呼び出しに煩わされていませんか それは両方の長所のようです。
supercat

@ myermian、@ supercat:はい、あなたはどちらも正しいです。私の間違い。supercatの提案に従って編集します。
P Daddy

5

#elseJon Skeetのポイントの1つに対処するnullスタブ関数を定義するステートメントもコードにあると仮定しましょう。2つの間に2つ目の重要な違いがあります。

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


2

カスタムを使用してネットワークトラフィックをログに記録する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.