【问题标题】:Why does the compiler complain that 'not all code paths return a value' when I can clearly see that they do?当我可以清楚地看到它们时,为什么编译器会抱怨“并非所有代码路径都返回值”?
【发布时间】:2017-06-06 05:56:22
【问题描述】:

我试图弄清楚为什么编译器有这个函数的问题。它给了我“并非所有代码路径都返回值”错误,但是我看不到控制流将传递给 if( a ) 表达式而 a 不为真的情况(所以 if( a ) 是多余的,但是编译器似乎无法识别)。

public static Boolean Foo(Boolean x)
{
    Boolean a = false;
    if( x )
    {
        a = true;
    }
    else
    {
        try
        {
            SomethingThatMightThrow();
            Assert.IsFalse( a );
            return a;
        }
        catch(Exception)
        {
            a = true;
        }
    }

    if( a )
    {
        return x;
    }
}

直接的解决方法是完全删除 if( a ) 保护语句并立即删除 return x - 但是为什么编译器会抱怨,即使它应该能够静态证明所有可能的代码路径都会遇到 return 语句?至关重要的是,没有循环,这通常是它无法证明 return-ness 的主要原因。

我正在使用 VS2015 更新 3。

【问题讨论】:

  • 如果变量总是设置为真,为什么首先要使用变量?删除条件,如果您还没有从方法返回,则始终打印使用情况。
  • 我想你会觉得这篇文章很有趣blog.coverity.com/2013/11/06/c-reachability/#.WIMgWIWcGdI
  • 如果您知道a 始终是true 为什么不完全删除if
  • C# 规范 8.1 端点和可达性: 为了确定特定语句或端点是否可达,编译器根据定义的可达性规则执行流分析每个语句。流分析考虑了控制语句行为的常量表达式(第 7.19 节)的值,但不考虑非常量表达式的可能值。换句话说,出于控制流分析的目的,给定类型的非常量表达式被认为具有该类型的任何可能值
  • 只是对 Style 的评论。您正在使您的代码更复杂,以便放置一个单点退出。这可以说是不鼓励的。见softwareengineering.stackexchange.com/questions/104551/…softwareengineering.stackexchange.com/questions/118703/…

标签: c# compiler-errors static-analysis


【解决方案1】:

当您到达函数的末尾时,支持的场景是 afalse。这种情况是当您调试代码并使用调试器设置 afalse

C# 编译器规则设计简单。在 C# 借用的语言中,函数可能不返回任何内容的问题是编译器警告无法涵盖的问题。误报太多了,因此对警告进行了调整,只对明显的情况发出警告,从而引入了误报。 C# 的规则是一种折衷方案,如果熟悉规则的人可以理解误报,则可以接受误报,而不能接受误报。你可以保证,如果你的函数有一个不返回值的代码路径,编译器会检测到它。

这些简单规则的一部分是不考虑变量的值。即使a 被静态保证为true,编译器在设计上也无法利用这一事实。

@PetSerAl 已经引用了 C# 语言规范中的相关措辞:

8.1 端点和可达性

[...]

为了确定特定语句或端点是否可达,编译器根据为每个语句定义的可达性规则执行流分析。流分析考虑了控制语句行为的常量表达式(第 7.19 节)的值,但不考虑非常量表达式的可能值。换句话说,出于控制流分析的目的,给定类型的非常量表达式被认为具有该类型的任何可能值。

这是当前最新发布的语言规范的一部分,该规范适用于 C# 5.0。您使用的版本 C# 6.0(这是 VS2015 提供的版本)尚未发布规范,因此措辞可能会略有不同,但正如您的编译器所示,实际上相同的规则仍然适用。

【讨论】:

  • 这是一个很好的答案,但由于这个问题变得如此流行,您可能会考虑通过参考 C# 语言规范的相关部分来改进它。 PetSerAl already cited it in a comment.
  • @CodyGray OP 使用的是 VS2015,它实现了 C# 6,据我所知还没有语言规范。
  • @hvd VS2015 使用您为项目选择的任何 C# 版本,问题中似乎没有特定的 C#6。
  • @BartoszKP 不,迄今为止,每个 C# 编译器仅实现该语言的一个版本。他们可以选择为新的语言功能发布错误,但他们没有选择实现早期版本的规范。 (最著名的例子可能是 C# 5 中的 foreach 变量更改:stackoverflow.com/questions/12112881/…)虽然问题中可能没有任何特定于 C# 6 的内容,但我怀疑规范的这一特定部分需要针对 C# 6 进行更改async 方法,虽然我不能 100% 确定。
  • 哦,等等 - async 是 C# 5 的一个特性,只是在 C# 6 中进行了一些改进。C# 6 中的其他任何内容都不需要更改规范的那部分......
【解决方案2】:

运行时与编译时

你的例子太复杂了。这也不会编译:

static int Test()
{
    bool f = true;
    if (f)
    {
        return 1;
    }
    else
    {
        //Not all code paths return a value
    }
}

另一方面,这将:

static int Test()
{
    if (true)
    {
        return 1;
    }
    else
    {
        //No error
    }
}

我猜想无论采用何种验证机制,都没有足够的逻辑来推断运行时变量的内容。编译时变量没问题。

【讨论】:

    【解决方案3】:

    我认为编译器对代码进行了非常简单的分析,因此必须明确给出返回值。

    这看起来像是一个错误的决定,但在处理复杂代码时,返回值可能不清楚。所以,程序员只好退货了。

    您的示例可以像这样减少到最低限度:

    public static Int32 Main(String[] args)
    {
        var printUsage = true;
        if (printUsage)
        {
            return 0;
        }
    
        // return nothing, so compiler is not happy
    }
    

    虽然仍然出现错误。

    注意:如果您使用 Resharper,它将执行您想要的分析并相应地警告您:

    if (printUsage)         // Warning: expression is always true 
    

    【讨论】:

    • 有趣 - 尽管根据@steve 链接到的文章(由 Eric Lippert 撰写,不少于!)它说编译器使用编译时常量计算可达性 - 所以根据那篇文章编译器不应该报告您的 Main 方法的任何问题(blog.coverity.com/2013/11/06/c-reachability/#.WIMgWIWcGdI
    • @Dai printUsage 不是编译时常量。
    • 而且,即使printUsage 被声明为static readonly 类成员,它仍然会抱怨。
    猜你喜欢
    • 1970-01-01
    • 2019-04-25
    • 2012-04-24
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    • 2011-12-17
    相关资源
    最近更新 更多