我已经做了一个基本的扩展方法来为我的 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.
如果我添加
throw new InvalidOperationException()
最后(或 return null
就此而言),错误按预期消失。我真正想知道的是:是否有任何代码路径实际退出此方法而没有返回值或抛出异常?我看不到。在这种情况下,我是否比编译器了解的更多,还是相反?
最佳答案
原因很简单,编译器必须能够静态验证所有执行流路径是否以 return 语句(或异常)结束。
让我们看看你的代码,它包含:
while
的一些变量循环 while
循环,带有 return
语句嵌入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
,并且永远不会改变 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;
很明显,编译器在静态分析这些东西时不会查看变量值。实现此功能并使其正确的成本可能超过不这样做的效果或缺点。请记住 "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
,它可以提前退出,但有一个异常(exception)。小于 1 while
-循环自 attempt
是 1 和 maxAttempts
至少为 1。try
语句抛出 HttpRequestException
然后 attempt
递增,如果仍然小于或等于 maxAttempts
while
-loop 将进行另一次迭代。如果它现在大于 maxAttempts
异常会冒泡。 所以基本上,这段代码可以说总是以抛出异常或返回结束,但编译器无法静态验证这一点。
由于您在两个位置嵌入了逃生舱口 (
attempt > maxAttempts
),这两个位置都是 while
的标准。 -loop,另外还有 catch
我会通过从 while
中删除它来简化代码-环形:while (true)
{
...
if (attempt > maxAttempts)
throw;
...
}
由于您保证运行
while
- 至少循环一次,它实际上是 catch
退出它的块,只需形式化它,编译器就会再次高兴。现在流量控制看起来像这样:
while
循环将始终执行(或者我们已经抛出异常)while
循环永远不会终止(内部没有 break
,所以循环后不需要任何代码)return
或异常,编译器不必再验证这两种情况,因为此特定错误消息的重点是标记有可能在没有显式 return
的情况下转义该方法的方法。 .由于无法再意外地逃避该方法,因此可以简单地跳过其余的检查。即使这个方法也会编译:
public int Test()
{
while (true)
{
}
}
关于c# - 错误原因 CS0161 : not all code paths return a value,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33606833/