c#中处理异常的良好实践
本文关键字:处理 异常 | 更新日期: 2023-09-27 17:55:05
我在The Pragmatic Programmer和其他一些文章(包括Joel Spolsky的一篇文章)中读到,您应该只在异常情况下抛出异常。否则,您应该返回一个错误。
有时是可能的(例如返回-1
, -0
或positive number
),但在其他情况下是不可能的。我的意思是,如果你要返回一个类,你总是可以返回null
,但是我认为这个想法是返回一些东西来提醒调用者发生了什么。
如果我总是返回null
,我不认为这是有用的说:如果这个方法返回null,它可能是因为A, B, C, D,或E
那么,这究竟如何在c#中实现呢?
编辑:
在我发布这个问题的几个小时后,我在这里看到了另一个问题,这个问题本身是关于发布的代码是否是一个好的实践。
我明白这是我在这里问的另一种方式。这是链接:
通用属性缺点?
一个更好的何时抛出异常的规则是:
当你的方法不能做它的名字所说的事情时抛出异常。
空值可以用来表示您请求的东西不存在。对于任何其他错误情况都不应该返回。
在我看来,"只在异常情况下抛出异常"的规则毫无帮助,因为它没有给你提供基准来指示什么是异常,什么不是异常。这就像说"只吃可食用的食物。"通过使用数字,您直接与Microsoft对例外的建议相矛盾。我找到的最好的信息来源是Jeffrey Richter的CLR via c# 3。此外,. net框架指南书中关于异常的内容也值得一读。
引用MSDN:
不返回错误码。异常是框架中报告错误的主要手段。
您可以采用的一种解决方案是out
参数,它可以获得结果。然后,您的方法返回一个bool
,就像您在框架中遇到的许多TryParse
方法一样。
要考虑的一个例子是int.TryParse
。它使用out
参数作为解析值,并使用bool
返回值来指示成功或失败。如果情况需要,可以将bool
替换为enum或更复杂的对象。(例如,数据验证失败的方式可能会导致一系列失败等)
.NET 4的另一个选择是Tuple
…所以int.TryParse
可以是:
public static Tuple<int, bool> TryParse(string text)
下一种可能是用一个对象封装整个结果,如果合适的话,包括失败模式。例如,您可以询问Task<T>
的结果、状态以及失败时的异常。
所有这些都只适用于不发生错误的情况。这并不表示有bug,只是用户输入错误之类的东西。我真的不喜欢返回错误代码——异常在。net中更加习惯。
Microsoft已经记录了。net中错误处理的最佳实践
http://msdn.microsoft.com/en-us/library/8ey5ey87 (VS.71) . aspx
我建议遵循这些指导方针,因为这是。net的标准,您将与其他。net开发人员发生更少的冲突。
(注意,我意识到我发的是一个较旧的链接,但建议仍然存在)
我也意识到不同的平台在如何看待正确的错误处理方面是不同的。这可能是一个主观的问题,但我将坚持我上面所说的——在。net开发时遵循。net指导方针。
您可能需要考虑遵循TryXXX
模式,并为客户端考虑几个简单的重载。
// Exception
public void Connect(Options o);
// Error Code
public bool TryConnect(Options o, out Error e);
Joel Spolsky错了。通过错误/返回码返回状态意味着你不能相信任何方法调用的结果—必须对返回值进行测试和处理。
这意味着返回这样一个值的每个方法调用至少引入一个选择点(或多个,取决于返回值的域),从而通过代码引入更多可能的执行路径。所有这些都必须经过测试。
那么,假设Method1()和Method2()的契约要么成功,要么抛出异常,这段代码中可能有1个流通过:
foo.Method(...) ;
bar.Method(...) ;
如果这些方法通过返回代码来指示状态,那么很快就会变得非常混乱。只是返回一个二进制值:
bool fooSuccess = foo.Method(...);
if ( fooSuccess )
{
bool barSuccess = bar.Method(...);
if ( barSuccess )
{
// The normal course of events -- do the usual thing
}
else
{
// deal with bar.Method() failure
}
}
else // foo.Method() failed
{
// deal with foo.Method() failure
}
返回状态码而不是抛出异常
- 复杂测试
- 使理解代码变得复杂
- 几乎肯定会引入bug,因为开发人员不会捕获和测试所有可能的情况(毕竟,您实际看到I/O错误发生的频率有多高?)
调用者应该在调用方法之前检查以确保一切正常(例如,检查文件是否存在)。如果文件不存在,不要尝试打开它)。
执行方法的契约:
- 先决条件。调用方保证在方法调用之前这些都为真。
- 后置条件。 calllee保证这些在方法调用后为真。
- 不变的条件。 Callee保证这些都是正确的。
如果违反契约则抛出异常。如果发生任何意外,则抛出异常。
你的代码应该是一个严格的纪律。
只有当我确信代码将被其他开发人员使用时不会产生误解时,我才返回null
引用或负索引值。类似于LINQ函数IEnumerable<T>.FirstOrDefault
。IEnumerable<T>.First
对空集合抛出异常,因为预计它将返回第一个元素,而空集合是一个例外情况