代码之家  ›  专栏  ›  技术社区  ›  Seth Spearman

有时候我能接受例外吗?

  •  36
  • Seth Spearman  · 技术社区  · 14 年前

    我有一个最佳实践问题。我意识到这是主观的,但我想问比我聪明的人,这是否是一个常见的编程实践。

    如果您有一种非关键方法,不想干扰应用程序的重要功能,那么使用这种错误接收器是否常见?

    Try 
        'do stuff.  not important if it fails.
    
    Catch ex as exception
        'sink.  do nothing.
    End Try
    

    如果你想雇用我,你读了我的一些代码,看到了这个……你会吗?

    塞思

    编辑 真的!谢谢你的回答。我认为大家的共识是永远不应该这样做,或者应该是非常罕见的。

    我想我会给你这个问题的背景。首先,我对Karl Sequin的文章非常熟悉,并且多年来一直遵循这种模式。

    但今天,在我正在进行的项目中,我正在研究变更列表,并面临着添加一个简单特性的问题。(如果您想知道……它正在向富文本框添加上下文菜单支持。)

    随附的便条上写着:“如果超过15分钟……就把它放下。”

    因此,我面临着添加一个可能有用的特性,但实际上没有时间测试它是否会破坏工作特性。对于记录,这个系统的异常处理程序有一个处理和下沉或记录这些错误的机制。但是如果我在一个没有健壮错误处理系统的系统上工作呢?是否可以添加此功能,如果发生错误…不会真正丢失任何内容。

    那是我的想法。但我已经把你的信息牢记在心…基本上这是个坏主意。

    塞思

    21 回复  |  直到 14 年前
        1
  •  13
  •   Steven Sudit    14 年前

    是的,这很常见,但一般来说不应该这样做。

    也有例外,比如 OutOfMemoryException 最好不要捕捉,除非您捕捉到它们试图优雅地终止应用程序。

    在大多数情况下,吞咽 System.Exception System.SystemException 将不可避免地隐藏进一步的运行时问题。

        2
  •  8
  •   Dan Rosenstark    14 年前

    你应该 从未 隐藏 例外 . 不管怎样,我可能会雇用你,但你在为我工作时不会这样做。)

    然而,在Java(例如)中,对于某些可能被忽略的情况,使用了异常。 FileNotFoundException (就像你做的,带着评论)。但是如果你发现了一种例外——比如 IOException --你不能把它藏起来。如果异常对您没有特别的意义,请用 RuntimeException 把它扔给客户。

    一路上的某个地方, Exception 应该处理,但不能隐藏。

        3
  •  7
  •   3Dave    14 年前

    这很常见,但最好避免。至少您应该将异常记录在某个地方。

    另外,请避免对流控制使用异常。应该编写代码来处理所有可能的情况,而不诉诸异常。除了性能提高之外,此方法还告诉您,当发生异常时,值得您注意。

        4
  •  5
  •   bdukes Jon Skeet    14 年前

    你不应该在这里抓住所有的例外。在极少数情况下,可以忽略一个特定的、预期的例外。但是,捕捉 全部的 例外和吞下它们意味着你试图在 OutOfMemoryException 以及其他无法弥补的例外

        5
  •  5
  •   Daniel Vassallo    14 年前

    虽然这里的大多数人实际上都在谈论开发人员,但我想指出 从另一个角度看这个故事。

    我承认:我接受了例外,而且在每一个案例中,这都是因为我是设计师 函数的 拧紧 .

    “例外”是指“例外”,而不是失败或错误。我的意思是:如果函数 必须确认输入可能不正确,我希望函数 不使用例外。我的愤怒特别针对“ParseException”的。

    如果分析输入,很可能会发现未知/损坏/不精确的输入。那 是“正常的”,也不例外。在这种情况下,如果开发者 如果函数无法正确分析,则引发ParseException。

    为什么?

    • 异常中断了控制流
    • 例外是缓慢的
    • 通常这并不重要,因为您是用默认值初始化的。

    如果您调用parseFunction数千或数百万(!)有时你会阻塞你的日志文件 确切地 没有什么。

    与此相反,我的理想函数返回一个带有状态代码和 消息类

    StatCode stat = parseXYZ(Reader r);
    

    然后可以进行测试。

    if (stat.code != OK)  
      //whatever
    

    如果以另一种方式,您遇到无法预见的问题(文件未锁定, 无意义的论点,非法线程状态)异常是很好的。

        6
  •  4
  •   David Hall    14 年前

    我个人认为这是非常糟糕的做法。

    这是(不幸的)我在检查代码时一直在寻找的一个问题,当我发现空的catch块或catch块时,我问的问题基本上是吞咽异常:

    1. 此时此刻,你是否100%确信这个例外永远不会发生?
    2. 您是否也百分之百地确定,无论代码库是如何开发的,将来在这里捕获的任何异常都将无关紧要?
    3. 即使1和2都是真的,在这里放一些日志真的很难吗?

    对我来说,最重要的是日志记录——异常的良好日志记录,对程序执行的良好跟踪是代码设计的基础,这些代码可以随着时间安全地修改,并演变为用户信任的稳定系统。

    除此之外,一个好的实践是只捕获特定的异常,并让所有其他异常冒泡到堆栈中。盲目地将异常作为一种处理错误的方法是永远不正确的。

        7
  •  4
  •   Frank V    14 年前

    最佳实践表明你应该“这样做”。什么是如此不重要,以至于你不想知道发生了一个错误?我想我从来没有遇到过这个。

    我要么不使用try-catch结构,要么将代码重构为一个返回布尔值的方法,这样我至少可以确定它是否成功…

    这是我的想法…

    更新 :有时可能需要接收异常。在这些情况下,我将确定哪些异常可能是这个的候选者,然后捕获那些特定的异常。但这还远远不够 Exception 可能是任何事情,比如前面提到的 OutOfMemoryException . 简而言之,我只捕获特定的异常,目的是忽略它。

        8
  •  1
  •   Mark Byers    14 年前

    不,我认为这不好。

    您的系统可能不是关键的,但您可能希望它运行,否则一开始就不会编写它。现在,如果有一天它停止运行,那些必须维护它的人不知道为什么它突然不工作,因为你完全隐藏了错误。事实上,他们甚至可能要花很长时间才能意识到错误的来源,或者甚至在第一时间就有了错误。它可能会浪费很多开发人员的时间,但是它不会花费很多时间来记录错误而不是丢弃错误。

    你会发现一些例外,比如 OutOfMemoryException 你真的不想抓住和丢弃。如果这段代码不重要,但是占用了太多内存,使整个系统变慢,那么您需要被告知,这样您就可以修复它了。

        9
  •  1
  •   Chris    14 年前

    有时我发现开发人员宁愿捕获并吞咽异常,而不是编写正确的错误处理代码。

    有一些代码,如果抛出异常,它们会捕获异常,尝试其他操作,如果抛出异常,则执行其他操作等。异常是对象,在抛出时创建。如果可以,请编写正确的处理代码来处理错误,并且仅在 can't continue 不管什么原因。

    至于吞咽异常,如果我说我写了没有的代码,那我就撒谎了。这不是一个最佳实践,更不用说在程序运行不好的时候很难调试了。我也被他们咬过。

        10
  •  1
  •   Erik van Brakel scottrakes    14 年前

    这几乎从来都不是一个好主意,但取决于你如何做例外情况,在适用的情况下也有例外情况。例如:

    try {
       Cache.refresh(myObject)
    }
    catch (NotInCacheException) {
       // If the object isn't already in the cache, then we don't have to worry 
       // about refreshing it. It's assumed that this is a common occurrence,
       // so we don't need to log it either; that ends up just spamming
       // the logs. So, just swallow it, silently.
    }
    

    (注意:cache.refresh()是否应该抛出notIncacheException在这里没有问题;我只是将其作为一个简单的示例使用)。

    不过,请注意:只有当你有一个非常具体的例外,你才能摆脱这一点。在你的例子中,你正在 Exception ,这就是 方式 太高了(你会吞下其他人提到的严重错误)。

    另外:如果您有一个灵活的日志框架,那么出于信息目的,您可能仍然需要记录这个异常。我在这里特别考虑log4net/log4j:您可以配置日志(甚至在运行时)来根据类和严重性选择/忽略异常。因此,您可以在正常情况下消除这些类型的异常,但是如果您想在应用程序周围进行一些搜索,就要记录它们。

    如果你决定接受这个例外,我的政策是 总是 有一个代码注释来解释为什么这样做;这不仅标志着它是另一个坏的开发实践。

        11
  •  1
  •   justkt    14 年前

    作为一个被指派进行错误修复的新手,我发现我被指派的特定错误正在打印(调试)“这不应该发生。”在克服了我对在明显不可能的错误条件下工作的恐惧之后,我终于设法发现这是由一个微妙的竞争条件引起的。如果该代码的作者最初只是简单地消除了异常,而没有使用最少的调试语句,那么跟踪争用条件就要困难得多。

    从那时起,我就成了处理异常或推动异常直到错误条件以用户友好的方式显示给最终用户的巨大支持者,这是简单地记录调试语句的又一步。

        12
  •  1
  •   RMorrisey    14 年前

    我认为解决这个问题的最好方法是讨论一些可以接受的案例。 在大多数情况下,这是不安全的。 在安全的情况下,最好至少在警告、调试或跟踪日志中吐出一些关于它的信息。我只能想到一些安全的情况:

    您期望有一个非常具体的异常,并且知道您可以从中恢复:

    try {
     Integer.Parse(someString);
    }
    catch (ParseException pe) {
     //In this particular use case, if the string doesn't parse, 
     //I don't need to do anything. Logging a warning might be a
     //good idea, in some cases
    }
    

    您正在使清理代码安全:

    beginDatabaseTransaction();
    try {
     doSomeDatabaseOperation();
    }
    catch (Exception e) {
     log.error(e); //log the original exception
     try {
      rollbackDatabaseConnection();
     }
     catch(Exception e2) {
      //You may ignore this one-off exception, though
      //I would strongly consider logging it
     }
     throw; //rethrow the original exception and let it propagate
    }
    

    (这决不是数据库操作的最佳实践模式;只是一个人为的示例。)

    在我们的生产系统中,我们有不止一个案例,过度的异常捕获造成了比它所做的好得多的损害。小心这个。

    还有一件事: 如果不排除要抛出的特定错误,则通常不应尝试捕获错误。

    大多数例外情况都是为了帮助您、开发人员查找和诊断代码中的错误。它们的设计目的是使程序(或当前操作)终止,以防止不可预知的行为(例如尝试加载数据库中的所有记录,因为您无法为SQL语句构造WHERE子句)。当捕获未知异常时,您将从当前运行的C隐藏错误。编写代码,使代码可以意外地运行,而不是仅仅终止。

        13
  •  1
  •   David R Tribble    14 年前

    你应该区分 可忽视的 例外和 致命的 例外情况。我不确定这是如何在VB中完成的,但是在Java(例如)中,来自 Error 应该 从未 被忽视。这包括类加载器失败、内存不足、堆栈溢出等。

        14
  •  1
  •   Daniel Vassallo    14 年前

    Best Practices for Handling Exceptions (MSDN) :

    根据他们,你也可以

    对错误做些什么,或者忽略它。

    我想由你和你的团队来决定吧。

        15
  •  1
  •   Chris McKenzie    14 年前

    一般来说,我会说这是一种糟糕的做法——但让我们假设,所讨论的方法是一种日志记录方法。我不希望我的应用程序崩溃,因为事件日志已满。在这种情况下,我会说没关系。不过,我还是会将它输出到调试窗口。

        16
  •  0
  •   Anon.    14 年前

    我会说“不要这样做”——不是那样的。

    首先,尝试重构“非关键”代码,这样它就不会引发异常。

    如果你不能做到,至少 不要盲目地抓住 Exception . 只捕获您期望它抛出的异常(并将它们记录到某个地方!)-别的什么 一些你需要注意的事情。

        17
  •  0
  •   fishhead    14 年前

    。如果使用VS2008,则至少可以将它们发送到调试窗口。

     System.Diagnostics.Debug.WriteLine("exception in method - my method -: "+ex.message);
    
        18
  •  0
  •   stacker    14 年前

    我这样做只是为了解决一些烦人的问题,比如关闭连接:

    try {
       conn.close()
    }
    catch( Exception e ) {
    }
    

    因为我已经确定我不再需要它了。

        19
  •  0
  •   jackjumper    14 年前

    我认为你可以做到,但你必须睁大眼睛,知道你为什么要这样做。仔细想想。在我的商店里,我们要求你有一个解释为什么你要吃这个例外的评论

        20
  •  0
  •   Andrew McGregor    14 年前

    如果这是好事还是坏事,取决于语言和异常的代价。您仍然不应该捕获所有异常,但是,例如,在Python中,以下是一个常见的习惯用法:

    d={}
    for i in theList:
      try:
        d[i] += 1
      except KeyError:
        d[1] = 1
    

    这是执行默认插入的最快方法。对于DivisionByZeroError或任何数量的其他异常,您都会做同样的事情。一组流控制结构的定义可以归结为 raise StopIteration

        21
  •  0
  •   staticsan    14 年前

    Java提出了用例外来编程的必要性。我发现Java倾向于随意抛出异常,这是恼人的,因为如果你不想让应用程序崩溃,你必须抓住它们。不幸的是,Java中的异常是一个相当重的特性。

    我了解了如何在图标中有效地使用异常。图标有一个整洁的东西叫做“失败”。如果表达式没有可以返回的值,那么它将失败。这就像一个小型的异常,而不是 try {} catch {} 符号,失败由语言元素直接处理,例如 if while .

    IMO,这就是异常在Java中应该如何工作…但不幸的是,他们没有这样做,相反,您最终编写的是防御性代码来防止异常,而不是简单自然地处理异常。默默地吞咽一个特殊的例外,因为它相当罕见,比阻止它更优雅,我认为这是一个有用的技巧。但这是一种回应” 你必须先知道规则,然后才能违反规则。 “。换句话说,不要经常这样做。

    我希望更多的语言支持图标中的成功/失败模型。