GOTO 和 THROW 之间的区别?

发布于 2024-09-10 23:54:46 字数 1453 浏览 3 评论 0原文

最近很多人指责我只提到一个词——“goto”。
这让我想知道,为什么它被认为是一个如此令人讨厌的词。
我知道之前关于该主题的一些讨论,但这并不能说服我 - 其中一些答案只是说“这很糟糕”,甚至没有试图解释,有些还带来了原因,与脚本语言,如 PHP、IMO

无论如何,我要问一个非常具体的问题:
让我们比较一下 gotothrow 语句。
在我看来,两者都做同样的事情:根据某些条件避免执行某些部分的代码。
如果是这样 - throw 是否具有与 goto 相同的缺点? (如果有的话)?
如果不是——那就有什么区别了。

经验丰富的人谁能说出以下两个代码片段的代码结构之间的根本概念差异?
关于这些代码片段,不是“理论上”或“一般来说”或“这是一个不错的 XKCD 漫画!”。
为什么第一个被认为是辉煌的,而后一个被认为是最致命的罪恶?

#$a=1;
#$b=2;

/* SNIPPET #1 */

try {
    if (!isset($a)) {
      throw new Exception('$a is not set');
    }
    if (!isset($b)) {
      throw new Exception('$b is not set');
    }
    echo '$a + $b = '.($a + $b)."<br>\n";
} catch (Exception $e) {
    echo 'Caught exception: ', $e->getMessage(), "<br>\n";
}

/* SNIPPET #2 */

if (!isset($a)) { 
  $message = '$a is not set';
  goto end;
}
if (!isset($b)) {
  $message = '$b is not set';
  goto end;
}
echo '$a + $b = '.($a + $b)."<br>\n";

end:
if (!empty($message)) {
  echo 'Caught exception: ', $message, "<br>\n";
}

请注意,我知道在制作意大利面条时,投掷功能更加强大且灵活。这对我来说没有什么主要区别。这是使用的问题,而不是概念的问题。

编辑
许多人告诉我,第一个示例应该更新使用。
原因:异常应该仅用于处理错误,而不是实现业务逻辑。
看起来很明智。

因此,停止无用代码执行的唯一方法是 goto (更不用说一些替代品,例如 while、return 等,它们是同样的事情,但更难实现)?

Many people accused me recently for just mentioning a single word - "goto".
It makes me wonder, why it is considered such a nasty word.
I am aware of several previous discussions on the topic, but it doesn't convince me - some of the answers just says "it's bad" not even trying to explain and some bring reasons, irrelevant for scripting languages like PHP, IMO.

Anyway, I am going to ask a very particular question:
Let's compare goto and throw statements.
Both, in my opinion, do the same thing: avoiding execution of some portion of code based on some condition.
If so - do throw have same disadvantages as goto? (if any)?
If not - whit is the difference then.

Anyone experienced enough, who can tell the fundamental, conceptual difference between code structures of the two following code snippets?
Regarding these very code snippets, not "in theory" or "in general" or "here is a nice XKCD comic!".
Why the first one considered to be brilliant and latter one considered to be deadliest of sins?

#$a=1;
#$b=2;

/* SNIPPET #1 */

try {
    if (!isset($a)) {
      throw new Exception('$a is not set');
    }
    if (!isset($b)) {
      throw new Exception('$b is not set');
    }
    echo '$a + $b = '.($a + $b)."<br>\n";
} catch (Exception $e) {
    echo 'Caught exception: ', $e->getMessage(), "<br>\n";
}

/* SNIPPET #2 */

if (!isset($a)) { 
  $message = '$a is not set';
  goto end;
}
if (!isset($b)) {
  $message = '$b is not set';
  goto end;
}
echo '$a + $b = '.($a + $b)."<br>\n";

end:
if (!empty($message)) {
  echo 'Caught exception: ', $message, "<br>\n";
}

Note that I am aware of the fact that throw is more powerful and flexible in making spaghetti. It does not make the principal difference for me. It is matter of use, not concept.

EDIT
I've been told by many people that first example should be newer used.
Reason: Exceptions should be used to handle errors only, not to implement business logic.
It looks sensible.

Therefore, the only way left to stop useless code execution is goto (not to mention some substitutes, such as while, return etc, which are the same matter but harder to implement)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

