[英]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
语句 因此,基本上,编译器必须验证以下内容:
while
循环 return
语句 编译器根本无法验证这一点。
让我们尝试一个非常简单的示例:
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点,这意味着它必须对整个程序包产生显着的净积极影响,才能使其成为语言。” 。
所以是的,这绝对是您比编译器更了解代码的情况。
为了完整起见,让我们看一下代码可以流动的所有方式:
maxAttempts
小于1,它可能会提早退出,并发生异常 while
因为-loop attempt
为1, maxAttempts
至少为1。 try
语句抛出一个HttpRequestException
然后attempt
递增,如果仍然小于或等于maxAttempts
的while
-loop会做另一次迭代。 如果现在大于maxAttempts
则异常将冒泡。 因此,基本上可以说,此代码总是以抛出异常或返回为最终结果,但是编译器无法静态验证此结果。
由于您已将转义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.