关于c#:为什么这段代码不可达?

Why isn't this code unreachable?

我发现了一个案例,我有一些代码我认为是不可访问的,并且没有被检测到。编译器和Visual Studio均未发出警告。

考虑此代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。不过,我不明白为什么不对无法访问的代码发出警告。我唯一的假设是,如果您在一个多线程程序中有一个争用条件,那么它可能是可以到达的。这是正确的吗?


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

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


C规格说明,

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.

关于常数表达式,

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块的主体是可到达的。


因为在编译时不能做出这样的保证。考虑这个可选的吧类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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));
        }
    }
}

请注意,"可访问"是在功能级别定义的。即使这样做是安全的,也不允许接触到正在测试的功能之外。


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

在现实的应用程序中,编译器经常面临着完全证明是不可访问的代码,甚至可能是像

1
2
3
4
5
6
7
8
9
10
static class Program
{
    private static void Main()
    {
        if (false)
        {
            Console.WriteLine("lol");
        }
    }
}

我在这台电脑上没有C编译器,但我敢打赌也没有警告。这是因为,当您将if (false) { ... }放在一个代码块周围时,您是故意这样做的,可能是为了暂时禁用某个实验。对你唠叨这件事没有帮助。

更常见的是,它不是字面上的false,它是编译时常量,生成系统将根据配置设置为true或false;您希望编译器删除一个生成中不可访问的代码,但不删除另一个生成中不可访问的代码,并且您也不希望投诉。

比这更常见的是,早期的优化(如内联和持续传播)发现条件总是错误的;假设您有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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调用过。