【问题标题】:Catching exceptions捕捉异常
【发布时间】:2012-01-10 08:19:27
【问题描述】:

在以下代码块中,new 构造函数被记录为抛出七种不同的异常类型,包括 System.IO.PathTooLongExceptionSystem.ArgumentExceptionSystem.UnauthorizedAccessExceptionSystem.SecurityException

try
{
    var fileInfo = new FileInfo(path);
}
catch ???

我只是想确保path 实际上是一个可访问的路径,并且当我尝试使用此path 创建文件时不会发生任何不良情况。

那么,问题:

书籍和编码指南告诉我我不应该使用catch (Exception) 构造,但我面临以下情况 - 我可以捕获和处理 4 个(共 7 个)异常类型 并且它们对每种异常类型的处理都是完全一样的。

这应该怎么做?


我也可以想到下面的解决方案,但是看起来还是很糟糕:

catch (Exception exception)
{
    Debug.Assert(exception is PathTooLongException || exception is ...);

    // (or maybe rethrow it further if there is a type mismatch)

    //... Handling code ...
}

【问题讨论】:

  • 能否描述一下详细的使用场景?

标签: c# .net exception exception-handling try-catch


【解决方案1】:

你可以这样做:

try
{
    var fileInfo = new FileInfo(path);
}
catch (System.IO.PathTooLongException ex)
{
    handleError(ex);
}
catch (System.ArgumentException ex)
{
   handleError(ex);
}
catch (System.UnauthorizedAccessException ex)
{
}
catch (System.SecurityException ex)
{
   handleError(ex);
}

public void handleError(Exception ex)
{
    //do some stuff
}

