c# – 错误原因CS0161:并不是所有的代码路径都返回一个值

我已经做了一个基本的扩展方法来添加重试功能到我的HttpClient.PostAsync:

public static async Task<HttpResponseMessage> PostWithRetryAsync(this HttpClient httpClient, Uri uri, HttpContent content, int maxAttempts, Action<int> logRetry)
{
    if (maxAttempts < 1)
        throw new ArgumentOutOfRangeException(nameof(maxAttempts), "Max number of attempts cannot be less than 1.");

    var attempt = 1;
    while (attempt <= maxAttempts)
    {
        if (attempt > 1)
            logRetry(attempt);

        try
        {
            var response = await httpClient.PostAsync(uri, content).ConfigureAwait(false);
            response.EnsureSuccessStatusCode();
            return response;
        }
        catch (HttpRequestException)
        {
            ++attempt;
            if (attempt > maxAttempts)
                throw;
        }
    }
}

上面的代码给我以下错误:

Error CS0161 ‘HttpClientExtensions.PostWithRetryAsync(HttpClient, Uri, HttpContent, int, Action)’: not all code paths return a value.

如果我在结尾处添加新的InvalidOperationException()(或者返回null),那么该错误将如预期那样消失。我真正想知道的是:有没有任何代码路径实际退出此方法,而不返回值或抛出异常?我看不到它在这种情况下,我比编译器知道的多,还是相反呢?

简单的原因是编译器必须能够静态地验证所有的执行流程路径是否都有一个返回语句(或异常)。

我们来看看你的代码,它包含:

>一些控制一个while循环的变量
>一个while循环,其中包含return语句
>循环后无返回语句

所以基本上编译器必须验证这些东西:

> while循环实际上是执行的
> return语句总是被执行
>或者总是抛出一些异常。

编译器根本无法验证这一点。

让我们尝试一个非常简单的例子:

public int Test()
{
    int a = 1;
    while (a > 0)
        return 10;
}

这个简单的例子会产生完全相同的错误:

CS0161 ‘Test()’: not all code paths return a value

所以编译器根本不能推论出这些事实:

> a是一个局部变量(意味着只有本地代码可以影响它)
> a的初始值为1,永远不会更改
>如果一个变量大于零(它是),则返回返回语句

那么代码将始终返回值10。

现在看这个例子:

public int Test()
{
    const int a = 1;
    while (a > 0)
        return 10;
}

唯一的区别是我做了一个常数。现在它编译,但这是因为优化器现在能够删除整个循环,最后的IL就是这样的:

Test:
IL_0000:  ldc.i4.s    0A 
IL_0002:  ret     

整个while循环和局部变量都消失了,剩下的只是这样:

return 10;

很明显,当编译器静态分析这些东西时,不会看到变量值。执行此功能并获得正确的成本可能超过了不执行此功能的效果或缺点。记住“Every feature starts out in the hole by 100 points, which means that it has to have a significant net positive effect on the overall package for it to make it into the language.”

所以是的,这绝对是一个比编译器更了解代码的情况。

为了完整,我们来看看代码可以流通的所有方式:

>如果maxAttempts小于1,它可以提前退出异常
>它将进入while循环,因为尝试为1,maxAttempts至少为1。
>如果try语句中的代码引发了一个HttpRequestException,那么尝试增加,如果仍然小于或等于maxAttempts,while循环将执行另一次迭代。如果现在大于maxAttempts,则异常将会起泡。
>如果抛出一些其他的异常,它将不会得到处理,并且会冒出方法
>如果没有抛出异常,则返回响应。

所以基本上,这个代码可以说总是会抛出一个异常或返回,但编译器不能静态地验证这一点。

由于您已经在两个地方嵌入了转义填充(尝试> maxAttempts),它们都作为while循环的标准,另外在catch块内也可以通过从while循环中删除来简化代码:

while (true)
{
    ...
        if (attempt > maxAttempts)
            throw;
    ...
}

因为你保证至少运行一次while循环,并且它实际上将是它的一个catch块,只是正式化,编译器将再次开心。

现在流程控制如下所示:

> while循环将始终执行(或者我们已经抛出异常)
> while循环永远不会终止(内部没有中断,所以在循环后不需要任何代码)
>退出循环的唯一可能的方法是显式返回或异常,编译器也不必再验证任何一个,因为这个特定错误消息的重点是标记有可能的方法来转义方法而没有明确回报。由于没有办法意外逃避该方法,所以其余的检查可以简单地跳过。

即使这种方法会编译:

public int Test()
{
    while (true)
    {
    }
}
http://stackoverflow.com/questions/33606833/cause-of-errorcs0161-not-all-code-paths-return-a-value

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – 错误原因CS0161:并不是所有的代码路径都返回一个值