为什么C#编译器不能告诉该函数总是返回或抛出?

JSB*_*ոգչ 1 c# code-analysis for-loop exception

我有以下(简化)方法:

public bool DoWorkWithRetry()
{
    for (int remainingTries = Constants.MaxRetries; remainingTries >= 0; remainingTries--)
    {
        try
        {
            return DoWork();
        }
        catch (Exception ex)
        {
            if (remainingTries == 0)
            {
                throw new WorkException(
                        String.Format("Failed after {0} retries.", Constants.MaxRetries),
                        ex);
            }
            // fall through to retry
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我觉得这个方法要么退还要么抛出.但是,C#编译器向我抱怨not all code paths return a value.

  • 这是C#编译器代码分析的限制吗?
  • 或者是否有一些条件,我没有看到for循环可以完成没有投掷或返回?

Jon*_*eet 12

编译器只是遵循语言规范.

该语言并没有尝试执行的"如果你开始一个整数,并多次减一,你总会最终击中在一些点零"的分析.这甚至假设它Constants.MaxRetries是一个编译时常量.

基本上,语言规范具有可达性规则- for如果其中任何一个为真,则可以访问语句的结束点(C#4规范的第8.8.3节):

  • for语句包含break退出for语句的可访问语句
  • for语句是可达的,并且存在for-condition并且没有常量值true

后一点是这里的情况,因此for语句的结尾是可达的.不允许非void方法的结束,因此错误.(C#规范第8.1节)

另一种方法是使语言变得更加复杂.我完全可以不用编译.


Ser*_*rvy 5

编译器不知道for循环将始终至少执行一次.它认为它是一个永远不会运行的有效选项,在这种情况下它不会throwreturn.

虽然通过对程序进行足够复杂的分析,理论上可以证明它总是返回或抛出,但这种分析非常复杂且性能密集.C#编译器团队不想冒错误的风险,或者考虑大幅增加编译时间以添加这种复杂的分析.他们选择使用更简单的"可达"和"无法访问"代码定义,这些代码更容易实现.

至于实际修复,只需throw在方法的最后添加一个,并可能放入一条注释,说明实际上无法点击它.