我会试着问我的问题,这样它就不会以一个简单的争论线索结束。
我最近跳到了一个用C#编码的应用程序中,发现了异常机制。我和他们有过一些不好的经历,比如下面的
// _sValue is a string
try
{
return float.Parse(_sValue);
}
catch
{
return 0;
}
我把它改成:
float l_fParsedValue = 0.0f;
if (float.TryParse(_sValue, out l_fParsedValue))
{
return l_fParsedValue;
}
else
{
return 0;
}
结果,我在Visual Studio中的输出不再像那样充斥着消息
第一次机会System.FormatException blabla
当类似"-"的字符串出现在代码段中时。我认为使用第二个片段更干净。
更进一步,我经常看到异常被经常使用,比如:"我在这个try-catch中想做什么就做什么,如果出了问题,就抓。"。
现在,为了不陷入糟糕的误解,我希望你们能帮助我清楚地定义如何/何时使用这些例外,以及何时坚持老派的"如果……其他"。
提前感谢您的帮助!
在特殊情况下应该抛出异常。也就是说,当一些意想不到的事情发生时。如果您期望一个函数定期抛出异常,那么很可能是糟糕的设计。
在您的示例中,很明显TryParse
更好,因为异常似乎经常发生。
但例如,当解析一个文件时,我希望它几乎总是有效的。所以我通常使用Parse
来捕获异常,并将捕获的异常作为内部异常生成InvalidDataException
。通常会大大简化解析代码,即使它可能是糟糕的风格。
我推荐Eric Lippers的博客条目:Vexing exceptions
在Parse()
/TryParse()
的情况下,最好不要等待异常,使用TryParse并自行处理错误输入。
异常应用于异常行为,而不是流控制。一个基本的指导原则是,如果正常的程序流经常遇到异常,你就做错了什么。
但是,需要注意的是,仅存在try { } catch { }
本身不会对性能产生负面影响。只有当实际抛出异常并且需要计算堆栈跟踪时,您才会看到(在某些情况下相当严重)性能下降。
到目前为止还没有深入研究的另一点是,异常是有代价的。它们破坏了程序中的正常控制流程,因此会占用一些资源。
一个简单的测试是编写一个在原始浮点上循环的程序。分析包含一些无效数据的代码,并将运行所需的时间与TryParse版本进行比较——会有一个小但明显的差异。
在做出关于异常的决定时,我脑海中挥之不去的一个片段来自本文:
几乎规则#1
在决定是否应该投掷例外,假装投掷语句使计算机发出嘟嘟声3次,并睡眠2秒。如果你还想扔到下面情况,争取吧。
将异常作为一部分的程序他们的正常处理受到所有可读性和经典的可维护性问题意大利面条代码。
--Andy Hunt和Dave Thomas
关于如何/何时使用异常,我认为没有简单的正确答案。这取决于您正在处理的应用程序的体系结构和其他因素。
我可以建议你阅读第8.3章。错误处理技术和8.4。代码全书的例外情况。
啊,如果就这么简单就好了!但是,唉,决定何时使用例外情况往往是主观的。尽管如此,还是有一些指导原则可以使用。例如,微软有一些。
总而言之,抛出异常的经验法则是-只有当函数不能执行它应该执行的操作时,才抛出异常。基本上,每个函数都有一个契约——它有一个合法的输入值范围和一个合法范围的输出值范围。当输入值无效,或者无法提供预期的输出值时,应该抛出异常。
请注意,这里有一个棘手的问题——(用户输入的)验证错误是否也应该作为异常抛出?有些学派说不(包括微软),有些学派说是。你的电话。每种方法都有其优点和缺点,如何构建代码取决于您自己。
捕获异常的经验法则是-您应该只捕获可以处理的异常。现在,这也很滑。向用户显示错误消息是否也在"处理"它?但如果是臭名昭著的StackOverflowException
或OutOfMemoryException
呢?那时你几乎什么都看不出来。这些可能不是唯一会使整个系统处于不可用状态的例外情况。再说一遍,你的电话。