2010-09-28 5 views
7

Secondo la documentazione sulla classe ConditionalAttribute:È previsto che ConditionalAttribute escluda intere righe o solo chiamate di metodo?

Applicando ConditionalAttribute a un metodo indica a compilatori che un chiamata al metodo non deve essere compilato in Microsoft Intermediate lingua (MSIL) a meno che il condizionale il simbolo di compilazione associato con ConditionalAttribute è definito.

Per me questo sta dicendo che l'attributo Conditional altera solo il comportamento al livello di chiamata del singolo metodo. Ma si consideri il seguente frammento di codice:

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(); 
    } 
} 

Confrontare le uscite di debug e build di rilascio:

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

È questo il comportamento specificato da qualche parte? Avevo pensato che entrambe le versioni avrebbero dovuto avere lo stesso output tranne per le righe che leggevano "Esecuzione del metodo condizionale".

+0

mia comprensione di '[condizionale]' è uguali ai suoi, e Penso che tu stia vedendo l'effetto di un'ottimizzazione che si verifica qui. Che aspetto ha IL in modalità di rilascio? –

+0

È interessante notare che anche Visual Studio (o ReSharper?) Colora l'intera linea grigia/non utilizzata nel primo caso, se non definisci DEBUG. Considerando che nel secondo, colora la chiamata a ConditionalMethod() grigio/non usato. –

+0

possibile duplicato di [Perché l'attributo condizionale di .NET causa la rimozione degli effetti collaterali?] (Http://stackoverflow.com/questions/410865/why-does-nets-conditional-attribute-cause-side-effects-to -be-removed) –

risposta

2

Caratteristica di interattività :-) Non l'ho mai notato.

Ho dato un'occhiata all'IL. Questo non spiega il comportamento (il processo di compilazione), ma documenta comunque il risultato, credo.

L'intera linea di codice C# è chiaramente lasciato fuori nella IL:

  • Nella compilation debug viene creato un nuovo oggetto (la variabile x), conservati a posizione 0 e caricato. Successivamente i tre metodi sono applicati successione: DoSideEffects(), DeMoreSideEffects(), e ConditionalMethod()
  • Nella compilazione RELEASE variabile ancora viene creato, ma dal momento che non è necessario, viene immediatamente pop'ed . Invece la variabile y è memorizzata nella posizione 0 e caricata.

Per me, questo sembra un bug, davvero. Sembra che sarebbe stato possibile escludere semplicemente la chiamata ConditionalMethod() nell'IL. Ma sembra che tu abbia ragione, che l'intera linea sia lasciata fuori.

// 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

spiacenti trascinare fino ad un vecchio post, ma ho appena incontrato la stessa cosa e questo era l'unico discussione di questo problema che ho potuto trovare.

Ho l'impressione di cosa sta succedendo. Lo [Conditional] interrompe la chiamata a ConditionalMethod()e qualsiasi espressione che funge da parametro passato (come per la documentazione e l'altro thread collegato sopra).

La mia ipotesi è che il parametro implicito this venga trattato esattamente nello stesso modo. Nella riga x.DoSideEffects().DoMoreSideEffects().ConditionalMethod(); l'espressione passata come this è x.DoSideEffects().DoMoreSideEffects() che viene diligentemente eliminata, eliminando gli effetti collaterali.

Se si riscrive in pseudo-codice in cui si passa in modo esplicito this come primo parametro di ogni metodo diventa molto più chiaro:

ConditionalMethod(DoMoreSideEffects(DoSideEffects(x)));

Problemi correlati