编译时间错误和无法访问的代码

时间:2012-02-23 16:05:28

标签: c#

好的,请考虑以下代码:

private const int THRESHHOLD = 2;

static void Main(string[] args)
{
     string hello;

     if (THRESHHOLD > 1) return;
     Console.WriteLine(hello);        
}

令人惊讶的是,这段代码没有抛出“使用未分配的局部变量'hello'”编译时错误。它只是发出警告“检测到无法访问的代码”。

即使代码无法访问,它仍然是编译时错误,我认为正确的做法是抛出编译时错误。如果我要做以下事情:

private const int THRESHHOLD = 2;

static void Main(string[] args)
{
     string hello;

     if (THRESHHOLD > 1) return;
     hello.LMFAO();       
}

果然,我得到一个“'字符串'不包含'LMFAO'的定义,并且没有扩展方法'LMFAO'接受类型'string'的第一个参数可以找到(你是否错过了使用指令或程序集引用?)“编译时错误。

为什么使用未分配的变量不一样?

编辑更改了const变量,因此不会分散注意力。我想许多人都忽略了问题的重点,这取决于哪种情况,编译时错误优先于无法访问的代码。

3 个答案:

答案 0 :(得分:12)

如果您查看第5部分中的C# language specification,则说明:

  

对于最初未分配的变量,要明确考虑   在某个位置分配,变量的赋值必须   发生在通往该位置的每条可能的执行路径中。

进一步在5.3.3.1中:

  

v肯定是在任何无法访问的语句的开头分配的。

由于不可到达的代码不是可能的执行路径,因此不必分配它来避免错误。

至于你的问题为什么未知函数是无法访问的代码中的编译器错误而未分配的变量不是。你必须考虑上面的标准。无法访问的代码并不意味着它无法在语法上有效。代码仍然必须是可编译的,唯一的区别是无法访问的代码使得它考虑在该点分配的所有最初未分配的变量。但这并不意味着您可以像未定义的变量或方法那样注入语法无效的东西。

未分配变量的错误消息也给了我们一个暗示,它告诉我们在使用之前必须分配一个最初未分配的变量,但由于代码无法访问,所以技术上并没有使用...

答案 1 :(得分:12)

James Michael Hare的回答给出了 de jure 解释:本地变量 是明确分配的,因为代码无法访问,并且所有局部变量都是在无法访问的代码中明确分配的。换句话说:如果有办法观察未初始化的局部变量的状态,程序只是一个错误。在你的程序中,没有办法观察本地,因此这不是一个错误。

现在,我注意到编译器不需要非常聪明。例如:

void M()
{
    int x = 0;
    int y;
    if (x + 0 == x) return;
    Console.WriteLine(y);
}

你知道并且我知道该方法的最后一行是无法访问的,但编译器不知道这一点,因为可达性分析器不知道零是整数的加性同一性。编译器认为最后一行可能是可达的,因此会出错。

有关在编程语言中设计可达性和明确赋值分析器方面的更多信息,请参阅我关于该主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/tags/reachability/

http://blogs.msdn.com/b/ericlippert/archive/tags/definite+assignment/

我注意到没有人回答了更深层次的问题,为什么应该在无法访问的代码中抑制错误?如您所知,我们在无法访问的代码中提供其他语义分析错误。

要考虑该决定的利弊,您必须考虑为什么有人会首先获得无法访问的代码。无论是故意无法访问,还是无意无法访问。

如果无意中无法访问,则程序包含错误。该警告已经引起了对主要问题的注意:代码无法访问。如果存在无法访问的代码,则该方法的控制流存在严重错误。很可能开发人员必须对方法的控制流程进行严格的改变;我们对无法访问的代码进行的任何局部变量分析都可能会产生误导性的噪音。让开发人员修复代码,以便一切都可以访问,然后然后我们将分析现在可访问的与控制流相关的错误代码。

如果无法访问无法访问的代码,因为开发人员无法访问,则可能无法访问这些代码:

// If we can Blah, then Frob. However, if we cannot Blah and we can Baz, then Foo.
void M()
{
    int y;
    // TODO: The Blah method has a bug and always throws right now; fix it later.
    if (false /* Blah(out y) */ )
    {
        Frob(y);
    }
    else if (Baz(out y))
    {
        Foo(y);
    }
}

此程序中Frob(y)应该是错误吗?

答案 2 :(得分:1)

当编译器看到代码无法访问时,它不会为它生成代码 - 因此,没有问题。

如果你取出return行,那么最后一行再次可以访问,编译器会为它生成代码,并会告诉你它有问题。