断桥再见 2024-09-17 23:54:46

throw/catch 比 goto 灵活得多:您可以从调用抛出异常的代码的函数中捕获。此外,所需的析构函数会自动调用。

Throw/catch are much more flexible than goto: you can catch from the functions which call the code throwing the exception. Additionally, the required destructors are called automagically.

一向肩并 2024-09-17 23:54:46

我认为你需要反过来问这个问题:当异常完全适合这种情况时,为什么要使用 goto ?这就是例外的原因。

您可能还会问“当我可以轻松使用 goto 时,为什么要使用 whilefor 和函数?”

I think you need to ask the question the other way around: why would you use goto when exceptions are perfectly suited to the situation? That's what exceptions are for.

You may as well ask "why use while, for and functions when I can just as easily use goto?"

云裳 2024-09-17 23:54:46

goto 将执行路径硬编码到代码中。另一方面,异常允许在运行时确定执行路径。

例如,假设您有一个在错误时引发异常的数据库类。除了例外情况,您可以捕获该错误,并在呈现错误页面之前执行其他操作(例如清理分配的资源,或者如果您使用非事务性数据库类型,则“回滚”先前的更改。如果您使用 goto,则不会没有机会这样做,因为 goto 会呈现错误页面。

记住,保持代码可重用和灵活是两者的对立面。

goto hard codes the execution path into the code. Exceptions on the other hand allow the execution path to be determined at runtime.

For example, let's assume you have a database class that throws an exception on error. With exceptions, you could capture that error, and do something else before rendering the error page (Like clean up allocated resources, or "rollback" prior changes if you use a non-transactional db type. If you used a goto, you wouldn't have the chance to do that, since the goto would have rendered the error page.

Remember, keep your code reusable and flexible. goto is the antithesis of both...

孤独陪着我 2024-09-17 23:54:46

在您的示例中,当您检查错误条件并引发异常或在条件失败时调用 goto 时,实际上没有区别。您的示例可以重新编码以消除对任一构造的需要。

异常有用的地方是您调用可能有错误状态但无法自行处理的方法,如以下伪代码所示:

try
{
    $c = add($a, $b);
    echo '$a + $b = '.($c)."<br>\n";
}
catch (Exception $e)
{
    echo 'Caught exception: ', $e->getMessage(), "<br>\n";
}

add 方法执行错误checking:

if (!isset($a))
{
    throw new Exception('$a is not set');
}
if (!isset($b))
{
    throw new Exception('$b is not set');
}

然后返回相加的结果。

这意味着您的主代码流显示了程序的预期路径。

In your example there is effectively no difference as you are checking the error condition and either raising the exception or calling goto if the condition fails. Your example could be recoded to remove the need for either construct.

Where exceptions are useful is where you are calling methods that may have an error state but can't handle it themselves as the following pseudo code illustrates:

try
{
    $c = add($a, $b);
    echo '$a + $b = '.($c)."<br>\n";
}
catch (Exception $e)
{
    echo 'Caught exception: ', $e->getMessage(), "<br>\n";
}

The add method does the error checking:

if (!isset($a))
{
    throw new Exception('$a is not set');
}
if (!isset($b))
{
    throw new Exception('$b is not set');
}

then returns the result of the addition.

This means that your main code flow shows the expected path through the program.

⒈起吃苦の倖褔 2024-09-17 23:54:46

由于还没有人对这个问题提供 OP 认为可以接受的答案,我将投身其中。

…代码结构之间基本的、概念上的区别
以下两个代码片段中的哪一个?

您提供的两个代码片段之间根本没有概念差异。在这两种情况下都会测试条件,如果满足,则会将消息抛出到程序的另一部分进行处理。

为什么第一个被认为是出色的,而后一个被认为是
成为最致命的罪孽?

感谢斯拉普内尔上校为下面的咆哮打开了大门。

<咆哮>

每个编程结构都有被滥用的可能性。对goto的教条式迫害让我想起了对PHP的类似抨击。 PHP 会导致糟糕的编程习惯。 相比:Goto 会导致意大利面条式代码。

简而言之:goto 是“邪恶的”,因为 Edsger Dijkstra 是这么说的尼克劳斯·沃斯 (Niklaus Wirth) 对此表示认可。 ;-p

为了您的阅读乐趣:http://en.wikipedia.org/wiki/Goto

对 GOTO 最著名的批评可能是 Edsger 1968 年的一封信
Dijkstra 称​​Go To statements 被认为是有害的。在那封信中
Dijkstra 认为应该废除不受限制的 GOTO 语句
来自高级语言,因为它们使任务复杂化
分析和验证程序的正确性(特别是
那些涉及循环的)。另一种观点提出于
Donald Knuth 的带有 go to 语句的结构化编程
分析了许多常见的编程任务并发现其中一些
GOTO 是最适合使用的语言结构。

它接着说,

一些程序员,例如Linux内核设计者和编码员Linus
Torvalds 或软件工程师兼书籍作者 Steve McConnell 也
反对 Dijkstra 的观点,指出 GOTO 可能是一个有用的
语言特性,提高程序速度、大小和代码清晰度,
但只有当由一个相对明智的人以明智的方式使用时
程序员。

也就是说,自 Commodore BASIC 以来,我个人还没有发现 GOTO 的实际用途,但这既不存在也不存在。 :-)

