Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Language agnostic 您更喜欢例外还是返回码,以及为什么?_Language Agnostic_Exception - Fatal编程技术网

Language agnostic 您更喜欢例外还是返回码,以及为什么?

Language agnostic 您更喜欢例外还是返回码,以及为什么?,language-agnostic,exception,Language Agnostic,Exception,我的问题是,对于错误处理、异常或错误返回代码,大多数开发人员更喜欢什么。请注意语言(或语系)的具体情况,以及为什么您更喜欢其中一种语言 我问这个是出于好奇。就我个人而言,我更喜欢错误返回代码,因为它们的爆炸性较小,如果用户不愿意,也不会强制用户代码支付异常性能惩罚 更新:谢谢所有的答案!我必须说,尽管我不喜欢异常情况下代码流的不可预测性。关于返回码(以及它们的哥哥句柄)的答案确实给代码增加了很多噪音。我实际上使用了这两种方法 如果是已知的、可能的错误,我会使用返回码。如果这是一个我知道可以并且将

我的问题是,对于错误处理、异常或错误返回代码,大多数开发人员更喜欢什么。请注意语言(或语系)的具体情况,以及为什么您更喜欢其中一种语言

我问这个是出于好奇。就我个人而言,我更喜欢错误返回代码,因为它们的爆炸性较小,如果用户不愿意,也不会强制用户代码支付异常性能惩罚

更新:谢谢所有的答案!我必须说,尽管我不喜欢异常情况下代码流的不可预测性。关于返回码(以及它们的哥哥句柄)的答案确实给代码增加了很多噪音。

我实际上使用了这两种方法

如果是已知的、可能的错误,我会使用返回码。如果这是一个我知道可以并且将要发生的场景,那么有一个代码会被发回


异常只用于我不期望的事情。

对于任何合适的编译器或运行时环境,异常都不会招致严重的惩罚。它或多或少类似于跳转到异常处理程序的GOTO语句。此外,运行时环境(如JVM)捕获异常有助于更轻松地隔离和修复bug。我将在java中使用任何C++中的一个Null PoExtExchange异常。< /P> < P>我的首选项(C++和Python)是使用异常。语言提供的工具使它成为一个定义良好的过程,可以引发、捕获和(如有必要)重新抛出异常,从而使模型易于查看和使用。从概念上讲,它比返回代码更简洁,因为特定的异常可以通过它们的名称来定义,并且附带有附加信息。对于返回代码,您仅限于错误值(除非您要定义ReturnStatus对象或其他内容)


除非您正在编写的代码是时间关键型的,否则与展开堆栈相关的开销不足以让您担心。

只有在发生您不希望发生的事情时才应返回异常

从历史上看,另一个例外是返回代码本质上是专有的,有时可以从C函数返回0来表示成功,有时返回-1,如果失败则返回其中任何一个,如果失败则返回1表示成功。即使在枚举时,枚举也可能是不明确的

异常还可以提供更多的信息,并明确指出“出了什么问题,这里是什么,堆栈跟踪和上下文的一些支持信息”


也就是说,一个枚举良好的返回代码对于一组已知的结果非常有用,一个简单的“函数的结果如下,它就是这样运行的”

我只使用异常,没有返回代码。我这里说的是Java


我遵循的一般规则是,如果我有一个名为
doFoo()
的方法,那么如果它没有“doFoo”,那么就会出现异常情况,应该抛出异常。

我从实用程序员那里得到的一条很好的建议就是“您的程序应该能够在不使用任何异常的情况下执行其所有主要功能”。

我担心异常的一件事是抛出异常会扰乱代码流。例如,如果您这样做

void foo()
{
  MyPointer* p = NULL;
  try{
    p = new PointedStuff();
    //I'm a module user and  I'm doing stuff that might throw or not

  }
  catch(...)
  {
    //should I delete the pointer?
  }
}

甚至更糟糕的是,如果我删除了一些不应该删除的内容,但在我进行其余清理之前被抛出捕获,会怎么样呢?抛出会给糟糕的用户IMHO带来很大的影响。

我更喜欢使用异常来处理错误和返回值(或参数)作为函数的正常结果。这提供了一个简单且一致的错误处理方案,如果正确执行,将使代码看起来更干净。

最大的区别之一是异常迫使您处理错误,而错误返回代码可以不检查

错误返回码,如果大量使用,也会导致非常难看的代码,有很多类似于此表单的if测试:

if(function(call) != ERROR_CODE) {
    do_right_thing();
}
else {
    handle_error();
}
就个人而言,我更喜欢对调用代码应该或必须处理的错误使用异常,并且只对“预期失败”使用错误代码,其中返回的内容实际上是有效和可能的。

