【问题标题】:Compile time errors and unreachable code编译时错误和无法访问的代码
【发布时间】:2012-02-24 02:08:23
【问题描述】:

好的,考虑下面的代码:

private const int THRESHHOLD = 2;

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

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

令人惊讶的是,这段代码不会引发“Use of unassigned local variable 'hello'”编译时错误。它只是给出一个警告“检测到无法访问的代码”。

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

private const int THRESHHOLD = 2;

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

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

果然,我得到一个“'string'不包含'LMFAO'的定义,并且找不到接受'string'类型的第一个参数的扩展方法'LMFAO'(您是否缺少使用指令还是程序集引用?)”编译时错误。

为什么使用未赋值的变量不一样?

EDIT 更改了 const 变量,使其不那么分散注意力。我认为很多人都忽略了问题的重点,即根据哪种情况,编译时错误优先于无法访问的代码。

【问题讨论】:

  • 只是想知道:您是否希望TRUE 评估为除true 之外的任何内容?
  • 未使用的变量不会阻止您的代码工作或编译,这就是为什么它只是一条警告消息。尝试使用未定义的方法会阻止您的代码运行,因此会出现错误。我不确定我是否理解这个问题。
  • @BrianRasmussen 也许他来自一个“真”关键字全部大写的语言?
  • 其他相关的有趣问题:stackoverflow.com/questions/636932/…
  • 因为一个未知的方法(或变量)在语法上是无效的,在这种情况下,无法访问的代码唯一改变的是它认为所有未分配的变量都已分配,因为它们必须在它们被分配之前被分配 used,不能用在无法访问的代码中。

标签: c#


【解决方案1】:

James Michael Hare 的回答给出了法理的解释:局部变量肯定分配的,因为代码是不可访问的,并且所有局部变量肯定是在不可访问的代码中分配的。换句话说:如果有办法观察未初始化的局部变量的状态,程序只会出错。在你的程序中没有办法观察到局部,因此它不是错误。

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

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) 应该是这个程序中的一个错误吗?

【讨论】:

  • 一如既往的好东西。感谢 Eric 的回答,你的例子说明了为什么实现的行为是可取的,这一切都变得更加清晰。
【解决方案2】:

如果您查看第 5 节中的 C# language specification,它会指出:

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

在 5.3.3.1 中进一步:

v 绝对分配在任何无法访问的语句的开头。

由于不可访问的代码不是可能的执行路径,因此没有必要对其进行分配以避免错误。

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

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

【讨论】:

  • 是的,行为符合规范。我更感兴趣的是为什么可达性优先于明确的分配。 Eric 的示例显示了您希望如此的情况。感谢您的回答。
  • @InBetween:别担心,他是大师 :-)
【解决方案3】:

当编译器发现代码不可访问时,它不会为它生成代码——因此,没有问题。

如果你把return那行去掉,那么最后一行又可以访问了,编译器会为它生成代码,并告诉你它有问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-18
    • 2013-04-27
    • 1970-01-01
    相关资源
    最近更新 更多