简体   繁体   English

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

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

I've made a basic extension method to add retry functionality to my HttpClient.PostAsync :我做了一个基本的扩展方法来向我的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;
        }
    }
}

The above code gives me the following error:上面的代码给了我以下错误:

Error CS0161 'HttpClientExtensions.PostWithRetryAsync(HttpClient, Uri, HttpContent, int, Action)': not all code paths return a value.错误 CS0161 'HttpClientExtensions.PostWithRetryAsync(HttpClient, Uri, HttpContent, int, Action)':并非所有代码路径都返回值。

If I add throw new InvalidOperationException() at the end (or return null for that matter), the error goes away as expected.如果我在末尾添加throw new InvalidOperationException() (或return null ),则错误会按预期消失。 What I'd really like to know is: is there any code path that actually exits this method without either a value being returned or an exception being thrown?我真正想知道的是:是否有任何代码路径实际退出此方法而没有返回值或抛出异常? I can't see it.我看不到它。 Do I know more than the compiler in this case, or is it the other way around?在这种情况下,我是否比编译器了解更多,还是相反?

The simple reason is that the compiler has to be able to statically verify that all execution flow paths end up with a return statement (or an exception). 原因很简单,编译器必须能够静态验证所有执行流路径是否以return语句(或异常)结尾。

Let's look at your code, it contains: 让我们看看您的代码,其中包含:

  • Some variables controlling a while loop 一些控制while循环的变量
  • A while loop, with the return statement embedded 一个while循环,嵌入return语句
  • No return statement after the loop 循环return语句

So basically the compiler has to verify these things: 因此,基本上,编译器必须验证以下内容:

  1. That the while loop is actually executed 实际上执行了while循环
  2. That the return statement is always executed 总是执行return语句
  3. Or that some exception is always thrown instead. 总是抛出一些异常。

The compiler is simply not able to verify this. 编译器根本无法验证这一点。

Let's try a very simple example: 让我们尝试一个非常简单的示例:

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

This trivial example will generate the exact same error: 这个简单的示例将产生完全相同的错误:

CS0161 'Test()': not all code paths return a value CS0161'Test()':并非所有代码路径都返回一个值

So the compiler is not able to deduce that because of these facts: 因此,由于以下事实,编译器无法推断出这一点:

  • a is a local variable (meaning that only local code can impact it) a是局部变量(意味着只有局部代码可以影响它)
  • a has an initial value of 1 , and is never changed a的初始值为1 ,并且永远不变
  • If the a variable is greater than zero (which it is), the return statement is reached 如果a变量大于零(是),则return语句

then the code will always return the value 10. 那么代码将始终返回值10。

Now look at this example: 现在看这个例子:

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

Only difference is that I made a a const . 唯一的不同是,我做a一个const Now it compiles, but this is because the optimizer is now able to remove the whole loop, the final IL is just this: 现在可以编译了,但这是因为优化器现在可以删除整个循环,最终的IL就是这样:

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

The whole while loop and local variable is gone, all is left is just this: 整个while循环和局部变量都消失了,剩下的就是这个:

return 10;

So clearly the compiler does not look at variable values when it statically analyzes these things. 因此很明显,编译器在静态分析这些内容时不会查看变量值。 The cost of implementing this feature and getting it right probably outweighs the effect or the downside of not doing it. 实施并正确实现此功能的成本可能会超过不执行此操作所带来的影响或负面影响。 Remember that "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." 请记住, “每个功能在漏洞中的起点都是100点,这意味着它必须对整个程序包产生显着的净积极影响,才能使其成为语言。” .

So yes, this is definitely a case where you know more about the code than the compiler. 所以是的,这绝对是您比编译器更了解代码的情况。


Just for completeness, let's look at all the ways your code can flow: 为了完整起见,让我们看一下代码可以流动的所有方式:

  1. It can exit early with an exception if maxAttempts is less than 1 如果maxAttempts小于1,它可能会提早退出,并发生异常
  2. It will enter the while -loop since attempt is 1 and maxAttempts is at least 1. 它将进入while因为-loop attempt为1, maxAttempts至少为1。
  3. If the code inside the try statement throws a HttpRequestException then attempt is incremented and if still less than or equal to maxAttempts the while -loop will do another iteration. 如果里面的代码try语句抛出一个HttpRequestException然后attempt递增,如果仍然小于或等于maxAttemptswhile -loop会做另一次迭代。 If it is now bigger than maxAttempts the exception will bubble up. 如果现在大于maxAttempts则异常将冒泡。
  4. If some other exception is thrown, it won't get handled, and will bubble out of the method 如果引发了其他异常,它将不会得到处理,并且会冒出该方法
  5. If no exception is thrown, the response is returned. 如果没有抛出异常,则返回响应。

So basically, this code can be said to always end up either throwing an exception or return, but the compiler is not able to statically verify this. 因此,基本上可以说,此代码总是以抛出异常或返回为最终结果,但是编译器无法静态验证此结果。


Since you have embedded the escape hatch ( attempt > maxAttempts ) in two places, both as a criteria for the while -loop, and additionally inside the catch block I would simplify the code by just removing it from the while -loop: 由于您已将转义attempt > maxAttempts填充( attempt > maxAttempts )嵌入了两个位置,均作为while -loop的标准,另外还包含在catch块内,因此我只需将其从while -loop中删除即可简化代码:

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

Since you're guaranteed to run the while -loop at least once, and that it will actually be the catch block that exits it, just formalize that and the compiler will again be happy. 由于可以确保至少运行一次while -loop,并且实际上它是退出该循环的catch块,因此只需对其进行形式化,编译器将再次感到满意。

Now the flow control looks like this: 现在,流控制如下所示:

  • The while loop will always execute (or we have already thrown an exception) while循环将始终执行(或者我们已经引发了异常)
  • The while loop will never terminate (no break inside, so no need for any code after the loop) while循环永远不会终止(内部不会break ,因此循环后不需要任何代码)
  • The only possible way to exit the loop is either an explicit return or an exception, neither of which the compiler has to verify any more because the focus of this particular error message is to flag that there is potentially a way to escape the method without an explicit return . 退出循环的唯一可能方法是显式return或异常,编译器都不必再进行验证,因为此特定错误消息的重点是标记出存在一种可能的方法,而无需明确的return Since there is no way to escape the method accidentally any more the rest of the checks can simply be skipped. 由于无法避免方法的意外退出,因此可以简单地跳过其余​​检查。

    Even this method will compile: 即使此方法也可以编译:

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

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

As Error states that not all code paths return a value you are not returning value for each code path 由于错误指出not all code paths return a value您并不是为每个代码路径都返回值

You must throw a exception or return value 您必须抛出异常或返回值

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

you could modify your code so that all code path returns value. 您可以修改代码,以便所有代码路径都返回值。 code should be something like this 代码应该是这样的

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
        }
    }
}

but in case you want to continue to loop, you need to return at the end: 但是如果您想继续循环,则需要最后返回:

    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