控制流异常

4

这里有一篇与跨应用程序控制流相关的有趣帖子(链接)

最近,我遇到了一个有趣的问题。如何生成可能(实际上)无限递归序列中的第n个值。在成功时,这个特定算法将至少有10-15个堆栈引用。我的第一个想法是抛出一个类似于以下C#代码的SuccessException:

class SuccessException : Exception
{
    public string Value
    { get; set; }

    public SuccessException(string value)
        : base()
    {
        Value = value;
    }
}

那么可以这样做:
try
{
    Walk_r(tree);
}
catch (SuccessException ex)
{
    result = ex.Value;
}

我的思绪回到这里,我一遍又一遍地听到不要使用异常来控制流程。是否有任何理由可以使用异常?如果你要实现类似这样的功能,你会如何构建它的结构?

7个回答

5
在这种情况下,我将查看您的Walk_r方法,您应该有返回值的东西,抛出异常来表示成功并不是常见做法,而且至少会让任何看到代码的人非常困惑。更不用说与异常相关的开销了。

与在每个级别检测成功值并跳出10+堆栈引用的开销相比,这是什么?至于使用情况,这是一个个人项目,不过我想这并不能成为任何借口。 - Matthew Scharley
如果您的堆栈引用深度超过10层并停止处理,那么框架必须进行大量额外的处理。如果您设置递归调用以返回成功的值,则不应该有问题。 - Mitchel Sellers
确实如此。问题的另一半是我该如何以不同的结构进行构建。也许可以像这样 "bool Walk_r(object tree, out string result)"? - Matthew Scharley
我认为要回答这个问题,我们需要了解一下你正在做什么以及当前的方法结构。通常,递归调用的返回值是表示成功/失败的值。 - Mitchel Sellers
1
无论在那里进行什么操作,都没有理由为非异常情况抛出异常。返回值的方法是使用 return 语句。如果您遇到性能问题,可以使用显式堆栈。 - domgblackwell

1
我要扮演魔鬼的代言人,坚持使用异常来表示成功。捕获异常可能会很耗费资源,但与搜索本身的成本相比可能微不足道,并且可能比从方法中提前退出更少引起困惑。

搜索只需要几秒钟的时间。虽然不是微不足道,但就实时而言并不长。加上一个异常可能没什么关系,但我成功重构了调用,使其不再需要异常处理。对于其他人的可读性也很有用。 - Matthew Scharley

1

walk_r 应在被触发时简单地返回其值。这是一个相当标准的递归示例。我唯一可能看到的潜在问题是你说它可能无限循环,因此需要在 walk_r 代码中通过计算递归深度并在达到某个最大值时停止来进行补偿。

异常实际上使编程非常奇怪,因为方法调用现在会抛出异常来返回该值,而不是正常地返回。

try
{
    Walk_r(tree);
}
catch (SuccessException ex)
{
    result = ex.Value;
}

变成

result = Walk_r(tree);

我说得很明白。递归深度只有15,但覆盖的总问题空间实际上达到了数十亿,无法在实时情况下完成,因此是无限的。 - Matthew Scharley

0

在算法中抛出异常并不是一个很好的想法,特别是在.NET中。在某些语言/平台上,当抛出异常时,它们通常是相当高效的,例如当可迭代对象被耗尽时。


0

为什么不直接返回结果值呢?如果有返回值,就假设它是成功的。如果没有返回值,那么就意味着循环失败了。

如果你必须从失败中恢复,那么我建议你抛出一个异常。


0
使用异常的问题在于它们(从长远来看)非常低效和缓慢。在递归函数内部设置一个if条件语句并在需要时返回,这样做肯定会更容易。老实说,由于现代PC上的内存量很大,只有少量递归调用(<100)不太可能(但不是完全不可能)导致堆栈溢出。
如果堆栈是真正的问题,那么可能需要“创造性地”实现“深度限制搜索策略”,允许函数从递归中返回,并从最后(最深)节点重新启动搜索。
总之:异常应该仅在特殊情况下使用,我认为函数调用的成功并不符合这种情况。

0
在我的书中,将异常用于正常程序流程是最糟糕的做法之一。想象一下那些正在寻找被吞噬的异常并且设置了调试器以在发生异常时停止的可怜家伙。那个人现在变得很生气......而且他手里拿着一把斧头。 :P

因此,自定义异常和专门的catch是必要的。吞噬异常是不好的! - Matthew Scharley
这仍然会在我寻找已吞噬的错误时停止执行。正常应用程序流程中的异常是不正确的。使用架构来解决。 - Quibblesome

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接