#if DEBUG vs. Condizionale ("DEBUG")


432

Quale è meglio usare e perché su un grande progetto:

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

o

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

18
Vedi blogs.msdn.com/b/ericlippert/archive/2009/09/10/… per alcune riflessioni su questa domanda.
Eric Lippert,

2
puoi usare anche questo: if (Debugger.IsAttached) {...}
sofsntp

Nota per gli sviluppatori Unity: DEBUG significa nell'editor o nelle build di sviluppo. forum.unity.com/threads/…
KevinVictor il

Per tutti voi che cercate il blogpost archiviato
mbx

Risposte:


578

Dipende davvero da cosa stai cercando:

  • #if DEBUG: Il codice qui non raggiungerà nemmeno l'IL al rilascio.
  • [Conditional("DEBUG")]: Questo codice raggiungerà IL, tuttavia le chiamate al metodo verranno omesse a meno che DEBUG non sia impostato durante la compilazione del chiamante.

Personalmente uso entrambi a seconda della situazione:

Esempio condizionale ("DEBUG"): lo uso in modo da non dover tornare indietro e modificare il mio codice in seguito durante il rilascio, ma durante il debug voglio essere sicuro di non aver fatto errori di battitura. Questa funzione verifica che io digiti correttamente il nome di una proprietà quando provo ad usarlo nelle mie cose 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));
}

Non vuoi davvero creare una funzione usando a #if DEBUGmeno che tu non sia disposto a concludere ogni chiamata a quella funzione con la stessa #if DEBUG:

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

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

contro:

[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 esempio DEBUG: lo uso quando cerco di impostare diversi binding per la comunicazione WCF.

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

Nel primo esempio, il codice esiste tutto, ma viene semplicemente ignorato a meno che DEBUG non sia attivo. Nel secondo esempio, const ENDPOINT è impostato su "Localhost" o "BasicHttpBinding" a seconda che DEBUG sia impostato o meno.


Aggiornamento: sto aggiornando questa risposta per chiarire un punto importante e difficile. Se si sceglie di utilizzare il ConditionalAttribute, tenere presente che le chiamate vengono omesse durante la compilazione e non in fase di esecuzione . Questo è:

MyLibrary.dll

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

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

Quando la libreria viene compilata in modalità di rilascio (ovvero nessun simbolo DEBUG), per sempre la chiamata B()dall'interno verrà A()omessa, anche se A()viene inclusa una chiamata a perché DEBUG è definito nell'assembly chiamante.


13
Il debug #if per DoSomething non deve contenere tutte le istruzioni di chiamata circondate da #if DEBUG. puoi 1: solo # se DEBUG l'interno di DoSomething, oppure fai #else con una definizione vuota di DoSomething. Comunque il tuo commento mi ha aiutato a capire la differenza, ma se DEBUG non ha bisogno di essere brutto come hai dimostrato.
Apeiron

3
Se hai solo # se DEBUGI i contenuti, JIT potrebbe comunque includere una chiamata alla funzione quando il tuo codice viene eseguito in una build non di debug. L'uso dell'attributo Conditional significa che JIT non sa nemmeno generare l'output del nominativo in una build non DEBUG.
Jeff Yates

2
@JeffYates: non vedo come ciò che stai scrivendo sia diverso da quello che ho spiegato.
mio

1
@Apeiron se hai solo il contenuto della funzione nel debug #if, la chiamata della funzione viene comunque aggiunta allo stack di chiamate, mentre questo di solito non è molto importante, aggiungendo la dichiarazione e la chiamata della funzione a #if significa che il compilatore si comporta come se la funzione non esiste, quindi il mio metodo è il modo più "corretto" di usare #if. sebbene entrambi i metodi producano risultati indistinguibili l'uno dall'altro nell'uso normale
MikeT

5
se qualcuno si sta chiedendo, IL = Intermediate Language - en.wikipedia.org/wiki/Common_Intermediate_Language
jbyrd

64

Bene, vale la pena notare che non significano affatto la stessa cosa.

Se il simbolo DEBUG non è definito, nel primo caso lo SetPrivateValuestesso non verrà chiamato ... mentre nel secondo caso esisterà, ma tutti i chiamanti che sono compilati senza il simbolo DEBUG avranno quelle chiamate omesse.

Se il codice e tutti i suoi interlocutori sono nello stesso gruppo di questa differenza è meno importante - ma significa che nel primo caso è anche bisogno di avere #if DEBUGintorno al chiamante codice pure.

Personalmente consiglierei il secondo approccio, ma devi tenere chiara la differenza tra loro nella tua testa.


5
+1 per chiamare il codice dovrà avere anche le istruzioni #if. Ciò significa che ci sarà una proliferazione di dichiarazioni #if ...
Lucas B,

Mentre la seconda opzione (attributo condizionale) è più bella e più pulita in alcuni casi, potrebbe essere necessario comunicare il fatto che una chiamata al metodo verrebbe rimossa dall'assemblaggio durante la compilazione (ad esempio da una convenzione di denominazione).
acido lisergico,

45

Sono sicuro che molti non saranno d'accordo con me, ma dopo aver trascorso del tempo come un ragazzo di costruzione ascoltando costantemente "Ma funziona sulla mia macchina!", Prendo il punto di vista che non dovresti praticamente mai usare neanche. Se hai davvero bisogno di qualcosa per il test e il debug, cerca un modo per rendere tale testabilità separata dal codice di produzione effettivo.

Estrarre gli scenari con derisione nei test unitari, creare versioni una tantum delle cose per scenari una tantum che si desidera testare, ma non inserire i test per il debug nel codice per i binari che si testano e scrivono per la versione di produzione. Questi test di debug nascondono solo possibili bug dagli sviluppatori, in modo che non vengano trovati più avanti nel processo.


4
Sono totalmente d'accordo con te Jimmy. Se stai usando DI e derisione per i tuoi test, perché dovresti avere bisogno #if debugo un costrutto simile nel tuo codice?
Richard Ev,

@RichardEv Potrebbe esserci un modo migliore per gestirlo, ma attualmente lo sto usando per permettermi di interpretare la parte di diversi utenti tramite una stringa di query. Non lo voglio in produzione ma lo voglio per il debug in modo da poter controllare il flusso di lavoro che viene eseguito senza dover creare più utenti e accedere a entrambi gli account per seguire il flusso. Anche se questa è la prima volta che devo usarlo.
Tony,

4
Piuttosto che solo per i test, spesso facciamo cose come impostare un'e-mail del destinatario predefinita su noi stessi, nelle build di debug, in #if DEBUGmodo da non spammare accidentalmente altri durante il test di un sistema che deve trasmettere e-mail come parte del processo. A volte questi sono gli strumenti giusti per il lavoro :)
Gone Coding

6
In generale sarei d'accordo con te, ma se ti trovi in ​​una situazione in cui le prestazioni sono di primaria importanza, non vuoi ingombrare il codice con la registrazione estranea e l'output dell'utente, ma sono d'accordo al 100% che non dovrebbero mai essere usati per alterare il comportamento fondamentale
MikeT

5
-1 Non c'è niente di sbagliato nell'utilizzare uno di questi. Rivendicare unit test e DI in qualche modo sostituisce una build abilitata al debug di un prodotto è ingenuo.
Ted Bigham,

15

Anche questo può essere utile:

if (Debugger.IsAttached)
{
...
}

1
Personalmente, non vedo come questo possa essere utile rispetto alle altre 2 alternative. Ciò garantisce che l'intero blocco sia compilato e Debugger.IsAttacheddebba essere chiamato in fase di esecuzione anche nelle build di rilascio.
Jai,

9

Con il primo esempio, SetPrivateValuenon esisterà nella build se DEBUGnon è definito, con il secondo esempio, le chiamate a SetPrivateValuenon esisteranno nella build se DEBUGnon è definito.

Con il primo esempio, si dovrà avvolgere qualsiasi chiamata a SetPrivateValuecon #if DEBUGpure.

Con il secondo esempio, le chiamate a SetPrivateValueverranno omesse, ma attenzione che SetPrivateValueverrà comunque compilato. Questo è utile se stai costruendo una libreria, quindi un'applicazione che fa riferimento alla tua libreria può ancora usare la tua funzione (se la condizione è soddisfatta).

Se si desidera omettere le chiamate e salvare lo spazio del chiamante, è possibile utilizzare una combinazione delle due tecniche:

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

@P Papà: Spostamento #if DEBUGin giro Conditional("DEBUG")non rimuove le chiamate a tale funzione, rimuove solo la funzione da IL alltogether, così hai ancora chiamate a funzione che non esiste (errori di compilazione).
mio

1
Se non si desidera che il codice esista nella versione, si dovrebbe avvolgere il corpo del metodo in "#if DEBUG", possibilmente con uno stub "#else" (con un valore di ritorno o fittizio), e usare l'attributo per suggerire che i chiamanti non si preoccupano della chiamata? Sembrerebbe il migliore di entrambi i mondi.
supercat,

@myermian, @supercat: Sì, entrambi avete ragione. Errore mio. Modificherò secondo il suggerimento del supercat.
P Daddy,

5

Supponiamo che il tuo codice abbia anche #elseun'istruzione che definisce una funzione di stub null, indirizzando uno dei punti di Jon Skeet. C'è una seconda importante distinzione tra i due.

Supponiamo che la funzione #if DEBUGo Conditionalesista in una DLL a cui fa riferimento l'eseguibile del progetto principale. Usando il #if, la valutazione del condizionale verrà eseguita riguardo alle impostazioni di compilazione della libreria. Utilizzando l' Conditionalattributo, la valutazione del condizionale verrà eseguita in relazione alle impostazioni di compilazione dell'invocatore.


2

Ho un'estensione SOAP WebService per registrare il traffico di rete utilizzando un'abitudine [TraceExtension]. Lo uso solo per build di debug e ometto dalle build di rilascio . Utilizzare il #if DEBUGper avvolgere l' [TraceExtension]attributo rimuovendolo così dalle build di rilascio .

#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

Di solito ne avresti bisogno in Program.cs dove vuoi decidere di eseguire il debug su codice non di debug e quello troppo principalmente nei servizi di Windows. Quindi ho creato un campo di sola lettura IsDebugMode e impostato il suo valore nel costruttore statico come mostrato di seguito.

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        
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.