在Java中,我使用(按以下顺序):

  • 契约式设计(确保在尝试任何可能失败的事情之前满足先决条件)。这捕获了大部分内容,我为此返回了一个错误代码

  • 在处理工作时返回错误代码(并在需要时执行回滚)

  • 异常,但这些仅用于意外事件


  • 我在exception vs.return code参数中的一般规则:

    • 在需要本地化/国际化时使用错误代码——在.NET中,您可以使用这些错误代码引用资源文件,然后该文件将以适当的语言显示错误。否则,请使用异常
    • 仅对真正异常的错误使用异常。如果是经常发生的错误,请使用布尔或枚举错误代码
    我不久前写了一篇关于这方面的文章


    引发异常的性能开销不应在您的决策中发挥任何作用。如果您做得正确,那么异常毕竟是异常的。

    有很多理由认为异常优于返回代码:

    • 通常,为了可读性,人们会尽量减少方法中返回语句的数量。这样,异常就可以防止在不一致状态下做一些额外的工作,从而防止可能损坏更多数据
    • 异常通常比返回值更详细,也更容易扩展。假设一个方法返回自然数,并且在发生错误时使用负数作为返回代码,如果方法的范围发生变化,现在返回整数,则必须修改所有方法调用,而不只是稍微调整exc感同身受
    • 异常允许更容易地分离正常行为的错误处理
      try:
          dataobj = datastore.fetch(obj_id)
      except LookupError:
          # could not find object, create it.
          dataobj = datastore.create(....)
      
      # wrong way:
      if os.path.exists(directory_to_remove):
          # race condition is here.
          os.path.rmdir(directory_to_remove)
      
      # right way:
      try: 
          os.path.rmdir(directory_to_remove)
      except OSError:
          # directory didn't exist, good.
          pass
      
      try{
          db.UpdateAll(somevalue);
      }
      catch (Exception ex) {
          logger.Exception(ex, "UpdateAll method failed");
          throw;
      }
      
      try{
          dbHasBeenUpdated = db.UpdateAll(somevalue); // true/false
      }
      catch (ConnectionException ex) {
          logger.Exception(ex, "Connection failed");
          dbHasBeenUpdated = false;
      }
      
      try{
          db.UpdateAll(somevalue);
      }
      catch (Exception ex) {
          logger.Exception(ex, "UpdateAll method failed");
          throw;
      }
      finally {
          db.Close();
      }
      
      try{
          using(IDatabase db = DatabaseFactory.CreateDatabase()) {
              db.UpdateAll(somevalue);
          }
      }
      catch (Exception ex) {
          logger.Exception(ex, "UpdateAll method failed");
          throw;
      }
      
      try{
          try {
              IDatabase db = DatabaseFactory.CreateDatabase();
              db.UpdateAll(somevalue);
          }
          finally{
              db.Close();
          }
      }
      catch (DatabaseAlreadyClosedException dbClosedEx) {
          logger.Exception(dbClosedEx, "Database connection was closed already.");
      }
      catch (Exception ex) {
          logger.Exception(ex, "UpdateAll method failed");
          throw;
      }
      
      if(doSomething())
      {
         if(doSomethingElse())
         {
            if(doSomethingElseAgain())
            {
                // etc.
            }
            else
            {
               // react to failure of doSomethingElseAgain
            }
         }
         else
         {
            // react to failure of doSomethingElse
         }
      }
      else
      {
         // react to failure of doSomething
      }
      
      try
      {
         doSomething() ;
         doSomethingElse() ;
         doSomethingElseAgain() ;
      }
      catch(const SomethingException & e)
      {
         // react to failure of doSomething
      }
      catch(const SomethingElseException & e)
      {
         // react to failure of doSomethingElse
      }
      catch(const SomethingElseAgainException & e)
      {
         // react to failure of doSomethingElseAgain
      }
      
      CMyType o = add(a, multiply(b, c)) ;
      
      void doSomething(CMyObject * p, int iRandomData)
      {
         // etc.
      }
      
      void doSomething(CMyObject * p, int iRandomData)
      {
         if(iRandomData < 32)
         {
            MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
            return ;
         }
      
         if(p == NULL)
         {
            MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
            throw std::some_exception() ;
         }
      
         if(! p.is Ok())
         {
            MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
         }
      
         // etc.
      }
      
      # I care whether this succeeds. If it doesn't return :ok, raise an exception.
      :ok = File.write(path, content)
      
      # I don't care whether this succeeds. Don't check the return value.
      File.write(path, content)
      
      # This had better not succeed - the path should be read-only to me.
      # If I get anything other than this error, raise an exception.
      {:error, :erofs} = File.write(path, content)
      
      # I want this to succeed but I can handle its failure
      case File.write(path, content) do
        :ok => handle_success()
        error => handle_error(error)
      end
      
      with {:ok, content} <- get_content(),
        :ok <- File.write(path, content) do
          IO.puts "everything worked, happy path code goes here"
      else
        # Here we can use a single catch-all failure clause
        # or match every kind of failure individually
        # or match subsets of them however we like
        _some_error => IO.puts "one of those steps failed"
        _other_error => IO.puts "one of those steps failed"
      end
      
      # Raises a generic MatchError because the return value isn't :ok
      :ok = File.write(path, content)
      
      # Raises a File.Error with a descriptive error message - eg, saying
      # that the file is read-only
      File.write!(path, content)