2010-09-28 26 views
7

Selon la documentation sur la classe ConditionalAttribute:Est-ce que ConditionalAttribute est censé supprimer des lignes entières ou seulement des appels de méthode?

Application ConditionalAttribute à une méthode indique aux compilateurs qu'un appel à la méthode ne doit pas être compilé en langage intermédiaire Microsoft (MSIL) à moins que le conditionnel Le symbole de compilation associé avec ConditionalAttribute est défini.

Pour moi, cela signifie que l'attribut Conditional modifie uniquement le comportement au niveau de l'appel de méthode individuel. Mais considérez l'extrait de code suivant:

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

Comparer les sorties de débogage et versions Release:

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

Est-ce comportement spécifié quelque part? Je pensais que les deux builds étaient supposés avoir la même sortie à l'exception des lignes indiquant "Conditional method run".

+0

Ma compréhension de '[conditionnelle]' est le même que le vôtre, et Je pense que vous voyez l'effet d'une optimisation se produire ici. A quoi ressemble l'IL en mode release? –

+0

Fait intéressant, même Visual Studio (ou est-ce ReSharper?) Colore toute la ligne grise/inutilisée dans le premier cas, si vous ne définissez pas DEBUG. Alors que dans la seconde, il ne fait que colorier l'appel à ConditionalMethod() gray/unused. –

+0

duplication possible de [Pourquoi l'attribut conditionnel de .NET provoque-t-il l'élimination des effets de bord?] (Http://stackoverflow.com/questions/410865/why-does-nets-conditional-attribute-cause-side-effects-to -be-removed) –

Répondre

2

Fonction interessante :-) Je n'ai jamais remarqué cela.

J'ai jeté un oeil à l'IL. Cela n'explique pas le comportement (le processus de compilation), mais il documente le résultat de toute façon, je crois.

La ligne entière de code C# est clairement laissé dans l'IL:

  • Dans la compilation DEBUG un nouvel objet est créé (la variable x), stockée à l'emplacement 0 et chargé. Ensuite les trois méthodes sont appliquées successivement: DoSideEffects(), DeMoreSideEffects(), et ConditionalMethod()
  • Dans la compilation RELEASE la variable devient encore créé, mais comme il n'est pas nécessaire, il est immédiatement pop'ed . Au lieu de cela, la variable y est stockée à l'emplacement 0 et chargée.

Pour moi, cela ressemble vraiment à un bug. Il semble qu'il aurait été possible de simplement exclure l'appel ConditionalMethod() dans l'IL. Mais il semble que vous avez raison, que toute la ligne est laissée de côté.

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

Désolé de glisser un tel post ancien, mais je viens de rencontrer la même chose et ce fut la seule discussion de ce problème que je pouvais trouver.

J'ai une idée de ce qui se passe. Le [Conditional] supprime l'appel à ConditionalMethod()ainsi que toutes les expressions qui agissent en tant que paramètres passés à lui (selon la documentation, et l'autre thread lié ci-dessus). Je suppose que le paramètre this implicite est traité exactement de la même manière. Dans la ligne x.DoSideEffects().DoMoreSideEffects().ConditionalMethod(); l'expression qui est passée comme this est x.DoSideEffects().DoMoreSideEffects() qui est consciencieusement dénudé, en éliminant les effets secondaires.

Si nous réécrivons en code pseudo où nous passons explicitement this comme premier paramètre à chaque méthode, il devient beaucoup plus claire:

ConditionalMethod(DoMoreSideEffects(DoSideEffects(x)));