2010-09-28 5 views
7

De acuerdo con la documentación de la clase ConditionalAttribute:¿Se supone que ConditionalAttribute elimine líneas enteras, o solo llamadas a métodos?

Aplicando ConditionalAttribute a un método indica a los compiladores que una llamada con el método no debe ser compilar en Microsoft intermedia idioma (MSIL) a menos que el condicional El símbolo de compilación asociado con ConditionalAttribute está definido.

Para mí esto significa que el atributo Conditional solo altera el comportamiento en el nivel de llamada del método individual. Pero considere el siguiente fragmento de código:

class InstanceType 
{ 
    public InstanceType DoSideEffects() 
    { 
     Console.WriteLine("Side effects!"); 
     return this; 
    } 

    public InstanceType DoMoreSideEffects() 
    { 
     Console.WriteLine("More side effects!"); 
     return this; 
    } 

    [Conditional("DEBUG")] 
    public void ConditionalMethod() 
    { 
     Console.WriteLine("Conditional method run."); 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     var x = new InstanceType(); 

     // The compiler appears to strip out this entire line 
     // in a Release build. 
     x.DoSideEffects().DoMoreSideEffects().ConditionalMethod(); 

     var y = new InstanceType(); 

     // When each method call appears on its own line, 
     // the first two methods are included as expected. 
     y.DoSideEffects(); 
     y.DoMoreSideEffects(); 
     y.ConditionalMethod(); 
    } 
} 

Compare las salidas de depuración y las versiones de lanzamiento:

 
DEBUG     RELEASE 
Side effects!   Side effects! 
More side effects!  More side effects! 
Conditional method run. 
Side effects! 
More side effects! 
Conditional method run. 

¿Este comportamiento especificado en alguna parte? Pensé que se suponía que ambas construcciones tendrían el mismo resultado, excepto las líneas que decían "Ejecución de método condicional".

+0

Mi comprensión de '[condicional] 'es el mismo que el suyo, y Creo que está viendo el efecto de una optimización que tiene lugar aquí. ¿Cómo se ve el IL en el modo de lanzamiento? –

+0

Curiosamente, incluso Visual Studio (¿o es ReSharper?) Colorea toda la línea gris/sin usar en el primer caso, si no define DEBUG. Mientras que en el segundo, solo colorea la llamada a ConditionalMethod() gris/sin usar. –

+0

posible duplicado de [¿Por qué los efectos colaterales de causa condicional de .NET se eliminarán?] (Http://stackoverflow.com/questions/410865/why-does-nets-conditional-attribute-cause-side-effects-to -be-removed) –

Respuesta

2

Característica de interés :-) Nunca me había dado cuenta.

He echado un vistazo a la IL. Esto no explica el comportamiento (el proceso de compilación), pero documenta el resultado de todos modos, creo.

El conjunto C línea # código es claramente dejó fuera en la IL:

  • en la compilación de depuración se crea un nuevo objeto (la variable x), almacena en la posición 0 y cargado. Después los tres métodos se aplican sucesivamente : DoSideEffects(), DeMoreSideEffects(), y ConditionalMethod()
  • En la compilación LIBERACIÓN la variable todavía se crea, pero ya que no se necesita, se pop'ed inmediatamente . En cambio, la variable y se almacena en la ubicación 0 y se carga.

Para mí, esto parece un error, de verdad. Parece que habría sido posible excluir la llamada ConditionalMethod() en el IL. Pero parece que tienes razón, que toda la línea queda fuera.

// DEBUG compilation 
.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  58 (0x3a) 
    .maxstack 1 
    .locals init (class ConsoleApplication3.InstanceType V_0, 
      class ConsoleApplication3.InstanceType V_1) 
    IL_0000: nop 
    IL_0001: newobj  instance void ConsoleApplication3.InstanceType::.ctor() 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: callvirt instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoSideEffects() 
    IL_000d: callvirt instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoMoreSideEffects() 
    IL_0012: callvirt instance void ConsoleApplication3.InstanceType::ConditionalMethod() 
    IL_0017: nop 
    IL_0018: newobj  instance void ConsoleApplication3.InstanceType::.ctor() 
    IL_001d: stloc.1 
    IL_001e: ldloc.1 
    IL_001f: callvirt instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoSideEffects() 
    IL_0024: pop 
    IL_0025: ldloc.1 
    IL_0026: callvirt instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoMoreSideEffects() 
    IL_002b: pop 
    IL_002c: ldloc.1 
    IL_002d: callvirt instance void ConsoleApplication3.InstanceType::ConditionalMethod() 
    IL_0032: nop 
    IL_0033: call  valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() 
    IL_0038: pop 
    IL_0039: ret 
} // end of method Program::Main 

// RELEASE compilation 
.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  33 (0x21) 
    .maxstack 1 
    .locals init ([0] class ConsoleApplication3.InstanceType y) 
    IL_0000: newobj  instance void ConsoleApplication3.InstanceType::.ctor() 
    IL_0005: pop 
    IL_0006: newobj  instance void ConsoleApplication3.InstanceType::.ctor() 
    IL_000b: stloc.0 
    IL_000c: ldloc.0 
    IL_000d: callvirt instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoSideEffects() 
    IL_0012: pop 
    IL_0013: ldloc.0 
    IL_0014: callvirt instance class ConsoleApplication3.InstanceType ConsoleApplication3.InstanceType::DoMoreSideEffects() 
    IL_0019: pop 
    IL_001a: call  valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() 
    IL_001f: pop 
    IL_0020: ret 
} // end of method Program::Main 
0

Lo siento, arrastra una publicación tan antigua, pero me encontré con lo mismo y esta fue la única discusión sobre este tema que pude encontrar.

Tengo una idea de lo que está pasando. El [Conditional] está eliminando la llamada al ConditionalMethod(), así como a cualquier expresión que actúe como parámetro que se le haya pasado (según la documentación y el otro enlace vinculado anteriormente).

Supongo que el parámetro this implícito se está tratando exactamente de la misma manera. En la línea x.DoSideEffects().DoMoreSideEffects().ConditionalMethod();, la expresión que se pasa como this es x.DoSideEffects().DoMoreSideEffects() que se eliminó diligentemente, eliminando los efectos secundarios.

Si volvemos a escribir en pseudo código, donde se pasa explícitamente this como el primer parámetro para cada método se vuelve mucho más claro:

ConditionalMethod(DoMoreSideEffects(DoSideEffects(x)));

Cuestiones relacionadas