使用异常退出函数

Using exception to exit function

我在编写解析器时遇到了 运行 问题。 以下函数均调用 GetSymbol() 函数。函数也互相调用。 Body() 会调用 Statement(),Statement() 会调用 Expression() 等等。

问题是,在任何函数中,符号列表都可能为空。我认为没有必要为每个函数添加额外的代码。相反,我在程序中添加了一个 try catch。

这样使用异常是不是错了 因为如果 GetSymbol 运行 没有符号,那是 预期的 行为。

我应该避免抛出异常吗?

List<Symbol> symbols;

private void Term()
{...}
private void Expression()
{...}
private void Statement()
{...}
private void Body()
{...}

private Symbol GetSymbol()
{
    if (symbols.Count > 0)

    else
        throw new OutOfTokenException();
}

public void Program()
{
    try
    {
        while (Accept(Symbol.LBRACE))
            Body();
    }
    catch (OutOfTokenException ote)
    {
        Output("Unexpected end of file");
    }
}

}

Exceptions 表示您的用例集的 exception 情况,这是您的程序并不真正期望的事情,但可能会发生,这是一种常见情况,尤其是在与用户打交道时输出。

我不明白的是如何你的符号列表可以是空的?如果符合您的规则的有效文本正文可以为空,那么您将需要无一例外地满足这种情况。

如果另一方面,一组空符号表示用户应该做但没有做的事情,没有它你的应用程序就无法运行,那么你应该抛出异常。

你是对的 - 抛出异常来处理好的情况是不好的做法。这几乎与对一般分支和控制流使用异常是一种反模式(here is a Java related question 解释它)相同。

如果没有找到符号,您的 GetSymbol() 方法应该 return 为 null - 除非您特别想要在没有符号时出现错误状态。

尝试更改您的代码,这样您就可以检查是否还有剩余符号。像这样:

private Symbol GetSymbol()
{
    // just your logic to get symbol
}

private bool SymbolExists()
{
    return this.symbols.Count > 0;
}

并将您的调用代码重写为:

public void Program()
{
        while (SymbolExists())
        {
            Accept(GetSymbol(Symbol.LBRACE)
            Body();
        }
}

同时考虑将垃圾代码移动到独立的 unit/class,例如 SymbolReader 等。这样您就可以将 "symbols" 集合作为参数传递,并在分配之前对其进行验证。您可以保留原始的 GetSymbol() 实现,以确保在有人滥用您的代码时抛出正确的异常(通过在阅读之前不调用 SymbolExists())。

您的代码缺少一些上下文,例如class 声明将告诉 class 的 public 接口是什么。我假设 GetSymbol(Symbol.LBRACE) 调用是一个错误,因为没有具有匹配签名的方法。

似乎 class 的前提条件允许它在任何时候调用 Program,所以 Program 永远不会抛出异常。请记住,每个方法都有一个前置条件和一个后置条件。当且仅当调用者违反前提条件时,您应该抛出异常。

现在的问题是:当没有符号可获取时,是否允许调用GetSymbol?答案决定是否应该抛出异常。

但是:try/catch 的使用混淆了您的意图。这足以成为摆脱它的理由。像 Yura 建议的 SymbolExists 方法可以使您的意图非常明确。

如果 symbols 为空,则创建 GetSymbol return null 并检查 Accept 中的 null 是一个糟糕的解决方案。您仍然可以摆脱 try/catch,但它同样会混淆您的意图,因此同样糟糕(再次恕我直言)。