【问题标题】:Unassigned local variable and short-circuit evaluation未分配的局部变量和短路评估
【发布时间】:2017-01-19 13:45:46
【问题描述】:

我有两种方法,它们都编译正确:

public int A()
{
    int i;
    if(!int.TryParse("0", out i))
    {
        return -1;
    }
    // do sth
    return i;
}

public int B()
{
    int i;
    if(true)
    {
        return -1;
    }
    return i;
}

在第二种情况下(方法 B)编译器足够聪明,可以检测到变量 i 从未使用过,因此它不会抱怨未分配它。

但是,我有另一个示例(两者的组合)似乎等同于方法B

public int C()
{
    int i;
    if (true || !int.TryParse("0", out i))
    {
        return -1;
    }
    return i;
}

VisualStudio 2012 (.NET Framework 4.6.01055) 下在 Windows 上编译时会引发错误:Use of unassigned local variable 'i'。解决办法是:

  • 用任意值初始化i,或者
  • 使用| 运算符而不是||

为什么会这样?看起来编译器拥有所有必要的数据来检测无法访问的代码。

旁注:示例 Cmono 4.6.2 下的 Linux 上编译,并按预期发出有关无法访问代码的警告。

【问题讨论】:

  • 您是否熟悉停机问题 (en.wikipedia.org/wiki/Halting_problem)?这就是为什么不能总是期望编译器知道您的代码是否无法访问的主要原因......
  • 有一个类似这样的老错误,但它涉及dynamic
  • 在方法 B 的第一个示例中 - 如果您在 "if (true)" 语句的右大括号之后编写了无效代码,那么尽管没有可访问 - 因此报告使用未分配的局部变量似乎是一种更一致的方法。
  • @Idos 根本不回答这个问题。在这种情况下,编译器可以很清楚地知道i 没有被使用,事实上,这正是它在以后的版本中所做的。
  • @InBetween 重点是总会有编译器无法证明的情况。它会捕捉到一些明显的情况,但你不能指望它总能解决问题,即使在你可以证明给定代码不可达的情况下也是如此。

标签: c#


【解决方案1】:

这不能被认为是一个错误,但它是一个可改进的功能。当您说编译器有足够的信息知道从未使用过未分配的i 并因此它应该省略编译器错误时,您是正确的。

它是可以改进的,因为事实上,它已经被改进了;在 VS 2015 中,编译器的行为是预期的:没有编译时错误。我不能对以前版本的编译器说同样的话,因为我目前无法测试它们。

有趣的是,VS 2015 或 VS 2017 RC 都没有在return i 报告无法访问的代码警告,这似乎有点奇怪。 if (true) 将给出此警告,if (true || ....) 正确指出未使用 i,但由于我不明白的原因,该警告已被省略。

要详细了解行为改变的原因,请查看此answer。我知道这个问题很重要……几年前我自己也问过类似的问题;)。

【讨论】:

    【解决方案2】:

    在 Windows 上在 Visual Studio 2012 (.NET Framework 4.6.01055) 下编译时会抛出错误:Use of unassigned local variable 'i'

    应该如此。变量未赋值。您可能不会在代码中引用它。但有时,取决于编译器,它可能事先知道该行永远不会执行,并在检查变量使用之前将其完全删除。其他版本可能会切换可能导致不同结果的步骤。就个人而言,我会说你不应该依赖它:代码安全。

    仅供参考。 Roslyn(最新的 .NET 编译器)在编译时不会出错。看来验证只是丢弃了所有|| 条件。

    if (true || !int.TryParse("0", out i)) // // does not give an error, first return always used, other discarded
    
    if (true && !int.TryParse("0", out i)) // does not give an error, i is known to always be assigned
    
    if (false && !int.TryParse("0", out i)) // gives an error, if never results in true, and parse is known not to be called
    

    【讨论】:

    • 应该如此。不,它不应该是这样。你自己的回答否定了这种推理;较新的编译器版本不会引发编译时错误。
    • 他们已经优化了编译器以正确处理这一点并不重要,你应该让这样的代码徘徊。如果有人将true 更改为false,您就会打开一整套问题。我认为你应该尽快修复它们。
    • @InBetween:因为那部分代码无法访问 - 是否应该忽略所有无效代码?
    • @PaulF 这不是我认为编译器应该或不应该做的事情。事实上,编译器团队决定改变编译器的行为,在我看来,他们认为不引发错误是明智之举。
    • @PaulF 这也是一个明智的决定。我知道这个话题敲响了……几年前我问了一个类似的问题,这是 Eric Lippert 的answer
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-01
    • 2012-12-16
    • 1970-01-01
    • 2015-11-14
    • 2017-01-21
    相关资源
    最近更新 更多