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

时间:2011-04-21 22:31:35

标签: c# debugging assert

我最近浏览了一些代码,并考虑是否需要谨慎处理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类是特殊的,还是可以用类似的处理方法构建自己的方法?
  • 有没有办法在这里“欺骗”预处理器?更好的是,在现实世界的代码中可能会出现哪些情况可能会出现问题?

3 个答案:

答案 0 :(得分:23)

Debug.Assert声明为ConditionalAttribute;正如文档所述,除非定义了指定的条件编译符号,否则编译器应该忽略方法调用或属性。"

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

如果您右键单击其中一个Debug.Assert语句,则应该可以转到该定义。 Visual Studio将向您显示"代码"从元数据生成,您可以看到应用了[Conditional("DEBUG")]属性。因此,只有当DEBUG #define作为您构建的一部分时,才会尊重此代码。

答案 1 :(得分:5)

调试器上的方法使用伪自定义属性ConditionalAttribute,编译器检测并删除对具有该属性的任何方法的任何调用,除非指定的编译符号(在本例中为DEBUG ) 被定义为。任何人都可以使用void方法中的属性而不使用任何out参数。

答案 2 :(得分: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");
}