【讨论】:

    【解决方案2】:

    您不必在每一行代码中捕获所有可能的异常。

    相反,只有当您有上下文并且准备好处理并采取适当的行动时才能捕捉到。否则,就让它冒泡吧。

    捕获异常(尤其是在您没有足够上下文来处理的常用函数的堆栈深处)可能会出现问题。有时会导致吞咽和处理不当。有上下文时处理异常。

    对于那个 FileInfo 异常,它取决于它在堆栈中的深度。如果它在可以通过多个路径和场景到达的库中,您不应该处理,此时您将做什么?如果情况也是如此,并且它通过一个深堆栈一直冒泡到执行您想要处理它的操作的 GUI,您要捕获什么?它的执行路径上的每个代码路径的每个可能的异常?而且,随着代码的更改,您要重新评估所有路径吗?在浅代码路径(调用该方法的文件信息对话框)中,这是一个简单的调用,但即使那样你也不会以不同的方式处理它 - 你将在面板中显示错误对话框或消息。异常类型提供了处理不同的能力。在这两种情况下,您都会处理异常,所以我对该准则说 bah hum bug。

    非常清楚的是你不应该抛出异常类型。这会缩短以不同方式处理的能力。

    另一个相关帖子:

    Trying to understand exceptions in C#

    【讨论】:

    • 我读了很多这样的思路,但我不太同意。考虑两种情况: (1) System.ArgumentException 在尝试在 LoadDocument 方法开始时打开文件时发生,其效果是文档未打开但系统状态不受干扰; (2) System.ArgumentException 发生在 LoadDocument 中的某个其他点,例如由于不正确的跨线程操作破坏程序状态。如果前者的 ArgumentException 冒泡了,调用代码如何与后者区分开来?
    • 我建议正确的做法是捕获和包装系统异常,以指示整体系统状态。否则,允许恢复应该是可恢复的异常的唯一方法是使用 Pokemon 异常处理。
    【解决方案3】:

    另外,您可以先使用 File.Exists(path) 或 Directory.Exists(path) 测试路径

    【讨论】:

    • 避免流控异常总是一件好事。
    • 请记住,无论多么不可能,这里都存在竞争条件。文件或目录可能会在您检查它和您实际尝试使用它的时间之间消失。
    【解决方案4】:

    我想在这里补充一下,因为我见过的几乎所有java代码中的异常处理都是不正确的。 IE。对于被忽略的异常,您最终会遇到非常难以调试的错误,或者同样糟糕的是,您会得到一个不告诉您任何信息的晦涩异常,因为盲目地遵循“捕获(异常)是不好的”,事情会变得更糟。

    首先,了解异常是一种便于跨代码层返回错误信息的方式。现在,错误 1:一个层不仅仅是一个堆栈帧,一个层是具有明确职责的代码。如果您只是编写接口和实现代码只是因为,那么您有更好的东西要修复。

    如果层设计得很好并且有特定的职责,那么错误信息在冒泡时具有不同的含义。

    因此,这意味着当发生异常时,您有 2 个选项,但您需要了解您在层中的哪个位置:

    A) 如果你在一个层的中间,并且你只是一个内部的、通常是私有的、帮助函数并且出现了问题:不要担心,让调用者接收异常。完全没问题,因为您没有业务背景并且 1)您没有忽略错误并且 2)调用者是你层的一部分,应该知道这可能发生,但你现在可能没有上下文来处理它。

    或者...

    B) 你是层的顶部边界,内部的立面。然后,如果你得到一个异常,默认应该是 CATCH ALL 并阻止任何特定的异常跨越到上层,这对调用者来说没有意义,或者更糟糕的是,你可能会改变并且调用者将依赖于一个实现细节,两者都会打破。

    应用程序的优势在于层与层之间的解耦程度。在这里,您将作为一般规则停止一切并使用一般异常重新抛出错误,将信息转换为对上层更有意义的错误。

    规则:层的所有入口点都应使用 CATCH ALL 保护,并翻译或处理所有错误。现在这种“已处理”仅发生 1% 的时间,大多数情况下您只需要(或可以)以正确的抽象返回错误。

    不,我确信这很难理解。真实示例 ->

    我有一个运行一些模拟的包。这些模拟在文本脚本中。有一个包可以编译这些脚本,还有一个通用的 utils 包,它只读取文本文件,当然还有基本的 java RTL。 UML 依赖是->

    模拟器->编译器->utilsTextLoader->Java文件

    1) 如果某个私有内部的 utils 加载器出现问题,并且我得到 FileNotFound、Permissions 或其他任何内容,那就让它过去吧。你无能为力。

    2) 在边界处,在最初调用的 utilsTextLoader 函数中,您将遵循上述规则和 CATCH_ALL。编译器不关心发生了什么,它只需要现在是否加载了文件。因此,在捕获中,重新抛出一个新异常并将 FileNotFound 或其他任何内容转换为“无法读取文件 XXXX”。

    3) 编译器现在将知道源没有加载。这就是它需要知道的一切。因此,如果我稍后将 utilsTestLoader 更改为从网络加载,编译器将不会更改。如果您放开 FileNotFound 并在以后进行更改,您将一无所获。

    4) 循环重复:为文件调用底层的实际函数在收到异常时将不执行任何操作。所以它让它上升。

    5) 当异常到达模拟器和编译器之间的层时,编译器再次 CATCHES_ALL,隐藏任何细节并抛出更具体的错误:“无法编译脚本 XXX”

    6) 最后再循环一次,调用编译器的模拟器函数就放手了。

    7) 最后的边界是用户。用户是一个 LAYER 并且都适用。 main 尝试 catches_ALL 并最终创建一个漂亮的对话框或页面并向用户“抛出”翻译错误。

    所以用户看到了。


    模拟器:致命错误无法启动模拟器

    -编译器:无法编译脚本 FOO1

    --TextLoader: 无法读取文件 foo1.scp

    ---trl: FileNotFound


    比较:

    a) 编译器:NullPointer 异常

    b) 加载器:找不到文件

    c) 什么都没有发生,因为一切都被忽略了!!!

    当然,这假设在每次重新抛出时您都没有忘记设置原因异常。

    好吧,我的 2cts。这个简单的规则多次救了我的命……

    -啤酒

    【讨论】:

      猜你喜欢
      • 2020-04-20
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多