编译器如何处理始终为true或false的语句?

时间:2019-06-13 21:55:42

标签: .net

每当我需要暂时禁用代码段中的一段代码时,我都会将其包装在始终为假的语句中,例如:

if (1 == 2)
{
    // disabled code
}

,当我需要重新启用它时,我将语句的评估结果设置为true。

if (1 == 1)
{
    // enabled code
}

显然总是总是分别返回false / true。

编译器如何处理这样的实例,使其显然总是失败/通过?它是否每次都会得到优化或评估?

3 个答案:

答案 0 :(得分:3)

您可以(可能应该)使用#if

#if DEV1

#endif

您可以在整个项目的“项目选项”中设置(或取消设置)DEV1。

还请注意,您可以在类或方法之前添加一个标题:

[Conditional("DEV1")]
void TestMethod() {}

...
TestMethod();  // this call will be eliminated if DEV1 is not set

要回答主要问题:是的,if(1==2){ ... }中的代码将被删除。
但这真的重要吗?对于开发者而言?

每次使用它都会产生一个或多个警告。

答案 1 :(得分:2)

我在.NET Fiddle上尝试了一个非常简单的假设代码示例,并检查了IL,然后:编译器检测到1 == 2始终为假,并将其替换为。 一条指令:nop 什么也没有。哇!

然后,我尝试了另一种条件Math.E == 2,但这也有效。出于良好的考虑,我尝试了Math.Pow(3, 2) == 10,但是并没有得到优化,可能是因为它是方法调用。

TL; DR:C#编译器对此似乎有点聪明,但只有在编译时就可以证明的事情。

有关原始问题的更惯用的方法,请参见Henk's answer

答案 2 :(得分:1)

C#语言规范描述了constant expressions,它们是可以在编译时求值的表达式。编译器将计算常量表达式,并像程序声明了适当的值一样工作。因此,如果我说1 + 2,则编译器会将其视为3

该规范还描述了statement reachability的概念。本质上,该规范包括有关编译器如何确定是否可能达到语句的规则。编译器可以使用常量表达式来确定是否无法通过代码到达某些路径。

您的案件涉及if声明:

if(1==2){
    /* disabled code */
}

The rules for the if statement状态:

  

如果if语句可访问并且布尔表达式不具有常量值if,则false语句的第一个嵌入式语句是可访问的。

(短语“第一个嵌入式语句”是指if之后的代码或代码块,在您的情况下是{ /* disabled code */ }部分。如果您有else,则会有一个“第二个嵌入式语句”。)

换句话说,如果if的条件是编译器可以保证的if,则false语句的内部是不可访问的(“死代码”)。如果编译器可以保证它是true,或者如果编译器不能保证一种或另一种方法,那么代码是可以访问的(当然,假设您将if语句放在任何地方都是其自身)可达)。


现在,除了要求编译器发出警告并禁止编译器发出错误之外,我不相信规范会明确说明编译器在遇到无法访问的代码时应采取的措施。换句话说,编译器可以选择为无法到达的代码生成IL(存储在.NET .dll.exe s中的代码),即使它知道正常执行中不会使用这些代码。

我认为Visual Studio附带的官方C#编译器可能会选择在调试构建期间为无法访问的代码生成IL,因此您可以使用调试器显式地跳入该块。情况似乎并非如此;以下C#:

static void CallMe()
{
    if (1 == 2)
    {
        Console.WriteLine("LOL");
    }
}

关闭“优化代码”时,编译为以下IL:

.method private hidebysig static void  CallMe() cil managed
{
  // Code size       6 (0x6)
  .maxstack  1
  .locals init ([0] bool V_0)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0005
  IL_0005:  ret
} // end of method Program::CallMe

此IL将值0(对于false)存储到临时布尔变量中-因此,至少IL在某种意义上正在“评估”常量值1 == 2-然后有一条分支指令使代码跳转到下一行。因此,看起来C#编译器的行为仍然像有一个if块,只是(1)该块为空,(2)代码无条件地跳过了该块。

相关问题