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

Why isn't this code unreachable?

我发现我有一些我认为无法访问且未被检测到的代码。 编译器和 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语句中的条件为假。 我不明白为什么没有针对无法访问的代码发出警告。 我唯一的假设是,如果您在多线程程序中存在竞争条件,那么这可能是可行的。这是正确的吗?

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

如果您的代码正在检查 local 变量的值,那么编译器可以知道它是否无法更改。但这里不是这样。

你期望的警告没有实现,因为它不是一个有用的警告。

在实际应用中,编译器经常面对它可以完全证明无法访问的代码,甚至可能像

一样秃头
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 调用

因为在编译时无法做出这样的保证。考虑这个替代 Bar class

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));
        }
    }
}

请注意 "reachable" 是在函数级别定义的。即使在安全的情况下,也不允许触及正在测试的功能之外。

C# specification 说,

The first embedded statement of an if statement is reachable if the if statement is reachable and the boolean expression does not have the constant value false.

并且,关于 constant expressions

A constant expression must be the null literal or a value with one of the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, object, string, or any enumeration type.

Only the following constructs are permitted in constant expressions:

  • Literals (including the null literal).
  • References to const members of class and struct types.
  • References to members of enumeration types.
  • References to const parameters or local variables
  • Parenthesized sub-expressions, which are themselves constant expressions.
  • Cast expressions, provided the target type is one of the types listed above. checked and unchecked expressions
  • Default value expressions
  • The predefined +, , !, and ~ unary operators.
  • The predefined +, , *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and >= binary operators, provided each operand is of a type listed above.
  • The ?: conditional operator.

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