限制try块的作用域,这有关系吗?

4

可能是重复问题:
Java中的try块是否应该尽可能紧密地作用于范围内?

保持try块小有没有性能优势(特别是在C++或Java中),除了更明确地告诉读者哪个语句可能会抛出异常之外。

给定以下方法,我不希望从该方法中抛出异常。

void function() throws Exception
{
    statement1
    statement2
    statement3 // can throw
    statement4
    statement5
}

你认为这样做更好吗:

选项1

void function()
{
    try {
        statement1
        statement2
        statement3 // can throw
        statement4
        statement5
    }
    catch (...) {
    }
}

或者

选项2

void function()
{
    statement1
    statement2

    boolean success = false;
    try {
        statement3 // can throw
        success = true;
    }
    catch (...) {
    }

    if (success)
    {
        statement4
        statement5
    }
}

1
另外,你为什么要压制这样的异常呢?话虽如此,选项1更加简洁。像你所说的那样,这样的注释可以提供与第二个选项相同的好处(知道哪个函数可能会抛出异常),而不会显得丑陋。 - GManNickG
2
@GMan 为什么我要压制异常?因为这只是我脑海中的一个简单示例,用来说明我的性能问题。我想你接下来会说代码片段无法编译。 - Integer
我认为我已经两次传达了“更好”的上下文:问题是“在保持try块大小方面是否有任何性能优势(特别是在C++或Java中)”,标题是“限制try块范围。有关系吗?”。我认为我的问题很清楚。考虑到整个东西本质上是伪代码,我觉得你问我为什么要消除异常是很愚蠢的。我只能翻白眼。但还是谢谢。 - Integer
我的对于#2的考虑完全取决于try块的作用域大小是否会影响性能。你提到了一个无关的压制异常的点,这让我觉得你完全没有理解我的意思,所以我试图把你带回到正题上来。然而你又误解了我想表达的意思,认为我是在防御。 - Integer
这是一个几乎与https://dev59.com/2nE85IYBdhLWcg3wvGKm相同的内容。那里有更好的答案。 - Iain Samuel McLean Elder
显示剩余3条评论
4个回答

3

至少在我看过的编译器和异常处理机制中,不应该有任何区别。异常抛出的嵌套深度可能会有影响,但只有在抛出异常时才会产生影响,普遍的共识是在这种情况下,性能通常可以忽略不计。


我相信在一些旧版本的编译器中,即使打开try块也会有惩罚,但现在它们已经将这项工作转移到异常块中以提高性能并鼓励开发人员更多地使用try块。 - slf
@slf: Java是这样的吗?至少在C++中,还有相当多的开销。(尽管这仍然因平台而异) - jalf
@jalf:实际上,我在谈论Objective-C。不幸的是,我不确定每个c++编译器的具体规格。有没有什么电子表格?我很想看看数字。 - slf
我不知道它是否仍然被认为是最新的,但在TR18015 5.4中有一个关于异常开销的处理 - http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf - 根据该文档,使用“表格”方法的良好实现应在进入try块时不会产生运行时开销。 - Georg Fritzsche
据我所知,这是64位Windows采取的方法。但32位不是这样,因此你会得到运行时开销。其他平台我不确定。表格方法的问题在于它使用更多的内存。 - jalf
显示剩余2条评论

2

我不关注性能问题,除非你知道有瓶颈,否则我认为可读性更重要。话虽如此:

这取决于语句1-5的相关性。如果它是一个在5个步骤中执行的逻辑操作,我更喜欢选项1。

在选项2中,在try块的末尾设置成功标志非常丑陋且容易出错,我不建议在任何情况下使用该方法。


如果当3抛出异常时,4和5永远不会被执行,那么选项2只会让人更难理解发生了什么。+1 - Geoff Reedy
就可读性而言,选项2不是更易读吗?因为它更清晰地告诉读者哪一行代码出错了。我认为这是一个重要的信息。 - Integer
2
选项2很丑,更难理解和维护。 - Kevin Gale
1
@Integer,那么为什么还要使用异常呢?我个人认为将正常流程作为一个块来阅读,将异常处理作为下面的单独部分更容易理解。如果你想要的话,可以检查每个被调用函数返回的错误代码(例如在C中等),但我不喜欢这样混合逻辑和错误处理。 - Trent

1

选项1很好。一些代码行不会抛出异常并不重要。首先看看代码,它看起来更简洁。没有性能损失。

然而,你会听到有关不加区分地吞噬所有异常并忽略它们的讲座。

这是从使用异常(甚至拥抱异常)的代码层过渡到不使用异常的代码层时出现的类型不匹配问题。似乎你上面的层不喜欢异常,而下面的层喜欢。你上面的层不喜欢异常有原因吗?如果需要向上面的层解释为什么语句3失败,会发生什么?那么你就必须编写可怕的异常返回代码转换函数。


1
正如其他人所说,这些情况下生成的代码应该只有最小的差异。异常处理的“成本”与在堆栈展开时必须销毁多少临时对象有关(在较小程度上,代码必须跳转的位置数量)。这与堆栈的深度成比例——即在抛出异常和捕获异常之间有多少个函数调用——而不是从try块的开始到throw之间执行的指令数量。

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