Since no one has yet provided an answer to this question that the OP found acceptable, I’ll throw my hat in the ring.

…the fundamental, conceptual difference between code structures
of the two following code snippets?

There is no conceptual difference at all between the two code snippets you provided. In both instances a condition is tested, and if met, a message is thrown to another part of the program for handling.

Why the first one considered to be brilliant and latter one considered
to be deadliest of sins?

Thank you Col. Shrapnel for opening the door to the following rant.

<rant>

Every programming construct holds the potential for abuse. The dogmatic persecution of goto reminds me of the similar bashing of PHP. PHP leads to bad programming practices. Compared to: Goto leads to spaghetti code.

Simply put: goto is “evil” because Edsger Dijkstra said so and Niklaus Wirth gave it his seal of approval. ;-p

</rant>

For your reading enjoyment: http://en.wikipedia.org/wiki/Goto

Probably the most famous criticism of GOTO is a 1968 letter by Edsger
Dijkstra called Go To Statement Considered Harmful. In that letter
Dijkstra argued that unrestricted GOTO statements should be abolished
from higher-level languages because they complicated the task of
analyzing and verifying the correctness of programs (particularly
those involving loops). An alternative viewpoint is presented in
Donald Knuth's Structured Programming with go to Statements which
analyzes many common programming tasks and finds that in some of them
GOTO is the optimal language construct to use.

It goes on to say,

Some programmers, such as Linux Kernel designer and coder Linus
Torvalds or software engineer and book author Steve McConnell also
object to Dijkstra's point of view, stating that GOTOs can be a useful
language feature, improving program speed, size and code clearness,
but only when used in a sensible way by a comparably sensible
programmer.

That said, I personally haven’t found a practical use for GOTO since Commodore BASIC, but that’s neither here nor there. :-)

柠檬心 2024-09-17 23:54:46

goto 非常适合做这样的事情:

foreach ($items as $item) {
    if ($item->is_match()) goto match;
}

$item = new Item();

match:
render('find_item.php', $item);

与此相反:

$matching_item = null;
foreach ($items as $item) {
    if ($item->is_match()) {
        $matching_item = $item;
        break;
    }
}
if ($matching_item === null) {
    $item = new Item();
}

render('find_item.php', $item);

goto is usfull for doing things like this:

foreach ($items as $item) {
    if ($item->is_match()) goto match;
}

$item = new Item();

match:
render('find_item.php', $item);

as apposed to this:

$matching_item = null;
foreach ($items as $item) {
    if ($item->is_match()) {
        $matching_item = $item;
        break;
    }
}
if ($matching_item === null) {
    $item = new Item();
}

render('find_item.php', $item);
苍景流年 2024-09-17 23:54:46

与 throw 相比,goto 可能会有一点性能提升,因为它不会创建任何异常堆栈。

goto may have a little performance boost comparing to throw, since it doesn't create any exception stack.

夜访吸血鬼 2024-09-17 23:54:46

实际上,两个代码段之间的区别在于,您必须花费更多时间使用“goto”代码向其他程序员解释自己。

