C# 在C语言中处理异常的良好实践#

C# 在C语言中处理异常的良好实践#,c#,exception,exception-handling,C#,Exception,Exception Handling,我在《实用程序员》(Practical Programmer)和其他一些文章(包括Joel Spolsky的一篇)中读到过,您应该只在异常情况下抛出异常。否则,您应该返回一个错误 有时这是可能的(例如,返回-1、-0、或正数),但在其他情况下这是不可能的。我的意思是,如果您返回一个类,您总是可以返回null,但我认为这样做的目的是返回一些东西,提醒调用方发生了什么 如果我总是返回null,我认为这样说是没有用的:如果这个方法返回null,可能是因为A、B、C、D或E 那么,如何在C#中实现这一点

我在《实用程序员》(Practical Programmer)和其他一些文章(包括Joel Spolsky的一篇)中读到过,您应该只在异常情况下抛出异常。否则,您应该返回一个错误

有时这是可能的(例如,返回
-1
-0
、或
正数),但在其他情况下这是不可能的。我的意思是,如果您返回一个类,您总是可以返回
null
,但我认为这样做的目的是返回一些东西,提醒调用方发生了什么

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

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

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

我明白这是我在这里要求的另一种方式。以下是链接:


通过使用数字,除了例外情况,您直接与Microsoft的建议相矛盾。我所发现的能解开整个主题谜团的最好的信息来源是。这本书也值得一读,因为它有关于例外情况的材料

:

不要返回错误代码。异常是框架中报告错误的主要手段。


您可以采用的一种解决方案是一个
out
参数,它可以得到结果。然后,您的方法返回一个<代码> BOOL ,非常类似于“代码> > TyPARSE < /Calp>您在框架中遇到的方法。

< P>一个要考虑的例子是It.TyPARSE < /C>。它使用
out
参数作为解析值,使用
bool
返回值指示成功或失败。如果情况需要,可以用枚举或更复杂的对象替换
bool
。(例如,数据验证可能会以某种方式失败,而这种方式确实会导致一系列失败等。)

NET 4的另一个替代方法是
Tuple
。。。因此
int.TryParse
可能是:

public static Tuple<int, bool> TryParse(string text)
公共静态元组TryParse(字符串文本)
下一种可能性是让一个对象封装整个结果,包括故障模式(如果合适)。例如,您可以询问任务的结果、状态以及失败时的异常


所有这一切都只适用于这样的情况,如果这不是一个真正的错误。它并不表示有bug,只是有点像用户输入错误。我真的不喜欢返回错误代码-异常在.NET中更为惯用。

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

我建议您遵循这些指导原则,因为它是.NET的标准,您与其他.NET开发人员之间的冲突会少得多

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


我还意识到,不同的平台在如何看待正确的错误处理方面存在差异。这可能是一个主观的问题,但我会坚持我上面所说的——当在.NET中按照.NET指南进行开发时,

您可能会考虑遵循Tryxxx < /Cult>模式,用两个简单的重载为客户端考虑。

// Exception
public void Connect(Options o); 

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

只有当我确信代码将被其他开发人员使用时不会产生误解时,我才会返回诸如
null
引用或负索引值之类的值。类似LINQ函数的东西
IEnumerable.FirstOrDefault
IEnumerable.First
会为空集合引发异常,因为预计它将返回第一个元素,而空集合是一种例外情况

关于何时引发异常,更好的规则是:

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

空值可用于指示您请求的内容不存在。对于任何其他错误情况,都不应返回它


“仅在异常情况下抛出异常”的规则在国际海事组织(IMO)中是无用的,因为它没有提供任何基准来指示哪些是异常,哪些不是异常。这就像说“只吃可食用的食物。”

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

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

然后,假设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错误发生的频率有多高?)
调用方应该在调用该方法之前进行检查,以确保事情顺利进行(例如,检查文件是否存在。如果文件不存在,请不要尝试打开它)

强制执行您的方法的契约:

  • 先决条件。调用方保证在调用方法之前这些条件为真
  • 后置条件。被调用方保证在方法调用后这些条件为真
  • 不变条件。被调用方保证这些条件始终是tru