为什么这个代码不是不可访问的呢?

时间:2021-12-24 03:13:05

I found a case where I have some code that I believe to be unreachable and is not detected. No warning is issued neither by the compiler nor by Visual Studio.

我发现了一种情况,我有一些我认为不可访问的代码,并且没有被检测到。编译器和Visual Studio都不会发出任何警告。

Consider this code:

考虑这段代码:

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

Obviously, the program will not print out "lol" because the condition in the if statement is false. I do not understand why a warning is not issued for the unreachable code though. My only hypothesis is that that could potentially be reachable if you have a race condition in a multi-threaded program. Is this correct?

显然,程序不会输出“lol”,因为if语句中的条件为false。我不明白为什么没有发出警告,因为无法到达的代码。我唯一的假设是,如果在多线程程序中有竞争条件,那么它可能是可达的。这是正确的吗?

4 个解决方案

#1


134  

Static analysis can only do so much, and it will only mark code as unreachable if it can prove that a value cannot be changed. In your code, what happens inside Bar is out of the scope of the method flow and can't be statically reasoned about. What if Bar's constructor launches a thread that sets the value of type back to B? The compiler can't know about it, because, again, the internals of Bar aren't scoped to the method.

静态分析只能做这么多,而且只有当它能够证明一个值不能被修改时,它才会将代码标记为不可访问的。在您的代码中,Bar内发生的事情超出了方法流的范围,并且不能进行静态推理。如果Bar的构造函数启动一个线程,将类型的值设置为B,该怎么办?编译器不能知道它,因为Bar的内部属性没有作用域到方法。

If your code was checking the value of a local variable, then the compiler could know if there was no way for it to change. But that's not the case here.

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

#2


27  

The C# specification says,

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.

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

and, concerning 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.

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

Only the following constructs are permitted in constant expressions:

只有以下结构在常量表达式中被允许:

  • Literals (including the null literal).
  • 文字(包括空文字)。
  • References to const members of class and struct types.
  • 对类和结构类型的const成员的引用。
  • References to members of enumeration types.
  • 对枚举类型成员的引用。
  • References to const parameters or local variables
  • 引用const参数或局部变量
  • 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.
  • :条件操作符。

Member access expressions are not in this list, so the boolean expression is not constant. Thus the body of the if block is reachable.

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

#3


9  

Because no such guarantee can be made at compile time. Consider this alternative Bar class

因为在编译时没有这样的保证。考虑这个可选的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));
        }
    }
}

Please note that "reachable" is defined at function level. It is not allowed to reach outside the function that is being tested even when it is safe to do so.

请注意,“可到达”是在函数级定义的。即使在安全的情况下,也不允许超出正在测试的功能。

#4


2  

The warning you expected is not implemented because it's not a useful warning to have.

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

In real-world applications, the compiler is very often faced with code that it can totally prove is unreachable, maybe even something as bald-faced as

在真实的应用程序中,编译器经常会遇到一些代码,这些代码完全可以证明是不可访问的,甚至可能是一些像这样的无遮无拦的代码

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

I don't have a C# compiler on this computer but I bet you there's no warning for that, either. This is because, when you put if (false) { ... } around a block of code, you did that on purpose, perhaps to disable something temporarily for an experiment. Nagging you about it would not be helpful.

我在这台电脑上没有c#编译器,但我打赌你也没有警告。这是因为,当你输入if (false){…围绕一段代码,您是故意这样做的,可能是为了在实验中暂时禁用某些东西。对你唠叨不会有帮助。

More common is that it's not a literal false, it's a compile-time constant that the build system will set to true or false depending on configuration; you want the compiler to delete the unreachable code in one build but not the other, and you don't want complaints either way.

更常见的是,它不是一个字面上的false,而是编译时常量,构建系统根据配置将其设置为true或false;您希望编译器在一个构建中删除不可访问的代码,而不是在另一个构建中删除,而且您也不希望出现任何抱怨。