无论好坏,大多数程序员都认为永远不应该使用 goto。也许你可以证明这是没有根据的,并且你的 goto 代码是实现该功能的最佳方式。即便如此,你也必须与同事或任何与你合作的人进行斗争才能让他们接受它。

如果您自己工作并且没有人会看到您的代码 - 使用您喜欢的任何代码。但如果你在一个团队中工作,有时阻力最小的路径是最谨慎的。

In practical terms, the difference between your two code segments is that you'll have to spend more time explaining yourself to other programmers with the "goto" code.

For better or for worse, most programmers believe you should never use a goto. Perhaps you can prove that this is unfounded, and that your goto code is the best way to implement that function. Even so, you'll have to fight with coworkers or anyone else with whom you collaborate to get them to accept it.

If you are working by yourself and no one will see your code - use whatever you like. But if you're working on a team, sometimes the path of least resistance is most prudent.

孤芳又自赏 2024-09-17 23:54:46

“错误”的是,goto它可以实现多种控制机制,而不仅仅是throw,因此几乎没有固有的结构。因此,脚本程序员对使用 goto 替代方案感兴趣有几个原因:

首先,我们开始看到脚本从纯解释型转向字节码编译型。任何地方都可以找到这样的好例子,例如 Python、Perl 6、Scala (Lift)、Clojure (Compojure)。甚至 PHP 也有字节码编译器。由于 goto 的结构很少,因此无法进行控制机制的编译,这是相当大的性能损失,因为现代编译器通常非常擅长于此。对于抛出,编译器可以开始假设抛出的异常并不常见,因此它通常会以不太理想的“异常”控制路径为代价来优化“正常”控制路径。

即使您没有完整的编译器,当程序员使用具有更多结构的控制机制时,解释器也可以为程序员提供更多帮助。例如,for 循环块由编译器/解释器按词法限定范围,因此程序员可以使用此范围来模块化代码块。否则,使用 goto 您将被迫污染变量名称空间。

此外,如果您的语言允许您声明抛出异常的方法,编译器/解释器可以通知您尚未处理您调用的方法可能遇到的所有可能的异常(类似于< code>throws Java,如果您有这方面的经验)。使用goto,编译器不知道您要做什么,因此它不会在编译和测试期间为您提供帮助。

Goto 还要求程序员从错误中恢复。想象一下,如果您有一些像这样的代码(从您的代码片段中抓取并编辑):

if (!isset($a)) { 
  $message = '$a is not set';
  goto errorA;
}
if (!isset($b)) {
  $message = '$b is not set';
  goto errorB;
}
if (!moonInRightPhase) {
   goto errorP
}
echo '$a + $b = '.($a + $b)."<br>\n";

errorA:
// Do something non-trivial and unique to handle $a not being set.
goto afterError

errorP:
// Do something non-trivial and unique to handle moon phase
// This error requires drastic failure of code path:
goto failure

errorB:
// Do something non-trivial and unique to handle $b not being set.
goto afterError

afterError:
// Program continues

return SUCCESS

failure:
return FAILURE

这里的问题是程序员需要做正确的事情。如果他忘记goto afterError,他将运行下一个异常路径。使用 throw 机制就不太容易出错。

最后,我的个人经验让我不喜欢 goto 语句,只是因为它不容易阅读。当我试图理解一段带有四个或五个名为 some-errorfailretry 标签的代码时,它们是不靠近有问题/异常的代码,解开它可能是一场噩梦。如果它不容易阅读,那么即使程序员试图回顾他或她一年前所做的事情也可能会导致错误。

作为这个答案的后记,我不认为“错误”是描述 goto 的正确方式。这可能来自于著名的 Dijkstra 论文的耸人听闻。 “强大”可能是一个更好的术语,因为它意味着它几乎可以做所有事情,但即使是最简单的错误也会使“错误”变成“可怕的错误”。我们不应该禁止工具,因为它们确实很危险;我们应该更好地教育人们了解他们的危险,并让他们做出决定。

The thing that's "wrong" is that goto it can implement many kinds of control mechanisms, not just throw, and therefore has little inherent structure. Because of this, there are several reasons why a script programmer would be interested in using goto alternatives:

