C#编译器如何在发布版本中删除Debug.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类在这里是特殊的,还是可以使用类似的处理来构建自己的方法?
  • 有没有办法在这里“欺骗”预处理器? 更好的是,在现实世界的代码中可能遇到的情况可能会有问题吗?

Debug.Assert是用ConditionalAttribute声明的; 正如文档所述,这“[i]指示编译器应该忽略方法调用或属性,除非定义了指定的条件编译符号。”

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

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

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

我不相信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"); }