【问题标题】:.NET Catch General Exceptions.NET 捕获一般异常
【发布时间】:2009-09-21 12:24:38
【问题描述】:

.NET 编程指南声明我们不应捕获一般异常。由于一般的异常类型捕获,我假设以下代码不是很好:

    private object CreateObject(string classname)
    {
        object obj = null;
        if (!string.IsNullOrEmpty(classname))
        {
           try
           {
              System.Type oType = System.Type.GetTypeFromProgID(customClass);
              obj = System.Activator.CreateInstance(oType);
           }
           catch (Exception ex)
           {
               Log.Error("Unable to create instance for COM Object with class name " + classname + "\n" + ex.Message);
           }
        }
        return obj;
    }

在下面的代码中,我捕获了特定的异常,但不是全部,然后我重新抛出异常,以防与非泛型异常不同。然而,函数“CreateInstance”可以抛出许多异常(ArgumentNullException、ArgumentException、NotSupportedException、TargetInvocationException、MethodAccessException、MemberAccessException、InvalidComObjectException、MissingMethodException、COMException、TypeLoadException)。

是否可以捕获所有其他单个异常?还是有更好的办法?

    private object CreateObject(string classname)
    {
        object obj = null;
        if (!string.IsNullOrEmpty(classname))
        {
           try
           {
              System.Type oType = System.Type.GetTypeFromProgID(customClass);
              obj = System.Activator.CreateInstance(oType);
           }
           catch (NotSupportedException ex)
           {
              Log.Error("...." + ex.Message);
           }
           catch (TargetInvocationException ex)
           {
              Log.Error("...." + ex.Message);
           }
           catch (COMException ex)
           {
              Log.Error("...." + ex.Message);
           }
           catch (TypeLoadException ex)
           {
              Log.Error("...." + ex.Message);
           }
           catch (InvalidComObjectException ex)
           {
              Log.Error("...." + ex.Message);
           }
           catch (Exception ex)
           {
               Log.Error("Unable to create instance for COM Object with class name " + classname + "\n" + ex.Message);
               throw;
           }
        }
        return obj;
    }

【问题讨论】:

标签: .net exception-handling


【解决方案1】:

作为一般规则,您不应捕获异常,除非:

  1. 您有一个可以处理和解决的特定异常。但是,在这种情况下,您应该始终检查您是否应该首先考虑并避免异常。

  2. 您处于应用程序的顶层(例如 UI),不希望将默认行为呈现给用户。例如,您可能想要一个带有“请将您的日志发送给我们”样式消息的错误对话框。

  3. 您在以某种方式处理异常后重新抛出异常,例如,如果您回滚数据库事务。

在这个例子中,你为什么要捕捉所有这些不同的类型?在我看来,您的代码可以是:

try
{
    System.Type oType = System.Type.GetTypeFromProgID(customClass);
    return System.Activator.CreateInstance(oType);
}
catch (Exception ex)
{
    Log.Error("...." + ex.Message);

    //the generic catch is always fine if you then do this:
    throw;
}

所以你的问题是规则 (3) 的一个例子 - 你想记录一个异常,然后继续并抛出它。

所有不同的类型都在那里,因此在某些情况下你知道你可以处理(即案例 1)。例如,假设您知道有一个围绕 COMException 工作的非托管调用 - 那么您的代码变为:

try
{
    System.Type oType = System.Type.GetTypeFromProgID(customClass);
    return System.Activator.CreateInstance(oType);
}
catch (COMException cex)
{   //deal with special case:
    return LoadUnmanaged();
}
catch (Exception ex)
{
    Log.Error("...." + ex.Message);

    //the generic catch is always fine if you then do this:
    throw;
}

【讨论】:

  • 我捕获了所有这些不同类型的异常,因为它们可能会发生(例如 use 可以传递不受支持或未正确注册的类名),在这种情况下,没有一个好的方法来签入提前处理异常(因为我可以检查 ArgumentNullException)。
  • 我确信它们都可能发生,但除非您要针对该异常类型执行特定的操作,否则您最好还是抓住一般的Exception 并将其抛出。
  • 这个答案完全正确。关键是你向上抛出一般异常而不是吞下它们。
【解决方案2】:

在框架的 .NET 2+ 中捕获一般异常是完全可以接受的。

-- 编辑

您不这样做的唯一原因是,您是否可以通过不同的例外来做不同的事情。如果您打算以同样的方式处理它们,只需抓住一般的(或您所追求的特定的,然后让其他任何东西上升)。