Even more common than that is for early optimizations like inlining and constant propagation to discover that a conditional is always false; suppose you have something like

更常见的是进行早期优化,如内联和持续传播,以发现条件句总是错误的;假设你有

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

You clearly wouldn't want to get told that one side of the conditional inside Fizz() was unreachable just because it was only ever called with argument 4 in this program.

很明显,您不希望被告知Fizz()中的条件语句的一端是不可访问的,因为它在这个程序中只使用参数4调用。

#1


134  

Static analysis can only do so much, and it will only mark code as unreachable if it can prove that a value cannot be changed. In your code, what happens inside Bar is out of the scope of the method flow and can't be statically reasoned about. What if Bar's constructor launches a thread that sets the value of type back to B? The compiler can't know about it, because, again, the internals of Bar aren't scoped to the method.

静态分析只能做这么多,而且只有当它能够证明一个值不能被修改时,它才会将代码标记为不可访问的。在您的代码中,Bar内发生的事情超出了方法流的范围,并且不能进行静态推理。如果Bar的构造函数启动一个线程,将类型的值设置为B,该怎么办?编译器不能知道它,因为Bar的内部属性没有作用域到方法。

If your code was checking the value of a local variable, then the compiler could know if there was no way for it to change. But that's not the case here.

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

#2


27  

The C# specification says,

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.

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

and, concerning 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.

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

Only the following constructs are permitted in constant expressions:

只有以下结构在常量表达式中被允许:

  • Literals (including the null literal).
  • 文字(包括空文字)。
  • References to const members of class and struct types.
  • 对类和结构类型的const成员的引用。
  • References to members of enumeration types.
  • 对枚举类型成员的引用。
  • References to const parameters or local variables
  • 引用const参数或局部变量
  • 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.
  • :条件操作符。

Member access expressions are not in this list, so the boolean expression is not constant. Thus the body of the if block is reachable.

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

#3


9  

Because no such guarantee can be made at compile time. Consider this alternative Bar class

因为在编译时没有这样的保证。考虑这个可选的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));
        }
    }
}

Please note that "reachable" is defined at function level. It is not allowed to reach outside the function that is being tested even when it is safe to do so.

请注意,“可到达”是在函数级定义的。即使在安全的情况下,也不允许超出正在测试的功能。

#4


2  

The warning you expected is not implemented because it's not a useful warning to have.

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

In real-world applications, the compiler is very often faced with code that it can totally prove is unreachable, maybe even something as bald-faced as

在真实的应用程序中,编译器经常会遇到一些代码,这些代码完全可以证明是不可访问的,甚至可能是一些像这样的无遮无拦的代码

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

I don't have a C# compiler on this computer but I bet you there's no warning for that, either. This is because, when you put if (false) { ... } around a block of code, you did that on purpose, perhaps to disable something temporarily for an experiment. Nagging you about it would not be helpful.

我在这台电脑上没有c#编译器,但我打赌你也没有警告。这是因为,当你输入if (false){…围绕一段代码,您是故意这样做的,可能是为了在实验中暂时禁用某些东西。对你唠叨不会有帮助。

More common is that it's not a literal false, it's a compile-time constant that the build system will set to true or false depending on configuration; you want the compiler to delete the unreachable code in one build but not the other, and you don't want complaints either way.

更常见的是,它不是一个字面上的false,而是编译时常量,构建系统根据配置将其设置为true或false;您希望编译器在一个构建中删除不可访问的代码,而不是在另一个构建中删除,而且您也不希望出现任何抱怨。

Even more common than that is for early optimizations like inlining and constant propagation to discover that a conditional is always false; suppose you have something like

更常见的是进行早期优化,如内联和持续传播,以发现条件句总是错误的;假设你有

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

You clearly wouldn't want to get told that one side of the conditional inside Fizz() was unreachable just because it was only ever called with argument 4 in this program.

很明显,您不希望被告知Fizz()中的条件语句的一端是不可访问的,因为它在这个程序中只使用参数4调用。