C#编译器是否删除封装debug.writeline的if
我有一段代码如下:C#编译器是否删除封装debug.writeline的if,c#,.net,compiler-optimization,C#,.net,Compiler Optimization,我有一段代码如下: if (state != "Ok") { Debug.WriteLine($"Error occured: {state}, {moreInfo}"); } 如果我进行发布构建,编译器是否会对此进行优化?或者评估是否会保留下来并因此花费一些处理时间?是的,它会保留下来,至少对于Debug调用是这样的。我在这里看不到JIT编译器是否也删除了对if的求值,但我想是的,因为这个等式没有任何副作用 但是,通过调用Debug.WriteLineIf,可以更好地保护它的安全性
if (state != "Ok")
{
Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
如果我进行发布构建,编译器是否会对此进行优化?或者评估是否会保留下来并因此花费一些处理时间?是的,它会保留下来,至少对于
Debug
调用是这样的。我在这里看不到JIT编译器是否也删除了对if
的求值,但我想是的,因为这个等式没有任何副作用
但是,通过调用Debug.WriteLineIf
,可以更好地保护它的安全性,因为它不依赖于JIT编译器来删除计算
为了完整性,编译器删除Debug.WriteLine
的证明
发布版本中的代码:
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 17 (0x11)
.maxstack 8
IL_0000: call string [mscorlib]System.Console::ReadLine()
IL_0005: ldstr "Ok"
IL_000a: call bool [mscorlib]System.String::op_Inequality(string,
string)
IL_000f: pop
IL_0010: ret
} // end of method Program::Main
调试生成中的代码:
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 42 (0x2a)
.maxstack 2
.locals init ([0] string state,
[1] bool V_1)
IL_0000: nop
IL_0001: call string [mscorlib]System.Console::ReadLine()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "Ok"
IL_000d: call bool [mscorlib]System.String::op_Inequality(string,
string)
IL_0012: stloc.1
IL_0013: ldloc.1
IL_0014: brfalse.s IL_0029
IL_0016: nop
IL_0017: ldstr "Error occured: {0}"
IL_001c: ldloc.0
IL_001d: call string [mscorlib]System.String::Format(string,
object)
IL_0022: call void [System]System.Diagnostics.Debug::WriteLine(string)
IL_0027: nop
IL_0028: nop
IL_0029: ret
} // end of method Program::Main
正如您所看到的,发布模式没有调用调试.WriteLine,而调试模式是这样做的。来自
如果使用Debug
类中的方法打印调试信息并使用断言检查逻辑,则可以使代码更加健壮,而不会影响产品的性能和代码大小
ConditionalAttribute
属性应用于Debug
的方法。支持ConditionalAttribute
的编译器忽略对这些方法的调用,除非将“DEBUG”定义为条件编译符号
如您所见,编译器将忽略对非调试版本上的Debug
成员的任何调用。但是,它不会阻止程序检查if语句。如果希望编译器也忽略If语句,可以使用a将整个块括起来,如下所示:
#if DEBUG
if (state != "Ok")
{
Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
#endif
语言规范要求C#编译器删除Debug
调用及其参数的计算
如果.NET JIT是一个复杂的JIT,它将确定string方法调用不会产生副作用,并且可以删除。NET JIT不是很复杂,所以实际上它仍然有可能调用该方法。让我们看看
在发布模式下编译程序,反编译并在4.6.2上以x64的形式运行,而无需调试器抑制优化
static void Main()
{
var state = GetState();
if (state != "Ok")
{
Debug.WriteLine(state);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static string GetState()
{
return "x";
}
C#编译器保留了字符串不等式调用的完整性:
我不确定规范是否允许对此进行优化,因为这可能是一种副作用的方法。不确定允许编译器对其进行什么假设
我们出色的JIT也没有取消呼叫:
(1) 是GetState()
,(2)是字符串=代码>
使用Debug.WriteLineIf
是因为:
17.4.2.1条件法
用条件属性修饰的方法是条件方法。Conditional属性通过测试条件编译符号来指示条件。对条件方法的调用被包括或省略,这取决于此符号是否在调用点定义。如果定义了符号,则包括呼叫;否则,省略呼叫(包括对接收器和呼叫参数的评估)
使用
Debug.WriteLineIf
方法。注意:没有单一的C#编译器。有几种C#编译器,每种都有许多版本。C#规范几乎没有(如果有的话)强制性的优化,因此您的答案可能只针对特定的编译器和特定的版本。据我所知,不是这样。这是一个黑盒子。我不知道Debug.WriteLineIf。我喜欢!谢谢那么,JIT删除您所要求的if的证据在哪里呢?JIT确实是一个黑盒子,它没有提供任何明确的/文档化的保证来删除if语句,但是您当然可以根据经验验证它是否会删除if语句。在调试器下运行编译后的代码并查看反汇编。确保在调试器下运行时启用JIT优化;我不记得它在VS中是否默认关闭。发布代码仍然调用System.String::op_
。我想说它仍然计算if
语句,但它被视为有一个空的主体。你能参考一下它在规范中指出的Debug.X
调用必须删除的地方吗?@Jeroenvanevel我补充道。