繁体   English   中英

错误 CS0161 的原因:并非所有代码路径都返回值

[英]Cause of Error CS0161: not all code paths return a value

我做了一个基本的扩展方法来向我的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;
        }
    }
}

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

错误 CS0161 'HttpClientExtensions.PostWithRetryAsync(HttpClient, Uri, HttpContent, int, Action)':并非所有代码路径都返回值。

如果我在末尾添加throw new InvalidOperationException() (或return null ),则错误会按预期消失。 我真正想知道的是:是否有任何代码路径实际退出此方法而没有返回值或抛出异常? 我看不到它。 在这种情况下,我是否比编译器了解更多,还是相反?

原因很简单,编译器必须能够静态验证所有执行流路径是否以return语句(或异常)结尾。

让我们看看您的代码,其中包含:

  • 一些控制while循环的变量
  • 一个while循环,嵌入return语句
  • 循环return语句

因此,基本上,编译器必须验证以下内容:

  1. 实际上执行了while循环
  2. 总是执行return语句
  3. 总是抛出一些异常。

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

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

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

这个简单的示例将产生完全相同的错误:

CS0161'Test()':并非所有代码路径都返回一个值

因此,由于以下事实,编译器无法推断出这一点:

  • a是局部变量(意味着只有局部代码可以影响它)
  • a的初始值为1 ,并且永远不变
  • 如果a变量大于零(是),则return语句

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

现在看这个例子:

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

唯一的不同是,我做a一个const 现在可以编译了,但这是因为优化器现在可以删除整个循环,最终的IL就是这样:

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

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

return 10;

因此很明显,编译器在静态分析这些内容时不会查看变量值。 实施并正确实现此功能的成本可能会超过不执行此操作所带来的影响或负面影响。 请记住, “每个功能在漏洞中的起点都是100点,这意味着它必须对整个程序包产生显着的净积极影响,才能使其成为语言。”

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


为了完整起见,让我们看一下代码可以流动的所有方式:

  1. 如果maxAttempts小于1,它可能会提早退出,并发生异常
  2. 它将进入while因为-loop attempt为1, maxAttempts至少为1。
  3. 如果里面的代码try语句抛出一个HttpRequestException然后attempt递增,如果仍然小于或等于maxAttemptswhile -loop会做另一次迭代。 如果现在大于maxAttempts则异常将冒泡。
  4. 如果引发了其他异常,它将不会得到处理,并且会冒出该方法
  5. 如果没有抛出异常,则返回响应。

因此,基本上可以说,此代码总是以抛出异常或返回为最终结果,但是编译器无法静态验证此结果。


由于您已将转义attempt > maxAttempts填充( attempt > maxAttempts )嵌入了两个位置,均作为while -loop的标准,另外还包含在catch块内,因此我只需将其从while -loop中删除即可简化代码:

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

由于可以确保至少运行一次while -loop,并且实际上它是退出该循环的catch块,因此只需对其进行形式化,编译器将再次感到满意。

现在,流控制如下所示:

  • while循环将始终执行(或者我们已经引发了异常)
  • while循环永远不会终止(内部不会break ,因此循环后不需要任何代码)
  • 退出循环的唯一可能方法是显式return或异常,编译器都不必再进行验证,因为此特定错误消息的重点是标记出存在一种可能的方法,而无需明确的return 由于无法避免方法的意外退出,因此可以简单地跳过其余​​检查。

    即使此方法也可以编译:

     public int Test() { while (true) { } } 

如果抛出HttpRequestException并执行catch块,则可能会根据条件(attempt> maxAttempts)跳过throw语句,以使路径不会返回任何内容。

由于错误指出not all code paths return a value您并不是为每个代码路径都返回值

您必须抛出异常或返回值

    catch (HttpRequestException)
    {
        ++attempt;
        if (attempt > maxAttempts)
            throw;
        else
            return null;//you must return something for this code path
    }

您可以修改代码,以便所有代码路径都返回值。 代码应该是这样的

public static async Task<HttpResponseMessage> PostWithRetryAsync(this HttpClient httpClient, Uri uri, HttpContent content, int maxAttempts, Action<int> logRetry)
{
    HttpResponseMessage response = null;
    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
        {
            response = await httpClient.PostAsync(uri, content).ConfigureAwait(false);
            response.EnsureSuccessStatusCode();

        }
        catch (HttpRequestException)
        {
            ++attempt;
            if (attempt > maxAttempts)
                throw;
        }
    }
    return response;
}
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;
            else
                return something; // HERE YOU NEED TO RETURN SOMETHING
        }
    }
}

但是如果您想继续循环,则需要最后返回:

    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;               
        }
    }
    return something; // HERE YOU NEED TO RETURN SOMETHING
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM