为什么这段代码无法访问?

时间:2018-04-13 08:07:47

标签: c# .net unreachable-code

我发现了一个案例,我有一些我认为无法访问且未被检测到的代码。 编译器和Visual Studio都不会发出警告。

考虑以下代码:

enum Foo { A, B, C }
class Bar { public Foo type; }

static class Program
{
    private static void Main()
    {
        var bar = new Bar { type = Foo.A };

        if (bar.type == Foo.B)
        {
            Console.WriteLine("lol");
        }
    }
}

显然,该程序不会打印出来" lol"因为if语句中的条件为false。 我不明白为什么没有为无法访问的代码发出警告。 我唯一的假设是,如果你在多线程程序中遇到竞争条件,那么这可能是可以达到的。这是对的吗?

4 个答案:

答案 0 :(得分:134)

静态分析只能做很多事情,如果能证明代码无法改变,它只会将代码标记为无法访问。在您的代码中,Bar中发生的事情超出了方法流程的范围,无法进行静态推理。如果Bar的构造函数启动一个将type的值设置回B的线程,该怎么办?编译器无法知道它,因为同样,Bar的内部结构不是该方法的范围。

如果您的代码正在检查 local 变量的值,那么编译器可以知道它是否无法更改。但事实并非如此。

答案 1 :(得分:27)

C# specification说,

  

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

,关于constant expressions

  

常量表达式必须是null文字或具有以下类型之一的值:sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,object,string或任何枚举类型。

     

在常量表达式中只允许使用以下结构:

     
      
  • 文字(包括null文字)。
  •   
  • 引用类和结构类型的const成员。
  •   
  • 引用枚举类型的成员。
  •   
  • 对const参数或局部变量的引用
  •   
  • 带括号的子表达式,它们本身就是常量表达式。
  •   
  • 投射表达式,前提是目标类型是上面列出的类型之一。   选中和未选中的表达式
  •   
  • 默认值表达式
  •   
  • 预定义的+!~一元运营商。
  •   
  • 预定义的+*/%<<>>,{{1 }},|,&^&&||==!=<,{{1 }}和>二元运算符,前提是每个操作数都是上面列出的类型。
  •   
  • <=条件运算符。
  •   

成员访问表达式不在此列表中,因此布尔表达式不是常量。因此if块的主体是可达的。

答案 2 :(得分:9)

因为在编译时不能做出这样的保证。考虑这个替代Bar类

class Bar
{
   Random random = new Random();
   Array Foos = Enum.GetValues(typeof(Foo));

    private Foo _type;
    public Foo type
    {
        get { return _type; }
        set
        {
            _type = (Foo)Foos.GetValue(random.Next(3));
        }
    }
}

请注意&#34;可以到达&#34;在功能级别定义。即使在安全的情况下也不允许它到达正在测试的功能之外。

答案 3 :(得分:2)

您预期的警告未实施,因为它不是一个有用的警告。

在实际应用程序中,编译器常常面对代码,它可以完全证明是无法访问的,甚至可能是像光头一样的

static class Program
{
    private static void Main()
    {
        if (false)
        {
            Console.WriteLine("lol");
        }
    }
}

我这台计算机上没有C#编译器,但我打赌你也没有这个警告。这是因为,当您将if (false) { ... }放在代码块周围时,您是故意这样做的,也许是为了实验暂时禁用某些内容。唠叨它不会有帮助。

更常见的是它不是文字false,它是编译时常量,构建系统将根据配置设置为true或false;你希望编译器删除一个构建中的无法访问的代码而不是另一个构建中的无法访问的代码,并且你不希望出现任何方式的投诉。

比这更常见的是早期优化,例如内联和不断传播发现条件总是假的;假设你有像

这样的东西
static class Program
{
    private static void Fizz(int i)
    {
        if (i % 3 == 0) {
            Console.WriteLine("fizz");
        } else {
            Console.WriteLine(i);
        }
    }

    private static void Main()
    {
        Fizz(4);
    }
}

你显然不希望被告知Fizz()中条件内的一方是无法访问的,因为它只是在此程序中使用参数4 调用