Exception 异常处理:契约与异常方法

Exception 异常处理:契约与异常方法,exception,Exception,我知道两种异常处理方法,让我们看看它们 合同方法 当一个方法没有执行它在方法头中所说的操作时,它将抛出一个异常。因此,该方法“承诺”它将执行该操作,如果由于某种原因失败,它将抛出异常 例外做法 只有在真正奇怪的事情发生时才会抛出异常。当可以使用正常控制流(If语句)解决这种情况时,不应使用异常。您不会像在契约方法中那样,对控制流使用异常 让我们在不同的情况下使用这两种方法: 我们有一个Customer类,它有一个名为OrderProduct的方法 合同方法: class Customer {

我知道两种异常处理方法,让我们看看它们

  • 合同方法

    当一个方法没有执行它在方法头中所说的操作时,它将抛出一个异常。因此,该方法“承诺”它将执行该操作,如果由于某种原因失败,它将抛出异常

  • 例外做法

    只有在真正奇怪的事情发生时才会抛出异常。当可以使用正常控制流(If语句)解决这种情况时,不应使用异常。您不会像在契约方法中那样,对控制流使用异常

  • 让我们在不同的情况下使用这两种方法:

    我们有一个Customer类,它有一个名为OrderProduct的方法

    合同方法:

    class Customer
    {
         public void OrderProduct(Product product)
         {
               if((m_credit - product.Price) < 0)
                      throw new NoCreditException("Not enough credit!");
               // do stuff 
         }
    }
    
    class Customer
    {
         public bool OrderProduct(Product product)
         {
              if((m_credit - product.Price) < 0)
                       return false;
              // do stuff
              return true;
         }
    }
    
    if !(customer.OrderProduct(product))
                Console.WriteLine("Not enough credit!");
    else
       // go on with your life
    
    当我调用一个名为CreateCar的方法时,我希望得到一个Car实例,而不是一个糟糕的空指针,它会在十几行之后破坏我的运行代码。因此,与此相比,我更喜欢合同:

    class CarController
    {
         
         public Car CreateCar(string model)
         {
             // something went wrong, wrong model
             throw new CarModelNotKnownException("Model unkown");
    
             return new Car();
         }
     }
    

    你用哪种款式?您认为什么是处理异常的最佳通用方法?

    我认为,如果您正在构建一个将由外部程序使用(或将由其他程序重用)的类,那么您应该使用契约方法。这方面的一个很好的例子是任何类型的API。

    我通常的方法是使用契约来处理由于“客户端”调用而导致的任何类型的错误,也就是说,由于外部错误(即ArgumentNullException)

    参数上的每个错误都不会被处理。出现异常,由“客户”负责处理。另一方面,对于内部错误,请始终尝试更正它们(好像由于某种原因无法获得数据库连接),并且只有在无法处理时才重新引发异常


    重要的是要记住,这种级别的大多数未处理异常无论如何都无法由客户机处理,因此它们可能会转到最通用的异常处理程序,因此,如果发生这种异常,您可能是FUBAR。

    我赞成您称之为“契约”的方法。在支持异常的语言中,不需要返回null或其他特殊值来指示错误。我发现,如果代码中没有一堆“if(result==NULL)”或“if(result==1)”子句与非常简单、直接的逻辑混合在一起,那么理解代码就容易多了。

    如果您确实对异常感兴趣,并且想考虑如何使用它们构建健壮的系统,考虑阅读。

    < P>这两种方法都是正确的。这意味着,合同应该以这样一种方式编写,即为所有并非真正异常的情况指定一种不需要抛出异常的行为

    请注意,根据代码调用方的期望,某些情况可能是异常的,也可能不是异常的。如果调用者期望字典将包含某个项,并且缺少该项表示存在严重问题,则找不到该项是一种异常情况,应导致引发异常。但是,如果调用方不知道某个项是否存在,并且同样准备好处理该项的存在或不存在,则该项的存在将是一种预期情况,并且不应导致异常。处理调用方期望值变化的最佳方法是让契约指定两种方法:DoSomething方法和TryDoSomething方法,例如

    TValue GetValue(TKey Key); bool TryGetValue(TKey Key, ref TValue value); TValue GetValue(TKey); 布尔TryGetValue(TKey键,参考TValue值); 请注意,虽然标准的“try”模式如上所示,但如果要设计一个生成项的接口,一些备选方案可能也会有所帮助:

    // In case of failure, set ok false and return default<TValue>. TValue TryGetResult(ref bool ok, TParam param); // In case of failure, indicate particular problem in GetKeyErrorInfo // and return default<TValue>. TValue TryGetResult(ref GetKeyErrorInfo errorInfo, ref TParam param); //如果失败,将ok设置为false并返回默认值。 TValue TryGetResult(参考布尔正常,TParam参数); //如果出现故障,请在GetKeyErrorInfo中指出特定问题 //并返回默认值。 TValue TryGetResult(参考GetKeyErrorInfo errorInfo,参考TParam参数); 请注意,在接口中使用类似于正常TryGetResult模式的内容将使接口相对于结果类型保持不变;使用上述模式之一将允许接口相对于结果类型是协变的。此外,它将允许在“var”声明中使用结果:

    var myThingResult = myThing.TryGetSomeValue(ref ok, whatever); if (ok) { do_whatever } var myThingResult=myThing.TryGetSomeValue(参考ok,随便什么); 如果(ok){做任何事}
    不完全是标准方法,但在某些情况下,优点可能证明它是正确的。

    +1以返回null或-1结果为例。null或-1返回并没有使代码更清晰,而是强制调用方法的编写者知道被调用方法如何选择报告非标准结果。例外情况是一种更干净的契约方法,在这种方法中,调用方可以将被调用的对象视为(可能)不断变化的黑盒。调用程序必须知道它必须处理一个异常,而不是今天为null,明天为Integer.MIN_。 var myThingResult = myThing.TryGetSomeValue(ref ok, whatever); if (ok) { do_whatever }