【讨论】:

  • 我同意。有时我觉得我们把这并没有把一般例外推向极端。
  • 我不同意。这将使您的应用程序处于异常状态。你应该只处理你可以做某事的异常。简单地登录并继续前进是个坏主意。
  • @justin.m.chase:这是一个非常奇怪的立场。因此,每当您的应用崩溃时,您希望您的用户看到 .net 常规错误页面而不是您自己的页面?
  • 记录并继续操作对于未知异常来说是个坏主意,因为您不知道异常有多严重。例如,如果遇到 OutOfMemoryException 会发生什么?您不应该只记录该错误并继续前进。程序应该停止运行。 “快速失败”比试图保持不稳定的程序运行更好。 martinfowler.com/ieeeSoftware/failFast.pdf
  • @Noon 我同意这一点。但是,“如果您打算以同样的方式处理它们,则只捕获一般情况”可能会被一个没有经验的程序员解释为捕获一般 System.Exception,做某事,然后继续执行是可以的。最好指定如果你不知道你正在处理什么类型的错误,你最好重新抛出它或退出。
【解决方案3】:

我认为捕获所有异常以拦截问题并向用户显示友好消息而不是一些可怕的堆栈输出是可以的。

只要您不只是吞下异常,而是记录它们并在后台对它们做出适当的反应。

【讨论】:

    【解决方案4】:

    如果你想对不同类型的异常做一些特殊的事情,那么在一个单独的 catch 块中捕获它。否则单独记录是没有意义的。

    【讨论】:

      【解决方案5】:

      我们不应该使用一般异常并没有严格的规则,但是指导方针说,每当我们可以选择处理特定类型的异常时,我们不应该使用一般异常..

      如果我们确定所有异常都将以相同的方式处理,则使用泛型异常,否则针对每个特定异常,而泛型应在某些未知异常的最后出现..

      有时在您的应用程序中发生任何由于特定异常处理而未在代码中处理的异常,那么您的应用程序可能会崩溃。..

      所以更好的方法是处理所有特定的异常,然后给通用异常一个角落,这样应用程序就可以保持稳定而不会崩溃..

      并且可以在某处报告或记录那些不需要的通用异常,以便将来对应用程序进行版本改进..

      【讨论】:

        【解决方案6】:

        在处理错误时习惯性地捕捉基址Exception 被认为是一种不好的做法,因为这表明您可能对实际处理的内容缺乏了解。当你看到一段代码 catch Exception 时,它读到的是,“如果这里有任何问题,请执行此操作”,其中任何内容都可以从 NullReferenceExceptionOutOfMemoryException

        以同样的方式处理所有错误的危险在于,这意味着您不关心错误的严重程度或您可能如何解决错误。 99% 的情况下,当我看到代码 catch(Exception ex) 时,紧随其后的是吞下异常的代码,无法说明语句失败的实际原因。呃。

        您的错误记录示例显示了使用基础Exception 的正确方法,在您真正希望以相同方式处理所有异常的情况下,通常在应用程序的顶层以防止应用程序终止于丑陋的烂摊子。

        【讨论】:

          【解决方案7】:

          我同意基思的回答。这两个代码示例的真正问题是它们可以返回 null。您有一个名为 CreateObject 的方法,它返回一个对象类型的实例。该方法由于某种原因失败,例如 COMException。然后,两个代码示例都将返回 null。调用代码现在负责检查结果与 null,否则将抛出 NullReferenceException。

          引用Framework Design Guidelines 2nd ed.:

          如果会员不能成功 它的设计目的是什么,它应该 被视为执行失败, 并且应该抛出异常。

          它并没有像它的名字所暗示的那样做。扔!

          Keith 的代码很好;记录异常并重新抛出就可以了。

          这是一种私有方法,因此可以说设计指南不适用。不过,我仍然会在此处应用它们 - 当您可以使用异常处理时,为什么要使用“if (result == null)”来乱扔代码?

          【讨论】:

          • 有时有一个方法会尝试创建一个对象,但如果不能,则返回 null;然而,给方法起一个暗示它会做的事情的名字通常是个好主意。微软更喜欢让这样的例程返回一个成功/失败的布尔值,并有一个对象引用变量作为 byref 参数来存储新对象,但我更喜欢返回值或 null,因为它允许在语句中定义一个新变量尝试创建实例。
          • @supercat:如果我们可以信任其他开发人员(和我们自己)始终检查返回值是否为空,那就太好了……
          • 如果调用一个名为 TryGetFoo 的函数而不考虑它可能会失败,他们应该得到他们得到的。他们甚至可以更容易地忽略 MS 模式“TryGetFoo”方法的返回值(成功/失败标志)。 MS 模式版本在某些情况下确实有一个特性,这可能是好事也可能是坏事:函数可能会抛出异常,但在它这样做之前仍将传入的变量设置为某个值。如果变量是 IDisposable,这可能会很好,但如果忽略 create 方法的返回代码,则可能会导致混淆。
          猜你喜欢
          • 1970-01-01
          • 2010-11-01
          • 1970-01-01
          • 2010-10-03
          • 2011-11-24
          • 1970-01-01
          • 2011-01-03
          • 1970-01-01
          • 2011-11-22
          相关资源
          最近更新 更多