First, we are starting to see scripts moving from purely interpreted to byte-code compiled. Good examples of this can be found anywhere, such as Python, Perl 6, Scala (Lift), Clojure (Compojure). Even PHP has byte-code compilers. Since goto has little structure, compilation of control mechanisms can't happen, which is a considerable performance loss because modern compilers are generally very good at this. For throw, compilers can start assuming that an exception being thrown is not common, and therefore it will optimize the "normal" control path often at the cost of a less optimal "abnormal" control path.

Even if you don't have a full blown compiler, interpreters can assist a programmer considerably more when the programmer uses control mechanisms that have more structure. For example, for loop blocks are lexically scoped by the compiler/interpreter, and so a programmer can use this scoping to modularize chunks of code. Otherwise, with goto you are forced to pollute the variable namespace.

Furthermore, if your language allows you to declare methods that throw exceptions, the compiler/interpreter can inform you that you haven't handled all the possible exceptions that the methods you are calling may experience (similar to throws Java, if you have experience there). With goto, the compiler has no idea what you're trying to do, so it won't help you out during compilation and testing.

Goto also requires the programmer to recover from the error. Imagine if you had some code like this one (snatched and edited from your snippets):

if (!isset($a)) { 
  $message = '$a is not set';
  goto errorA;
}
if (!isset($b)) {
  $message = '$b is not set';
  goto errorB;
}
if (!moonInRightPhase) {
   goto errorP
}
echo '$a + $b = '.($a + $b)."<br>\n";

errorA:
// Do something non-trivial and unique to handle $a not being set.
goto afterError

errorP:
// Do something non-trivial and unique to handle moon phase
// This error requires drastic failure of code path:
goto failure

errorB:
// Do something non-trivial and unique to handle $b not being set.
goto afterError

afterError:
// Program continues

return SUCCESS

failure:
return FAILURE

The problem here is that the programmer is required to do the right thing. If he forgets to goto afterError, he will run through the next exception path. With throw the mechanisms are a bit less error prone.

Finally, my personal experience makes me dislike the goto statement simply because it is not easy to read. When I'm trying to understand a piece of code with four or five labels named some-error, fail, retry, and they're not located close to the offending/excepting code, it can be a nightmare to untangle. And if it's not easy to read, then even the programmer trying to review what he or she did a year ago may lead to mistakes.

As a post-script to this answer, I don't believe "wrong" is the right way to describe goto. That might have come from sensationalizing the famous Dijkstra paper. "Powerful" may be a better term, because it implies that it can do pretty much everything, but even the simplest mistakes make "wrong" turn into "horribly wrong". We shouldn't ban tools because they're really dangerous; we should better educate people about their danger, and let them decide.

猫烠⑼条掵仅有一顆心 2024-09-17 23:54:46

我不像很多人那样讨厌 GOTO,但我认为它的主要问题是它不限于特定的“范围”。对于所有其他控制结构,您都知道它从哪里开始和在哪里结束。另一方面,GOTO 标签可能会放置在更难以预测的地方。您当然可以搜索标签,但这会破坏可读性。

当然,一切都取决于它如何使用。

I don't hate GOTO as much as many do, but I think its main problem is that it's not restricted to a specific "scope". With every other control structure, you know where it starts and where it ends. A GOTO label, on the other hand, may be placed in a lot more unpredictable places. You can of course search the label, but it breaks the readability.

Of course all is up to how it's used.

记忆之渊 2024-09-17 23:54:46

也不要使用。在您想要跳转到末尾的大部分代码和“break”语句周围使用“while(1)”。

while(1)
{
    if (!isset($a)) { 
      $message = '$a is not set';
      break;
    }
    if (!isset($b)) {
      $message = '$b is not set';
      break;
    }

    echo '$a + $b = '.($a + $b)."<br>\n";
    break;
}

if (!empty($message)) {
  echo 'Caught exception: ', $message, "<br>\n";
}

Don't use either. Use a "while(1)" around the bulk of your code and "break" statements where you want to jump to the end.

while(1)
{
    if (!isset($a)) { 
      $message = '$a is not set';
      break;
    }
    if (!isset($b)) {
      $message = '$b is not set';
      break;
    }

    echo '$a + $b = '.($a + $b)."<br>\n";
    break;
}

if (!empty($message)) {
  echo 'Caught exception: ', $message, "<br>\n";
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文