使用流量控制的例外 [英] Using exceptions for flow control

查看:146
本文介绍了使用流量控制的例外的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近被告知,我正在滥用例外来控制我的应用程序的流程,所以我试图以某种方式澄清这种情况。



对我来说头脑中,一个方法应该会引发一个异常,当它遇到一个不能在内部处理的情况,或者可能由主叫方更好地处理。






所以 - 存在任何特定的规则集,可用于在开发应用程序时回答以下问题集: p>


  • 什么时候我应该抛出异常,什么时候应该用强不耐心保证编写代码, em>简单地返回 bool 表示成功或失败?


  • 最小化情况的数量,当方法引发异常时,或者恰恰相反,应该如何最大限度地提供处理这些情况的灵活性?


  • 坚持由开发我的应用程序时使用的框架/运行时设置的异常抛出约定,或者我应该包装所有这些调用,以便它们匹配我自己的异常抛出策略?


  • 我也建议使用错误代码进行错误处理,这似乎非常有效,但从句法角度来看也是丑陋的开发人员失去了为方法指定输出的能力)。你觉得怎么样?







示例第三个问题(我使用的是I / O框架,遇到以下情况):


所描述的框架不使用异常来处理错误,但是
其他代码使用它们。我应该用'?'表示的每个可能的失败
,并在这种情况下抛出异常?
或者我应该将我的方法的签名更改为 bool
PrepareTheResultingOutputPath
,只显示操作是否成功



  public void PrepareTheResultingOutputFile(
String templateFilePath,String outputFilePath)
{
if(!File.Exists(templateFilePath))
// ???

if(!Directory.MakePath(outputFilePath))
// ???

if(File.Exists(outputFilePath))
if(!File.Remove(outputFilePath))
// ???

if(!File.Copy(templateFilePath,outputFilePath)
// ???
}






另一个示例 - 甚至 .NET Framework 没有遵循一些严格的异常抛出策略,有些方法被记录为抛出10种不同的异常类型,其中包括简单的异常类型,如 NullArgumentException ,但是其中一些只返回 bool 表示操作成功或失败。



谢谢!

解决方案

异常的问题是它们本质上是荣耀的gotos,有能力解开程序的调用堆栈,所以,如果你是使用例外流程控制,你可能将它们用作gotos而不是作为异常条件的指示,这正是异常的一个原因,以及它们的名称的原因:它们应该仅在 exceptio中使用。所以,除非一个方法被设计为不抛出异常(一个例子是.NET的 int.TryParse ),可以为特殊情况引发异常。 p>

C#而不是Java的好处在于,在C#中,您可以通过返回一个元组类型或使用out参数实质返回两个或多个值。所以,返回一个错误代码作为方法的主要返回值没有太多丑,因为你可以使用其余的参数。例如,调用 int.TryParse 的常用范例是

  string s = / *从某个地方读一个字符串* /; 
int n;
if(int.TryParse(s,out n))
{
//使用n某种方式
}
else
{
/ /告诉用户他们输入错误的号码
}

现在您的第三个问题,这似乎是最实质的。参考您的示例代码,您会询问是否应该返回 bool 以指示成功/失败,或者您应该使用异常来指示失败。但是有第三个选择。您可以定义枚举来告诉方法如何失败,并将该类型的值返回给调用者。然后,调用者有一个很广泛的选择:调用者不必使用一堆try / catch语句,或者如果这样很少了解方法失败,但可以选择写入

  if(PrepareTheResultingOutputFile(templateFilePath,outputFilePath)== Status.Success)
//做某事
else
/ /失败了!

  switch(PrepareTheResultingOutputFile(templateFilePath,outputFilePath))
{
case Status.Success:
//做某事
break;
case Status.FileNotPresent:
//执行其他操作
break;
case Status.CannotMakePath:
//执行其他操作
break;
//等等
默认值:
//其他一些失败的原因
break;
}

您可以在这个问题上找到更多内容其中,但特别是在中的throwing-exceptions-to-control-flow-code-smell Joel Spolsky的帖子,我非常推荐。


I was recently told that I'm abusing exceptions to control the flow in my applications, so I this is my attempt to somehow clarify the situation.

To my mind, a method should throw an exception, when it encounters a situation, which can't be handled internally or might be handled better by the calling side.


So - does any particular set of rules exist, which can be used to answer the following set of question when developing your applications:

  • When should I throw an exception and when should I write code with strong nothrow guarantee, which might simply return bool to indicate success or failure?

  • Should I try to minimize the number of situations, when the method throws an exception or , on the contrary, should it be maximized to provide flexibility when handling these situations?

  • Should I stick to the exception throwing convention set by the frameworks / runtimes I use when developing my applications or should I wrap all these calls so that they match my own exception throwing strategy?

  • I was also adviced to use error codes for error handling, which seems pretty efficient, but ugly from the syntactical point of view (also, when using them a developer loses the ability to specify the output for a method). What do you think about this?


Example for the third question (I was using an I/O framework and encountered the following situation):

The described framework does not use exceptions to handle errors, but the other code does use them. Should I wrap every possible failure indicated with '???' and throw an exception in this case? Or should I change the signature of my method to bool PrepareTheResultingOutputPath and only indicate whether the operation was successful or not?

public void PrepareTheResultingOutputFile(
    String templateFilePath, String outputFilePath)
{
    if (!File.Exists(templateFilePath))
        // ???

    if (!Directory.MakePath(outputFilePath))
        // ???

    if (File.Exists(outputFilePath))
        if (!File.Remove(outputFilePath))
            // ???

    if (!File.Copy(templateFilePath, outputFilePath)
        // ???
}


Another example - even the .NET Framework doesn't follow some strict exception throwing strategy. Some methods are documented to throw 10+ different exception types, including trivial exception types like NullArgumentException, but some of them simply return bool to indicate success or failure of the operations.

Thank you!

解决方案

The problem with exceptions is that they are essentially glorified gotos that have the ability to unwind the program's call stack. So, if you are "using exceptions for flow control," you are probably using them as gotos rather than as indications of an exceptions condition. That's exactly the point of exceptions, and the reason for their name: they are supposed to be used only in exceptional cases. So, unless a method is designed not to throw an exception (an example is .NET's int.TryParse), it's OK to throw an exception in response to exceptional circumstances.

The nice thing about C# as opposed to Java is that in C# you can essentially return two or more values, by returning a tuple type or by using out parameters. So, there isn't much ugliness in returning an error code as the method's main return value, since you can use out parameters for the rest. For example, the common paradigm for calling int.TryParse is

string s = /* Read a string from somewhere */;
int n;
if (int.TryParse(s, out n))
{
    // Use n somehow
}
else
{
    // Tell the user that they entered a wrong number
}

Now for your third question, which seems to be the most substantial. In reference to your example code, you ask if you should return bool to indicate success/failure or if you should use exceptions to indicate failure. There is a third option, though. You can define an enum to tell how the method could fail, and return a value of that type to the caller. Then, the caller has a wide choice: the caller doesn't have to use a bunch of try/catch statements, or an if that gives little insight into how the method failed, but can choose to write either

if (PrepareTheResultingOutputFile(templateFilePath, outputFilePath) == Status.Success)
    // Do  something
else
    // It failed!

or

switch (PrepareTheResultingOutputFile(templateFilePath, outputFilePath))
{
    case Status.Success:
        // Do something
        break;
    case Status.FileNotPresent:
        // Do something else
        break;
    case Status.CannotMakePath:
        // Do something else
        break;
    // And so on
    default:
        // Some other reason for failure
        break;
}

You can find more on this issue here and here, but especially in Joel Spolsky's post, which I highly recommend.

这篇关于使用流量控制的例外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