在exception和返回值之间进行选择

我创建了一个从文件中解析一些文档的类。

class Parser { public Parse(string fileName) { /// } } 

有时可能会出现解析错误,解析器必须返回某些数据。 我为此创建了特殊课程。

 class ParsingError { // some data } 

我怎样才能正确处理这些错误。 我至少有两个选择:

创建我自己的exception或返回值。

方案一

 myParser.Parse(fileName, out error); 

方案二

 try { myParser.Parse(fileName) } catch(MyParsingException ex) { // use ex.Error field } 

UPDATE

如果我没有弄错,那么例外背后的意识形态就是它应该处理一些特殊的东西,这是该方法无意处理的一些情况。

这让我想知道是否例如:

解析器在文件中找到未知字段,或者编码错误

这会被视为特殊情况吗?

想想解决错误的典型程度。 如果这是典型的 – 支持返回值。 exception应该用于不正常的事情。

.NET land的exception理念与其他平台略有不同。 在.NET领域不需要很长时间才能意识到你不再在OZ中,并且经常会抛出exception。

从.NET 4上的MSDN:不要返回错误代码。 例外是在框架中报告错误的主要方法。

来自.NET 4.5上的MSDN:exception是执行程序遇到的任何错误情况或意外行为。

他们给出的一个例子是客户端无法连接到端点。 因此,如果您认为有时网站不可用于网络或其他原因,这不符合传统的“例外”定义,您可以了解他们的创作者如何打算使用例外。

我会选择三

 ParseResult result = myParser.Parse(filename) 

这里ParseResult可以提供有关解析结果的更多细节,包括正面和负面。

如果失败情况非常罕见并且被认为是意外结果,那么我会选择你的选项2并抛出exception。 当然,最终的解决方案可能是组合,其中在ParseResult中返回常见错误,但是例如未能打开文件等exception情况表现为exception。

这取决于MyParsingException是否只是来自System.Exeption的派生类,或者它包含有关尝试解析文件时出错的其他信息。 如果没有这样的信息,那么我认为Parse方法应该返回一个字符串,如果发生错误则返回null:

  public string Parse(string fileName) { string res = null; try { /// parse the file, assign parse result to res } catch { res = null; } return res; } 

或者,如果该例程确实有用,则包含有关文件(名称),行号等的信息:

 public string Parse(string fileName) { string res = null; int errorLine = -1; try { // foreach line of text to parse -- errorLine = lineNumber; /// parse the file, assign parse result to res } catch (Exeption ex) { MyParsingException myEx= new MyParsingException ("parsing error", ex); myEx.Data["fileName"] = fileName; myEx.data["lineNumber"] = errorLine ; throw myEx; } return res; } // then you can have some really useful info: try { myParser.Parse(fileName) } catch(MyParsingException ex) { // use ex.Data to get the "fileName" and "lineNumber" properties. } 

直接调用者要处理无效条件的情况下,错误标志或返回值通常更好。 如果有许多函数调用可能以特定方式失败,并且所有这些函数调用的error handling应该相同,则exception通常会更好。

通常,方法的作者不能知道哪个场景将适用于其调用者(事实上,一些调用者更常适合一种模式而另一种模式更常见)。 Microsoft处理此问题的首选方法是使用以下模式:

 Thing GetThing(int param1, string param2); bool TryGetThing(int param1, string param2, out Thing result); 

这肯定是比限制调用者适合一种模式或另一种模式更好的概念,尽管我不喜欢特定的实现。 Microsoft明确建议使用单独的方法,而不是使用一个带有参数的方法来指示失败是否应该抛出exception,但这种理念是有代价的:这意味着如果GetThingTryGetThing其中一个步骤可以用“do”或“try”方法, GetThingTryGetThing的代码必须在很大程度上重复,一个调用“do”方法,另一个调用“try”方法。

另一种方法是使用委托或接口类型对象,该对象指示发生exception时“try”方法应该执行的操作。 例如,一个人可以有一个function:

 Thing TryGetThing(int param1, string param2, bool ThrowOnError, out ErrorInfo errInf); 

可能有一个重载,如果省略最后两个参数,将为ThrowOnError调用上面的trueThrowOnError调用一个虚拟变量。 如果TryGetThing使用的方法遵循类似的模式,则可以调用它们而不必为try / do情况重复代码。

如果你的API说它处理解析错误,那么这不是一个例外的情况,也许应该返回解析错误。 对于其他东西,比如丢失文件,锁定文件,无效输入(除了狡猾的解析),你应该抛出exception。

对我来说,这取决于我计划如何消耗错误以及它是什么样的错误。 该方法的背景也起了作用。 如果预期该方法每隔一段时间就会在xml中出现错误,那么它在我眼中并不是一个例外,应该相应地对待。

假设我正在编写XMLvalidation器,然后返回错误是要走的路。 如果我正在编写XML配置解析器,则exception似乎更合适。