使用异常退出函数
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,但它同样会混淆您的意图,因此同样糟糕(再次恕我直言)。
我在编写解析器时遇到了 运行 问题。 以下函数均调用 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,但它同样会混淆您的意图,因此同样糟糕(再次恕我直言)。