c#中处理异常的良好实践

本文关键字:处理 异常 | 更新日期: 2023-09-27 17:55:05

我在The Pragmatic Programmer和其他一些文章(包括Joel Spolsky的一篇文章)中读到,您应该只在异常情况下抛出异常。否则,您应该返回一个错误。

有时是可能的(例如返回-1, -0positive number),但在其他情况下是不可能的。我的意思是,如果你要返回一个类,你总是可以返回null,但是我认为这个想法是返回一些东西来提醒调用者发生了什么。

如果我总是返回null,我不认为这是有用的说:如果这个方法返回null,它可能是因为A, B, C, D,或E

那么,这究竟如何在c#中实现呢?

编辑:


在我发布这个问题的几个小时后,我在这里看到了另一个问题,这个问题本身是关于发布的代码是否是一个好的实践。

我明白这是我在这里问的另一种方式。这是链接:

通用属性缺点?

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>.FirstOrDefaultIEnumerable<T>.First对空集合抛出异常,因为预计它将返回第一个元素,而空集合是一个例外情况