#if DEBUG vs. Condizionale (“DEBUG”)

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) { ... } 

Dipende davvero da cosa stai andando:

  • #if DEBUG : Il codice qui non raggiungerà nemmeno l’IL al momento del rilascio.
  • [Conditional("DEBUG")] : questo codice raggiungerà l’IL, tuttavia le chiamate al metodo verranno omesse a meno che DEBUG sia impostato quando il chiamante è compilato.

Personalmente uso entrambi a seconda della situazione:

Condizionale (“DEBUG”) Esempio: Io uso questo in modo che non debba tornare indietro e modificare il mio codice più tardi durante il rilascio, ma durante il debug voglio essere sicuro di non aver fatto errori di battitura. Questa funzione controlla che io digiti correttamente il nome di una proprietà quando provo ad usarlo nella mia roba 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)); } 

Davvero non vuoi creare una funzione usando #if DEBUG meno che non sia disposto a racchiudere tutte le chiamate su quella funzione con lo stesso #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 associazioni diverse per le comunicazioni WCF.

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

Nel primo esempio, il codice esiste, ma viene semplicemente ignorato a meno che DEBUG 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 delicato. Se si sceglie di utilizzare ConditionalAttribute , tenere presente che le chiamate vengono omesse durante la compilazione e non in fase di runtime . Questo è:

MyLibrary.dll

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

Quando la libreria è compilata in base alla modalità di rilascio (ovvero senza il simbolo DEBUG), avrà sempre la chiamata a B() dall’interno di A() omessa, anche se è inclusa una chiamata a A() perché DEBUG è definito nell’assembly chiamante .

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

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

Se il codice e tutti i suoi chiamanti si trovano nello stesso assembly, questa differenza è meno importante, ma significa che nel primo caso è necessario avere anche #if DEBUG attorno al codice chiamante .

Personalmente raccomanderei il secondo approccio – ma è necessario mantenere la differenza tra di loro chiaro nella tua testa.

Sono sicuro che molti non saranno d’accordo con me, ma avendo trascorso del tempo come un tipo costruttivo che sentiva costantemente “Ma funziona sulla mia macchina!”, Considero il fatto che non dovresti neanche più usarlo. Se hai davvero bisogno di qualcosa per test e debug, trova un modo per rendere questa testabilità separata dal codice di produzione attuale.

Astrarre gli scenari con il mocking nei test unitari, creare una versione off delle cose per uno degli scenari che si desidera testare, ma non mettere i test per il debug nel codice per i binari che si testano e scrivono per il rilascio di produzione. Questi test di debug nascondono possibili errori dagli sviluppatori in modo che non vengano trovati fino a un momento successivo.

Questo può essere utile pure:

 if (Debugger.IsAttached) { ... } 

Con il primo esempio, SetPrivateValue non esisterà nel build se DEBUG non è definito, con il secondo esempio, le chiamate a SetPrivateValue non saranno presenti nel build se DEBUG non è definito.

Con il primo esempio, dovrai anche SetPrivateValue le chiamate a SetPrivateValue con #if DEBUG .

Con il secondo esempio, le chiamate a SetPrivateValue verranno omesse, ma tenere presente che SetPrivateValue verrà 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 vuoi omettere le chiamate e salvare lo spazio del callee, puoi utilizzare una combinazione delle due tecniche:

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

Supponiamo che il tuo codice abbia anche un’istruzione #else 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 DEBUG o Conditional esista in una DLL a cui fa riferimento l’eseguibile del progetto principale. Utilizzando #if , la valutazione del condizionale verrà eseguita in relazione alle impostazioni di compilazione della libreria. Utilizzando l’attributo Conditional , la valutazione del condizionale verrà eseguita in relazione alle impostazioni di compilazione del invoker.

Ho un’estensione WebService SOAP per registrare il traffico di rete utilizzando un [TraceExtension] personalizzato. Io uso questo solo per le build di Debug e ometto dalle build di Release. Usa #if DEBUG per avvolgere l’attributo [TraceExtension] rimuovendolo dalle build Release.

 #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(...)