C#编译器如何在发布版本中删除Debug.Assert?

26

最近我在查看一些代码,考虑是否需要注意放置在Debug.Assert语句中的表达式,例如昂贵的操作或具有副作用的操作。不过,事实证明编译器非常聪明,能够完全删除Assert语句和内部表达式。

例如,以下代码仅会在调试构建时打印:

static void Main(string[] args)
{
    Debug.Assert(SideEffect());
}
private static bool SideEffect()
{
    Console.WriteLine("Side effect!");
    return true;
}

在发布版本中,这会提示使用了未初始化的变量 o

static void Main(string[] args)
{
    object o;
    Debug.Assert(Initialize(out o));
    o.ToString();
}
private static bool Initialize(out object o)
{
    o = new object();
    return true;
}

它甚至能够处理类似于这样的表达式(在两种情况下都打印 "After"):

static void Main(string[] args)
{
    if (false) Debug.Assert(true);
    Console.WriteLine("After");
}

编译器的聪明之处让我有些惊讶,它可以正确检测出Debug.Assert被移除的情况。这让我很好奇...

  • 这个语句是如何被移除的?在正确执行上述if语句之前,必须先构建表达式树并删除该语句。
  • System.Diagnostics.Debug类在这里是否特殊?还是可能创建具有类似处理方式的自定义方法?
  • 有没有什么方法可以在这里“欺骗”预处理器?更好的是,是否会在实际代码中遇到可能会产生问题的情况?

2
请参考http://blogs.msdn.com/b/ericlippert/archive/2009/09/10/what-s-the-difference-between-conditional-compilation-and-the-conditional-attribute.aspx,了解有关此问题的一些想法。 - Eric Lippert
3个回答

26

Debug.Assert 声明了 ConditionalAttribute;如文档所述,这"[i]ndicates to compilers that a method call or attribute should be ignored unless a specified conditional compilation symbol is defined." (表示编译器应忽略方法调用或属性,除非定义了指定的条件编译符号)。

C# 编译器对该属性具有特定支持,并在发布版本中删除 Debug.Assert,因此它永远不是构建表达式树的一部分。

如果你右键单击其中一个 Debug.Assert 语句,你应该能够转到定义。Visual Studio 将显示从元数据生成的“代码”,您可以在那里看到应用的 [Conditional("DEBUG")] 属性。 因此,当您的构建中定义了 DEBUG 时,才会考虑此代码。


5

调试器上的方法使用伪自定义属性 ConditionalAttribute,编译器会检测并移除任何带有该属性的方法调用,除非指定的编译符号 ( 在本例中为 DEBUG) 已定义。任何人都可以在没有任何 out 参数的情况下在 void 方法上使用该属性。


1
应强调的是,它会删除没有定义匹配符号的调用 - 它可以是任何符号,而不仅仅是DEBUG。此外,禁止使用“out”参数。 - Marc Gravell
MSDN文档指出:“符合公共语言规范(CLS)的编译器可以忽略ConditionalAttribute。C#,J#和Visual Basic编译器支持ConditionalAttribute”。这是否意味着Visual Studio的C#编译器不符合CLS?对我来说,这个备注很神秘。 - Gerard
不,这只是意味着符合CLS的编译器不必支持ConditionalAttribute,因此它是可选的。C#与CLS兼容。 - Mark Cidade

4
我不认为Debug.Assert有什么特别之处;它只是使用了Conditional属性,以便编译器在检测到“预处理器”定义不存在时将其删除(C#没有预处理器!)。
您可以像这样使用它来做相同的事情(只要您定义了DEBUG(或任何想要开启的符号,TRACE是另一个流行的符号):
[Conditional("DEBUG"), Conditional("TRACE")]
public void DebugOnlyMethod() {
    Console.WriteLine("Won't see me unless DEBUG or TRACE is defined");
}

1
我愿意打赌,Debug类是当初推动ConditionalAttribute产生的原因。 - Matt Greer
@Matt Greer:很可能,它们似乎是唯一使用它的框架类。 - Ron Warholic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接