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

.net ConditionalAttribute应该去掉整行,还是仅仅是方法调用?,.net,debugging,conditional,release,.net,Debugging,Conditional,Release,根据课程的文档: 将条件属性应用于 方法向编译器指示 对方法的调用不应为空 编译成微软中间版本 语言(MSIL),除非条件 关联的编译符号 定义了带有条件的属性 对我来说,这意味着Conditional属性只改变单个方法调用级别的行为。但是请考虑下面的代码片段: class InstanceType { public InstanceType DoSideEffects() { Console.WriteLine("Side effects!");

根据课程的文档:

将条件属性应用于 方法向编译器指示 对方法的调用不应为空 编译成微软中间版本 语言(MSIL),除非条件 关联的编译符号 定义了带有条件的属性

对我来说,这意味着
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. 调试版本 副作用!副作用! 更多的副作用!更多的副作用! 条件方法运行。 副作用! 更多的副作用! 条件方法运行。 这个行为是在什么地方指定的吗?我原以为两个构建应该有相同的输出,除了读“条件方法运行”的行。

有趣的特性:-)我从来没有注意到这一点

我已经看了一眼IL。这并不能解释行为(编译过程),但我相信它记录了结果

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

  • 在调试编译中,一个新对象 创建(x变量),存储 在位置0处加载。之后 应用这三种方法 依次为:dosDeEffects(), DeMoreSideEffects(),以及 条件方法()
  • 在版本编译中,仍然会创建变量,但由于不需要它,因此会立即弹出该变量。相反,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

很抱歉,我拖了这么一篇老文章,但我遇到了同样的事情,这是我能找到的关于这个问题的唯一讨论

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

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

如果我们重写为伪代码,在其中显式地传递
这个
作为每个方法的第一个参数,它会变得更加清晰:


ConditionalMethod(DoMoreSideEffects(x))

我对
[条件]
的理解与你的理解相同,我认为你看到了优化的效果。在发布模式下,IL看起来是什么样子?有趣的是,如果不定义调试,在第一种情况下,即使是Visual Studio(或者它是ReSharper?)也会将整行显示为灰色/未使用。而在第二个问题中,它只将对ConditionalMethod()的调用着色为灰色/unused。。。但是,另一个问题询问了由于评估传递给标有
条件属性的方法的参数而产生的副作用。这是关于方法调用的序列,提供调用所述方法的实例。因此,我觉得这两个方面是非常不同的。如果其他人不同意并投票关闭,我想这也没关系。没错,尽管这个问题的答案回答了你的问题(特别是关于条件属性所取代的
\If
/
\endif
)。