在C#中处理异常时的良好做法

时间:2011-05-06 21:52:02

标签: c# exception exception-handling

我在The Pragmatic Programmer和其他一些文章(包括来自Joel Spolsky的文章)中读过,你应该只在例外案例中抛出异常。否则,您应该返回错误。

有时可能(例如,返回-1-0positive number),但在其他情况下,这是不可能的。我的意思是,如果你要返回一个班级,你总是可以返回null,但我认为这个想法是返回一些能够提醒来电者发生的事情。

如果我总是返回null,我认为说:如果此方法返回null,则可能是因为A,B,C,D或E

那么,如何在C#中实现这一目标呢?

修改
在我发布这个问题后的几个小时,我在这里看到了另一个问题,问题本身就是关于发布的代码是不是一个好习惯。

我看到这是我在这里做的事情的另一种方式。这是链接:

Generic property disadvantages?

7 个答案:

答案 0 :(得分:12)

何时抛出异常的更好的规则是:

当您的方法无法执行其名称所表示的操作时抛出异常。

空值可用于表示您已经要求不存在的内容。任何其他错误情况都不应该返回。

规则“只在特殊情况下抛出异常”是无助的IMO,因为它没有给你基准来表明什么是异常和什么不是。这就像说“只吃可食用的食物。”

答案 1 :(得分:10)

通过使用数字,您直接与Microsoft推荐的Exceptions相矛盾。我发现,使整个主题神秘化的最佳信息来源是Jeffrey Richter's CLR via C# 3。另外,.NET Framework Guidelines本书值得阅读其关于例外的材料。

To quote MSDN

  

不要返回错误代码。例外是在框架中报告错误的主要方法。

您可以采用的一个解决方案是out参数,它可以获得结果。然后,您的方法返回bool,就像您在框架中遇到的很多TryParse方法一样。

答案 2 :(得分:8)

要考虑的一个例子是int.TryParse。它对解析的值使用out参数,并使用bool返回值表示成功或失败。如果情况合适,bool可以用枚举或更复杂的对象替换。 (例如,数据验证可能会以真正保证收集故障的方式失败等。)

使用.NET 4的替代方法是Tuple ...所以int.TryParse 可以

public static Tuple<int, bool> TryParse(string text)

下一种可能性是让对象封装整个结果,包括适当的失败模式。例如,如果失败,您可以向Task<T>询问结果,状态和异常。

所有这些只适用于发生这种情况的错误。它并不表示错误,只是用户输入错误。我真的不喜欢喜欢返回错误代码 - 在.NET中,异常更加惯用。

答案 3 :(得分:1)

Microsoft已经记录了.NET中错误处理的最佳实践

http://msdn.microsoft.com/en-us/library/8ey5ey87(VS.71).aspx

我建议遵循这些准则,因为它是.NET的标准,与其他.NET开发人员的冲突要少得多。

(注意,我意识到我发布了一个较旧的链接,但建议仍然存在。)

我也意识到不同的平台在看待正确的错误处理方式上有所不同。这可能是一个主观问题,但我会坚持上面所说的 - 在.NET中开发时遵循.NET准则。

答案 4 :(得分:1)

您可能需要考虑遵循TryXXX模式,并允许客户端考虑几个简单的重载。

// Exception
public void Connect(Options o); 

// Error Code
public bool TryConnect(Options o, out Error e); 

答案 5 :(得分:1)

乔尔斯波尔斯基错了。通过错误/返回代码返回状态意味着您不能信任任何方法调用的结果 - 必须测试和处理返回的值。

这意味着返回此类值的每个方法调用都会引入至少一个选择点(或更多,取决于返回值的域),从而引入更多可能的代码执行路径。所有这些都必须经过测试。

然后,假设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
}

返回状态代码而不是抛出异常

  • 使测试复杂化
  • 使对代码的理解变得复杂
  • 几乎肯定会引入错误,因为开发人员不会捕获并测试每个可能的情况(毕竟,您实际看到I / O错误的频率是多少?)。

调用者应该在调用方法之前检查以确保事情是重要的(例如,检查文件是否存在。如果文件不存在,请不要尝试打开它。)

执行您方法的合同:

  • 前提条件。调用者保证在方法调用之前这些都是正确的。
  • 后置条件。 Callee保证在调用方法后这些都是正确的。
  • 不变条件。 Callee保证这些都是真的。

如果违反合同,则抛出异常。 如果发生任何意外情况,则抛出异常。

你的代码应该是严格的纪律。

答案 6 :(得分:0)

只有当我确定在其他开发人员使用代码时不会产生误解时,我才会返回null引用或负索引值等值。像LINQ函数IEnumerable<T>.FirstOrDefault之类的东西。 IEnumerable<T>.First会为空集合抛出异常,因为它会返回第一个元素,而空集合是一个例外情况