在异常和返回值之间进行选择

时间:2011-03-28 14:01:41

标签: c# architecture exception-handling

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

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

在解析器具有的情况下,有时会出现解析错误 返回某些数据。我为此创建了特殊课程。

class ParsingError
{
 // some data
}

我如何正确处理此类错误。我至少有两个选择:

创建我自己的异常或返回值。

选项一

myParser.Parse(fileName, out error);

选项二

try
{
  myParser.Parse(fileName)
}

catch(MyParsingException ex)
{
  // use ex.Error field
}

更新

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

这让我想知道是否例如:

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

会被视为例外吗?

7 个答案:

答案 0 :(得分:14)

想一想解析错误的典型程度。如果它是典型的 - 支持返回值。异常应该用于不正常的事情。

答案 1 :(得分:5)

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

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

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

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

答案 2 :(得分:3)

我会选择三个

ParseResult result = myParser.Parse(filename)

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

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

答案 3 :(得分:2)

这取决于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;
 }

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

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.
}

答案 4 :(得分:2)

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

方法的作者通常无法知道哪个场景将适用于其调用者(实际上,某些调用者通常更适合一种模式而另一种模式更适合其他模式)。 Microsoft首选的处理方式是使用模式:

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

这肯定是一个比限制调用者适合一种模式或另一种模式更好的概念,尽管我不喜欢特定的实现。 Microsoft明确建议使用单独的方法,而不是使用一个带有参数的方法来指示失败是否应该抛出异常,但这种理念是有代价的:它意味着如果GetThing或{{TryGetThing之一的步骤之一1}}可以使用“do”或“try”方法完成,GetThingTryGetThing的代码必须大部分重复,一个调用“do”方法,另一个调用“尝试“方法。

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

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

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

答案 5 :(得分:1)

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

答案 6 :(得分:0)

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

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