2010-09-24 12 views
349

¿Qué es mejor usar, y por qué, en un gran proyecto:#if DEBUG vs condicional ("Test")

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

o

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

Ver http://blogs.msdn.com/b/ericlippert/archive/2009/09/10/what-s-the- difference-between-conditional-compilation-and-the-conditional-attribute.aspx para algunas reflexiones sobre esta cuestión. –

+0

puede usar esto también: if (Debugger.IsAttached) {...} – sofsntp

Respuesta

471

Realmente depende de lo que vamos a dar:

  • #if DEBUG: El código aquí ni siquiera llegará a IL en el lanzamiento.
  • [Conditional("DEBUG")]: Este código llegará a la IL; sin embargo, llamadas al método se omitirán a menos que DEBUG se establezca cuando la persona que llama se compila.

Personalmente utilizo ambos, dependiendo de la situación:

condicional ("Test") Ejemplo: Puedo utilizar esto para que yo no tengo que volver atrás y editar mi código más tarde durante la liberación, pero durante la depuración quiero asegurarme de no haber cometido errores tipográficos. Esta función comprueba que escribo un nombre de propiedad correctamente cuando trato de usarlo en mi material 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)); 
} 

Realmente no desea crear una función utilizando #if DEBUG menos que esté dispuesto para envolver cada llamada a la función con el mismo #if DEBUG:

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

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

frente:

