为什么可以';C#编译器不会告诉你这个函数总是返回或抛出吗?

为什么可以';C#编译器不会告诉你这个函数总是返回或抛出吗?,c#,exception,for-loop,code-analysis,C#,Exception,For Loop,Code Analysis,我有以下(简化的)方法: 我似乎很清楚,这个方法要么返回要么抛出。然而,C#编译器向我抱怨说,并不是所有的代码路径都返回值 这是C#编译器代码分析的一个限制吗 或者是否存在某种情况,我看不到for循环可以在没有抛出或返回的情况下完成 编译器不知道for循环至少会执行一次。它认为它是一个有效的选项,它从不运行,在这种情况下,它不会抛出或返回 虽然通过对您的程序进行足够复杂的分析,理论上可以证明它总是返回或抛出,但这种分析非常复杂,而且需要大量的性能。C编译器编译器不想冒错误的风险,或者考虑编译的

我有以下(简化的)方法:

我似乎很清楚,这个方法要么返回要么抛出。然而,C#编译器向我抱怨说,
并不是所有的代码路径都返回值

  • 这是C#编译器代码分析的一个限制吗
  • 或者是否存在某种情况,我看不到
    for
    循环可以在没有抛出或返回的情况下完成

编译器不知道for循环至少会执行一次。它认为它是一个有效的选项,它从不运行,在这种情况下,它不会
抛出
返回

虽然通过对您的程序进行足够复杂的分析,理论上可以证明它总是返回或抛出,但这种分析非常复杂,而且需要大量的性能。C编译器编译器不想冒错误的风险,或者考虑编译的时间大大增加,以便增加这种复杂的分析。他们选择使用“可访问”与“不可访问”代码的更简单定义,这样实现起来更容易、更快


至于实际的修复,只需在方法的最后添加一个
throw
,并可能添加一条注释,说明实际上不可能命中它。

编译器不知道for循环至少会执行一次。它认为它是一个有效的选项,它从不运行,在这种情况下,它不会
抛出
返回

虽然通过对您的程序进行足够复杂的分析,理论上可以证明它总是返回或抛出,但这种分析非常复杂,而且需要大量的性能。C编译器编译器不想冒错误的风险,或者考虑编译的时间大大增加,以便增加这种复杂的分析。他们选择使用“可访问”与“不可访问”代码的更简单定义,这样实现起来更容易、更快

至于实际的修复,只需在方法的最后添加一个
throw
,并可能添加一条注释,说明实际上不可能命中它

为什么C#编译器不能告诉这个函数总是返回或抛出

编译器不会试图模拟程序的工作方式来查看可以执行哪些分支。它只是查看所有可能的分支。在您的情况下,如果出于某种原因,
remaingtrys>=0
计算为false,那么它将从方法末尾脱落,而不返回布尔值或抛出值

您可以通过删除
for
循环中的复选框来修复它:

public bool DoWorkWithRetry()
{
    for (int remainingTries = Constants.MaxRetries;; remainingTries--)
    {
        // etc...
    }
}
那张支票没有任何用处

您还可以重写方法以完全避免问题:

for (int remainingTries = Constants.MaxRetries; remainingTries >= 0; remainingTries--)
{
    try
    {
        return DoWork();
    }
    catch (Exception ex)
    {
        // fall through to retry
    }
}

throw new WorkException(
        String.Format("Failed after {0} retries.", Constants.MaxRetries),
        ex);
也请不要抓住和吞下所有的例外。捕获您知道的特定异常。捕获所有可能的异常是个坏主意

为什么C#编译器不能告诉这个函数总是返回或抛出

编译器不会试图模拟程序的工作方式来查看可以执行哪些分支。它只是查看所有可能的分支。在您的情况下,如果出于某种原因,
remaingtrys>=0
计算为false,那么它将从方法末尾脱落,而不返回布尔值或抛出值

您可以通过删除
for
循环中的复选框来修复它:

public bool DoWorkWithRetry()
{
    for (int remainingTries = Constants.MaxRetries;; remainingTries--)
    {
        // etc...
    }
}
那张支票没有任何用处

您还可以重写方法以完全避免问题:

for (int remainingTries = Constants.MaxRetries; remainingTries >= 0; remainingTries--)
{
    try
    {
        return DoWork();
    }
    catch (Exception ex)
    {
        // fall through to retry
    }
}

throw new WorkException(
        String.Format("Failed after {0} retries.", Constants.MaxRetries),
        ex);

也请不要抓住和吞下所有的例外。捕获您知道的特定异常。捕获所有可能的异常是一个坏主意。

它必须非常聪明,才能知道for循环中的内容是否会被执行。为了让它知道,它必须测试For循环是否实际被执行,因为
MaxRitries>=0

它必须非常聪明,无法知道For循环中的内容是否被执行过。为了让它知道,它必须测试For循环是否实际得到执行,因为
MaxRitries>=0

编译器只是遵循语言规范

该语言不尝试执行“如果你从一个整数开始,然后反复减去一,你总会在某个点上碰到零”的分析。这甚至假设
常量.MaxRetries
是编译时常量

基本上,语言规范具有可访问性规则,如果其中任何一项为真,则可访问
for
语句的端点(C#4规范第8.8.3节):

  • for
    语句包含一个可访问的
    break
    语句,该语句退出
    for
    语句
  • for
    语句是可访问的,并且存在for条件,并且没有常量值
    true
后一点是这里的情况,因此可以到达
for
语句的末尾。不允许访问非void方法的结尾,因此会出现错误。(C规范第8.1节)


另一种方法是让语言变得越来越复杂。我完全同意不进行编译。

编译器只是遵循语言规范

该语言不尝试执行“如果你从一个整数开始,然后反复减去一,你总会在某个点上碰到零”的分析。这甚至假设
常量.MaxRetries
是编译时常量

基本上,语言规范具有可访问性规则,如果其中任何一项为真,则可访问
for
语句的端点(C#4规范第8.8.3节):

  • for
    语句包含一个可访问的
    break
    语句,该语句退出
    for
    语句