我已经做了一个基本的扩展方法,可以向我添加重试功能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; } } }
上面的代码给我以下错误:
错误CS0161’HttpClientExtensions.PostWithRetryAsync(HttpClient,Uri,HttpContent,int,Action)’:并非所有代码路径都返回值。
如果我throw new InvalidOperationException()在末尾添加(或return null为此添加),则错误将按预期消失。我真正想知道的是:是否有任何代码路径实际上退出此方法而没有返回值或引发异常?我看不到 在这种情况下,我是否比编译器了解更多,还是相反?
throw new InvalidOperationException()
return null
原因很简单,编译器必须能够 静态验证 所有执行流路径 是否 以return语句(或异常)结尾。
让我们看看您的代码,其中包含:
while
return
因此,基本上,编译器必须验证以下内容:
编译器根本无法验证这一点。
让我们尝试一个非常简单的示例:
public int Test() { int a = 1; while (a > 0) return 10; }
这个简单的示例将产生完全相同的错误:
CS0161’Test()’:并非所有代码路径都返回一个值
因此,由于以下事实,编译器无法推断出这一点:
a
1
那么代码将始终返回值10。
现在看这个例子:
public int Test() { const int a = 1; while (a > 0) return 10; }
唯一的区别是我做a了一个const。现在可以编译了,但这是因为优化器现在可以删除整个循环,最终的IL就是这样:
const
Test: IL_0000: ldc.i4.s 0A IL_0002: ret
整个while循环和局部变量都消失了,剩下的就是这个:
return 10;
因此很明显,编译器在静态分析这些内容时不会查看变量值。实施并正确实现此功能的成本可能会超过不执行此操作所带来的影响或负面影响。请记住,“每个功能在漏洞中的起点都是100点,这意味着它必须对整个程序包产生显着的净积极影响,才能使其成为语言。” 。
所以是的,这绝对是您比编译器更了解代码的情况。
为了完整起见,让我们看一下代码可以流动的所有方式:
maxAttempts
attempt``maxAttempts
try
HttpRequestException
attempt
因此,基本上可以说,此代码总是以抛出异常或返回为最终结果,但是编译器无法静态验证此结果。
由于您已经将逃逸阴影(attempt > maxAttempts)嵌入了两个位置,这两个位置均作为while-loop 的标准,并且另外在该catch块内,因此我只需将其从while-loop中删除即可简化代码:
attempt > maxAttempts
catch
while (true) { ... if (attempt > maxAttempts) throw; ... }
由于可以确保while至少运行一次-loop,并且实际上它将是catch退出该循环的块,因此只需对它进行形式化即可使编译器再次感到满意。
现在,流控制如下所示:
break
即使此方法也可以编译:
public int Test() { while (true) { } }