错误原因CS0161:不是所有的代码path都返回一个值

我做了一个基本的扩展方法来添加重试function到我的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)':不是所有的代码path都返回一个值。

如果我添加throw new InvalidOperationException()在最后(或return null ),错误消失如预期。 我真的很想知道的是:是否有任何代码path实际上退出此方法没有返回值或exception被抛出? 我看不到它。 在这种情况下,我是否比编译器知道更多,还是相反?

简单的原因是编译器必须能够静态validation所有的执行stream程path是以return语句(或exception)结束的。

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

  • 一些控制while循环的variables
  • 一个while循环,embedded了return语句
  • 循环没有return语句

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

  1. while循环实际上被执行
  2. return语句总是被执行
  3. 或者总是抛出一些exception。

编译器根本无法validation这一点。

我们来看一个非常简单的例子:

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

这个微不足道的例子会产生完全相同的错误:

CS0161'Test()':不是所有的代码path都返回一个值

所以编译器无法推断出这些事实:

  • a是一个局部variables(意味着只有本地代码可以影响它)
  • a的初始值为1 ,永不改变
  • 如果avariables大于零(它是),则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循环和局部variables都不见了,剩下的只是这个:

 return 10; 

所以很明显,编译器在静态分析这些东西时不会查看variables值。 实现这个function和实现这个function的成本可能大于不做这个的效果或者不利的一面。 请记住: “每个function在洞中开始100分,这意味着它必须对整个套件产生显着的净积极影响,才能使其成为语言。” 。

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


为了完整起见,我们来看看代码可以stream的所有方式:

  1. 如果maxAttempts小于1,它可以提前退出
  2. 它将进入while -loop,因为attempt是1, maxAttempts至less是1。
  3. 如果try语句内部的代码抛出一个HttpRequestException那么attempt会增加,如果仍然小于或等于maxAttemptswhile -loop将会执行另一次迭代。 如果它现在比maxAttempts更大,则exception将会冒泡。
  4. 如果抛出一些其他的exception,将不会被处理,并会冒出方法
  5. 如果没有引发exception,则返回响应。

所以基本上,这段代码可以说是总是抛出一个exception或返回,但编译器无法静态validation这一点。


既然你已经在两个地方embedded了escape hatch( attempt > maxAttempts ),作为while -loop的标准,另外在catch块内部,我将通过从while循环中移除来简化代码:

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

既然你保证至less运行一次while -loop,并且它实际上将成为退出的catch块,只需将其正式化,编译器就会再次开心。

现在stream量控制如下所示:

  • while循环将始终执行(或者我们已经抛出exception)
  • while循环将永远不会终止(内部没有break ,所以在循环之后不需要任何代码)
  • 退出循环的唯一可能的方法是显式return或exception,编译器不必再进行validation,因为这个特定错误消息的焦点是标记有没有可能的方法来逃避方法明确的return 。 由于没有办法意外地逃避方法,其余的检查可以简单地跳过。

    即使这种方法将编译:

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

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

 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 } 

由于错误指出not all code paths return a value你不会为每个代码path返回值

您必须抛出exception或返回值

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

您可以修改您的代码,以便所有代码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; }