[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 ejemplo: Lo uso cuando trato de configurar diferentes enlaces para la comunicación WCF.

En el primer ejemplo, el código existe, pero se ignora a menos que DEBUG esté activado. En el segundo ejemplo, la const ENDPOINT se establece en "Localhost" o "BasicHttpBinding" dependiendo de si DEBUG está configurado o no.


Actualización: Debido a que esta respuesta es la más alta votaron respuesta a la pregunta, estoy actualizando esta respuesta para aclarar un punto importante y difícil. Si elige usar el ConditionalAttribute, tenga en cuenta que las llamadas se omiten durante la compilación y no en el tiempo de ejecución. Es decir:

MyLibrary.dll

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

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

Cuando la biblioteca se compila contra el modo de liberación (es decir, sin símbolos de depuración), será siempre tienen la llamada a B() desde dentro A() omitido, incluso si una llamada a A() está incluido porque DEBUG se define en el conjunto de llamadas.

+9

La depuración #if para DoSomething no necesita tener todas las instrucciones de llamada rodeadas por #if DEBUG. puede 1: simplemente # si DEBUG el interior de DoSomething, o bien, haga #else con una definición en blanco de DoSomething. Aun así, tu comentario me ayudó a comprender la diferencia, pero # si DEBUG no tiene que ser tan feo como lo has demostrado. – Apeiron

+2

Si simplemente #if DEBUG el contenido, el JIT aún puede incluir una llamada a la función cuando su código se ejecuta en una compilación que no es de depuración. Usar el atributo Condicional significa que el JIT no sabe ni siquiera dar salida al callsite cuando está en una compilación que no es DEBUG. –

+0

Condicional no excluye el código. Lo marca con metadatos. El valor de DEPURACIÓN se compara en el momento de la compilación del código de LLAMADA. Esto puede estar dentro del conjunto que tiene el método marcado o fuera de él. –

54

Bueno, vale la pena señalar que no significan lo mismo en absoluto.

Si no se define el símbolo de depuración, a continuación, en el primer caso la SetPrivateValue en sí no se llamará ... mientras que en el segundo caso, existirá, pero cualquier personas que llaman que se compilan sin símbolos de depuración tendrá esas llamadas omitidas.

Si el código y todas sus llamadas están en la misma asamblea esta diferencia es menos importante - pero significa que en el primer caso que También necesita tener #if DEBUG alrededor del código llamando así.

Personalmente recomendaría el segundo enfoque, pero es necesario que mantenga la diferencia entre ellos clara en su cabeza.

+3

+1 para el código de llamada necesitará también tener las sentencias #if. Lo que significa que habrá una proliferación de sentencias #if ... –

+0

Si bien la segunda opción (atributo Condicional) es más clara y limpia en algunos casos, puede ser necesario comunicar el hecho de que una llamada a un método se eliminaría del ensamblado durante compilación (por una convención de nombres, por ejemplo). –

9

Con el primer ejemplo, SetPrivateValue no existirá en la acumulación DEBUG si no se define, con el segundo ejemplo, llama a SetPrivateValue no existirá en la acumulación DEBUG si no está definido.

Con el primer ejemplo, deberá ajustar todas las llamadas a SetPrivateValue con #if DEBUG también.

Con el segundo ejemplo, se omitirán las llamadas a SetPrivateValue, pero tenga en cuenta que el propio SetPrivateValue se compilará. Esto es útil si está creando una biblioteca, por lo que una aplicación que haga referencia a su biblioteca aún puede usar su función (si se cumple la condición).

Si desea omitir las llamadas y ahorrar el espacio del destinatario de la llamada, se puede utilizar una combinación de las dos técnicas:

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

@P Daddy: Envolver '# si DEPURAR' alrededor de' Condicional ("DEPURAR") 'no elimina las llamadas a esa función, simplemente elimina la función de IL completamente, por lo que todavía tiene llamadas para funcionar que no funciona t existe (errores de compilación). –

+0

Si no se quiere que el código exista en la versión, se debe ajustar el cuerpo del método en "#if DEBUG", posiblemente con un stub "#else" (con un valor de retorno throw o dummy), y usar el atributo para sugieren que las personas que llaman no se molesten con la llamada? Eso parecería lo mejor de ambos mundos. – supercat

+0

@myermian, @supercat: Sí, ambos tienen razón. Mi error. Voy a editar según la sugerencia de supercat. –

4

Vamos a suponer que su código también tenía una declaración #else que definía una función de código nulo, abordando uno de los puntos de Jon Skeet. Hay una segunda distinción importante entre los dos.

Supongamos que existe la función #if DEBUG o Conditional en una DLL a la que hace referencia el ejecutable de su proyecto principal. Con el #if, la evaluación del condicional se realizará con respecto a la configuración de compilación de la biblioteca. Con el atributo Conditional, la evaluación del condicional se realizará con respecto a la configuración de compilación del invocador.

36

Estoy seguro de que muchos estarán en desacuerdo conmigo, pero después de haber pasado un tiempo como un chico de la construcción escuchando constantemente "¡Pero funciona en mi máquina!", Considero que casi nunca deberías usar ninguno. Si realmente necesita algo para probar y depurar, encuentre una manera de hacer que esa capacidad de prueba se separe del código de producción real.

Resuma los escenarios con burlas en las pruebas unitarias, realice una versión de las cosas para escenarios únicos que desee probar, pero no ponga pruebas para depurar en el código los binarios que prueba y escribe para el lanzamiento de producción. Estas pruebas de depuración simplemente ocultan posibles errores de los desarrolladores para que no se encuentren hasta más adelante en el proceso.

+3

Estoy totalmente de acuerdo contigo Jimmy. Si usa DI y burlas para sus pruebas, ¿por qué necesitaría '#if debug' o cualquier construcción similar en su código? –

+0

@RichardEv Puede haber una manera mejor de manejar esto, pero actualmente lo estoy usando para permitirme jugar el papel de diferentes usuarios a través de una cadena de consulta. No quiero que esto esté en producción, pero sí quiero depurarlo para poder controlar el flujo de trabajo que se realiza sin tener que crear múltiples usuarios e iniciar sesión en ambas cuentas para seguir el flujo. Aunque esta es la primera vez que tengo que usarla. – Tony

+3

En lugar de simplemente probar, a menudo hacemos cosas como configurar un correo electrónico de destinatario predeterminado para nosotros mismos, en compilaciones de depuración, usando '#if DEBUG' para no enviar spam accidentalmente a otros mientras probamos un sistema que debe transmitir correos electrónicos como parte del proceso. A veces estas son las herramientas adecuadas para el trabajo :) –

0

Tengo una extensión SOAP WebService para registrar el tráfico de red usando una [extensión de seguimiento] personalizada. Lo uso solo para compilaciones de depuración y omito de versiones de lanzamiento. Use #if DEBUG para ajustar el atributo [TraceExtension] y eliminarlo de las compilaciones de 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(...) 
6

Ésta puede ser útil también:

if (Debugger.IsAttached) 
{ 
... 
}