当C#编译器的内容具有Conditional属性时,它是否会优化foreach块?

我正在编写一些调试代码,我想知道我在做什么可能会损害性能。

我们来看看代码:

foreach (var item in aCollection) Debug.WriteLine(item.Name); 

我知道Debug类使用Conditional属性来避免在发布模式下编译(或者每当DEBUG未定义时),但是当在发布模式下编译时,这最终会成为无用/空迭代,还是会被编译器优化?

C#编译器不会优化枚举,因为枚举集合的行为可能会产生副作用:

  1. 在数组的情况下,索引到数组中的行为意味着副作用(并且C#编译器将数组上的foreach循环重写为索引循环)。
  2. 对于其他集合,对GetEnumerator()MoveNext()意味着副作用。

在这两种情况下,潜在的null取消引用都是副作用。

在调用[Conditional]方法时,只会从编译的代码中省略方法调用及其forms参数。 请注意,即使是带副作用的参数也会被省略。 但是,不会省略任何周围的代码

我自己的测试表明,即使对于一个简单的数组,即使添加显式null检查也不会诱使C#编译器优化掉枚举。

JIT编译器是否优化了枚举代码是另一个问题。 它可能,如果它可以certificate该集合总是非null 并且没有其他有意义的副作用。 JIT 可能足够复杂,可以为数组做到这一点; 不过,我不会赌它。 如果增加的开销涉及到您,请将枚举代码放在#if区域中,如@pid所示。

您没有在此处利用任何编译器符号。 把它裹在这些里面:

 #if DEBUG // your code here #endif 

这种方法的优点:

  • [Conditional]方法属性大大降低了可读性。 在调用方面,在编译代码中不会发生调用并不明显。 团队应避免采用这种做法,而采用更明确的条件编译方法。 即便评论也是不可取的,因为大型团队中总会有人忘记评论此类评论。 相反,上面的示例很容易阅读(VS2010 +甚至在不是当前构建配置文件的一部分时对文本进行着色)。
  • 构建集合可能非常昂贵(eG使用数据库中的数据构建1000个项目)。 在这些情况下,编译符号允许更容易和更清晰地剔除感兴趣的代码,而不会留下空循环或集合的构造。

那个foreach不会被抹去。

编译后的代码如下:

 foreach (var item in aCollection) { ; } 

无论如何都将列举该集合。