代码之家  ›  专栏  ›  技术社区  ›  Dan Tao

ConditionalAttribute是应该去掉整行,还是仅仅是方法调用?

  •  7
  • Dan Tao  · 技术社区  · 14 年前

    ConditionalAttribute 班级:

    将条件属性应用于 方法向编译器指示 对方法的调用不应为 编译为Microsoft中级 语言(MSIL),除非 关联的编译符号 已定义with conditional属性。

    对我来说这意味着 Conditional

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

    比较调试和发布版本的输出:

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

    这种行为是在什么地方规定的吗? 我以为这两个构建都应该有相同的输出,除了读取“Conditional method run”的行

    2 回复  |  直到 14 年前
        1
  •  2
  •   Maate    14 年前

    有趣的特点:我从来没注意到。

    整个C#代码行在IL中明显被忽略了:

    • 在调试编译中 创建(x变量),存储 三种方法都适用 依次为:DoSideEffects(), 条件方法()
    • 在版本编译中,仍然会创建该变量,但由于不需要它,它会立即弹出。相反,y变量存储在位置0并加载。

    对我来说,这看起来像只虫子,真的。似乎可以在IL中排除ConditionalMethod()调用。但似乎你是对的,整条线都被遗漏了。

    // 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
    
        2
  •  0
  •   Tim McSweeney    9 年前

    抱歉拖出这么一个旧帖子,但我遇到了同样的事情,这是我唯一能找到的关于这个问题的讨论。

    我对发生的事有预感。这个 [Conditional] 正在取消呼叫 ConditionalMethod() 作为传递给它的参数的任何表达式(根据文档和上面链接的另一个线程)。

    我的猜测是 this x.DoSideEffects().DoMoreSideEffects().ConditionalMethod(); 传递为 x.DoSideEffects().DoMoreSideEffects() 这是尽职的剥离,消除了副作用。

    如果我们重写成伪代码 作为每种方法的第一个参数,它变得更加清晰:

    ConditionalMethod( DoMoreSideEffects( DoSideEffects( x )));