【问题标题】:Is ConditionalAttribute supposed to strip out entire lines, or just method calls?ConditionalAttribute 应该去掉整行,还是只去掉方法调用?
【发布时间】:2010-09-28 01:46:45
【问题描述】:

根据ConditionalAttribute 类的文档:

将 ConditionalAttribute 应用于 方法向编译器表明一个 不应该调用该方法 编译成微软中级 语言 (MSIL) 除非有条件 关联的编译符号 定义了 ConditionalAttribute。

对我来说,这就是说Conditional 属性只会改变单个方法调用级别的行为。但是考虑下面的代码sn-p:

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

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

调试发布 副作用!副作用! 更多副作用!更多副作用! 条件方法运行。 副作用! 更多副作用! 条件方法运行。

此行为是否在某处指定?我原以为两个构建应该具有相同的输出,除了“条件方法运行”行。

【问题讨论】:

  • 我对@9​​87654326@的理解和你的一样,我想你看到这里发生了优化的效果。发布模式下的 IL 是什么样的?
  • 有趣的是,如果您没有定义 DEBUG,即使是 Visual Studio(或者是 ReSharper?)在第一种情况下也将整行着色为灰色/未使用。而在第二个中,它只会将 ConditionalMethod() 调用着色为灰色/未使用。
  • @Adam:诚然,这两个问题是相似的......但是,另一个问题询问了由于评估传递给 to 标记的方法的参数而产生的副作用带有Conditional 属性。这个是关于方法调用的序列,提供调用所述方法的实例。所以我觉得两者足够不同。如果其他人不同意并投票关闭,我想这也很好。
  • 是的,尽管该问题的答案回答了您的问题(特别是关于 #if/#endif 作为 ConditionalAttribute 正在取代的要点)。

标签: .net debugging conditional release


【解决方案1】:

有趣的功能 :-) 我从来没有注意到。

我查看了 IL。这并没有解释行为(编译过程),但我相信它无论如何都会记录结果。

整个 C# 代码行在 IL 中显然被遗漏了:

  • 在 DEBUG 编译一个新对象 被创建(x 变量),存储 在位置 0 并加载。然后 三种方法都适用 先后:DoSideEffects(), DeMoreSideEffects() 和 条件方法()
  • 在 RELEASE 编译中,仍然会创建变量,但由于不需要它,因此会立即弹出它。相反,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】:

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

    我对正在发生的事情有预感。 [Conditional] 正在剥离对ConditionalMethod() 的调用以及作为传递给它的参数的任何表达式(根据文档和上面链接的其他线程)。

    我的猜测是隐式this 参数的处理方式完全相同。在x.DoSideEffects().DoMoreSideEffects().ConditionalMethod(); 行中,作为this 传递的表达式是x.DoSideEffects().DoMoreSideEffects(),它被尽职尽责地剥离,消除了副作用。

    如果我们改写成伪代码,我们明确地将this 作为第一个参数传递给每个方法,它会变得更加清晰:

    ConditionalMethod( DoMoreSideEffects( DoSideEffects( x )));

    【讨论】:

      猜你喜欢
      • 2019-08-19
      • 1970-01-01
      • 1970-01-01
      • 2012-01-24
      • 2017-11-10
      • 1970-01-01
      • 1970-01-01
      • 2015-04-30
      • 1970-01-01
      相关资源
      最近更新 更多