Exception 使用异常进行流控制
最近有人告诉我,我正在滥用异常来控制我的应用程序中的流,所以我想这是我试图以某种方式澄清这种情况 在我看来,当一个方法遇到内部无法处理或调用方可以更好地处理的情况时,它应该抛出一个异常Exception 使用异常进行流控制,exception,language-agnostic,exception-handling,Exception,Language Agnostic,Exception Handling,最近有人告诉我,我正在滥用异常来控制我的应用程序中的流,所以我想这是我试图以某种方式澄清这种情况 在我看来,当一个方法遇到内部无法处理或调用方可以更好地处理的情况时,它应该抛出一个异常 因此-是否存在任何特定的规则集,在开发应用程序时可用于回答以下问题: 什么时候我应该抛出异常,什么时候我应该编写带有强nothrow保证的代码,它可能只返回bool,以指示成功或失败 当方法抛出异常时,我应该尽量减少情况的数量,还是相反,应该最大化它以提供处理这些情况时的灵活性 我应该坚持开发应用程序时使用的
因此-是否存在任何特定的规则集,在开发应用程序时可用于回答以下问题:
- 什么时候我应该抛出异常,什么时候我应该编写带有强nothrow保证的代码,它可能只返回
,以指示成功或失败bool
- 当方法抛出异常时,我应该尽量减少情况的数量,还是相反,应该最大化它以提供处理这些情况时的灵活性
- 我应该坚持开发应用程序时使用的框架/运行时设置的异常抛出约定,还是应该包装所有这些调用,以便它们与我自己的异常抛出策略相匹配
- 还有人建议我使用错误代码进行错误处理,这似乎非常有效,但从语法的角度来看很难看(而且,当使用错误代码时,开发人员将失去为方法指定输出的能力)。你觉得这个怎么样
第三个问题的示例(我在使用I/O框架时遇到以下情况): 所描述的框架不使用异常来处理错误,但是 其他代码确实使用它们。我应该总结每一个可能的失败吗 用
“?”
指示,并在这种情况下引发异常?
或者我应该将方法的签名更改为bool吗
准备EthereSultingOutputPath
,并仅指示操作是否正确
成功与否
另一个例子是,即使是
.netframework
也没有遵循一些严格的异常抛出策略。一些方法被记录为抛出10+种不同的异常类型,包括像NullArgumentException
这样的普通异常类型,但其中一些方法只是返回bool
,以指示操作的成功或失败
谢谢大家! 对异常错误使用异常是可以的。例如,如果托管环境中的内存分配失败,则抛出异常是可以的(在嵌入式环境中,最好以不同的方式处理)。同样,如果没有遵守约定,则抛出(例如,当接收到一个空指针时抛出,预期会有一个有效指针)可能是合理的(可能会中止)。对预期的错误或控制流使用异常将起作用,但会打乱可接受性能的任何希望。异常的问题在于,它们本质上是美化的GOTO,能够释放程序的调用堆栈。因此,如果您“使用异常进行流控制”,那么您可能将它们用作GOTO,而不是异常条件的指示。这正是异常的要点,也是它们命名的原因:它们应该只在异常情况下使用。因此,除非方法被设计为不抛出异常(例如.NET的
int.TryParse
),否则在异常情况下抛出异常是可以的
与Java相反,C#的优点在于,在C#中,您可以通过返回元组类型或使用out参数来返回两个或多个值。因此,返回一个错误代码作为方法的主返回值并没有多大问题,因为您可以使用out参数来完成其余的操作。例如,调用int.TryParse
的常见范例是
string s = /* Read a string from somewhere */;
int n;
if (int.TryParse(s, out n))
{
// Use n somehow
}
else
{
// Tell the user that they entered a wrong number
}
现在谈谈你的第三个问题,这似乎是最重要的。参考示例代码,您询问是否应返回bool
以指示成功/失败,或者是否应使用异常指示失败。不过,还有第三种选择。您可以定义一个枚举来告诉方法如何失败,并将该类型的值返回给调用方。然后,调用者有很多选择:调用者不必使用一堆try/catch语句,也不必使用if语句,因为if语句对方法失败的原因知之甚少,但可以选择编写其中之一
if (PrepareTheResultingOutputFile(templateFilePath, outputFilePath) == Status.Success)
// Do something
else
// It failed!
或
您可以在这个问题上找到更多信息,尤其是在中,我强烈推荐。我觉得第一个问题已经得到了回答,而且非常简单。仅在特殊情况下使用异常 关于你的其他问题: 我是否应该尽量减少使用该方法时出现的情况 抛出异常,或者相反,应该将其最大化为 在处理这些情况时提供灵活性 如果我正确理解了你的问题,它可以通过问题1自行回答。抛出异常的情况是在异常情况下,这当然不是很多情况。你应该确保你的程序几乎不会出现异常,除非太阳和土星对齐。然而,您应该有测试异常情况实际引发异常的测试用例 我应该遵守 开发应用程序时使用的或应该使用的框架/运行时 我包装所有这些调用,以便它们与我自己的异常抛出匹配 策略 视情况而定。在您的示例中,这取决于未找到文件是否异常?如果您希望此文件存在,而它不存在(例如,安装在程序旁边的文件),则应引发异常。如果是用户希望打开的文件,您不能确定,必须对此进行说明。我不认为这种类型的用户错误是异常的,可能会为此使用返回代码。其他事情要考虑的是:这个成功的关键是执行程序吗?
if (PrepareTheResultingOutputFile(templateFilePath, outputFilePath) == Status.Success)
// Do something
else
// It failed!
switch (PrepareTheResultingOutputFile(templateFilePath, outputFilePath))
{
case Status.Success:
// Do something
break;
case Status.FileNotPresent:
// Do something else
break;
case Status.CannotMakePath:
// Do something else
break;
// And so on
default:
// Some other reason for failure
break;
}
var pop3Clinet=new pop3Client();
try
{
pop3Client.SetServer("server-address");
pop3Client.SetUserName("username");
pop3Client.SetPassword("password");
var mails=pop3Client.ReceiveMails();
}
catch(NullReferenceException exp)
{
throw new Exception("can not connect to server.server-address is wrong or the server is down",exp);
}
catch(UnauthorizedAccess) //the exception is meaningful and can be rethrown or this block can be removed.
{
throw;
}