你能在每个块中捕获不止一种类型的异常吗? [复制]

Posted

技术标签:

【中文标题】你能在每个块中捕获不止一种类型的异常吗? [复制]【英文标题】:Can you catch more than one type of exception with each block? [duplicate] 【发布时间】:2010-11-21 06:19:22 【问题描述】:

This question is close to what I want to do,但不完全在那里。

有没有办法简化下面的代码?

private bool ValidDirectory(string directory)

    if (!Directory.Exists(directory))
    
        if (MessageBox.Show(directory + " does not exist. Do you wish to create it?", this.Text) 
            == DialogResult.OK)
        
            try
            
                Directory.CreateDirectory(directory);
                return true;
            
            catch (IOException ex)
            
                lblBpsError.Text = ex.Message;
            
            catch (UnauthorizedAccessException ex)
            
                lblBpsError.Text = ex.Message;
            
            catch (PathTooLongException ex)
            
                lblBpsError.Text = ex.Message;
            
            catch (DirectoryNotFoundException ex)
            
                lblBpsError.Text = ex.Message;
            
            catch (NotSupportedException ex)
            
                lblBpsError.Text = ex.Message;
            
        
    

    return false;

这似乎是一种浪费,如果我以后想更改向用户报告错误的方式,或者我想记录这些错误,或者其他什么,那么我必须更改 5 个不同的 catch 块。我是否遗漏了什么,或者这是否公然反对代码重用?

我只是想(太)懒惰吗?

【问题讨论】:

【参考方案1】:

你可以使用:

catch (SystemException ex)

  if(    (ex is IOException)
      || (ex is UnauthorizedAccessException )
// These are redundant
//    || (ex is PathTooLongException )
//    || (ex is DirectoryNotFoundException )
      || (ex is NotSupportedException )
     )
       lblBpsError.Text = ex.Message;
    else
        throw;

【讨论】:

这可能会简化一点 - 删除 DirectoryNotFoundException 和 PathTooLongException 因为(例如 IOException)将为它们返回 true 不,不会的。 is 运算符非常精确,它不会为子类返回 true,只会返回确切的类。我也是这么想的,然后在LINQPad中测试了一下。 Matthew,下面的测试代码返回 true。我不确定您为什么得到不同的结果...尝试 throw new DirectoryNotFoundException(); catch (Exception ex) return (ex is IOException); Hrrm... 你是对的,我可能搞砸了我的测试(在你的例子中检查了 IOException 是否是 DirectoryNotFoundException)。哎呀。无视我。 我还要在这里说的一件事是,您可能应该只捕获SystemException。重新投掷并不等于一开始就没有抓住它。除此之外,FxCop 和其他类似程序会警告捕获异常,最好的方法是不要这样做。【参考方案2】:

如果异常共享一个共同的超类,那么您可以捕获该超类。

【讨论】:

他们没有。或者更确切地说,它们不共享一个我想要处理的任何其他可能的异常都没有的通用超类。【参考方案3】:

是的,你想变得懒惰,但懒惰是the virtues of a programmer 之一,这很好。

至于您的问题:我不知道,但有一些解决方法:

为异常提供一个共同的祖先。我认为这在您的情况下是不可能的,因为它们似乎是内置的。 尽可能捕捉最通用的异常。 将处理代码移到自己的函数中,并从每个 catch 块中调用。

【讨论】:

为了记录,它们都是内置的,被调用的方法也是如此。【参考方案4】:

这很烦人,其他答案提出了很好的解决方法(我会使用@Lotfi)。

但是,考虑到 C# 的类型安全性,这种行为是必需的。

假设你可以这样做:

try

    Directory.CreateDirectory(directory);
    return true;

catch (IOException, 
    UnauthorizedAccessException,
    PathTooLongException,
    DirectoryNotFoundException,
    NotSupportedException ex)

    lblBpsError.Text = ex.Message;

现在ex 是什么类型?它们都有.Message,因为它们继承了System.Exception,但是尝试访问它们的任何其他属性时就会遇到问题。

【讨论】:

您必须使用最小公分母。您只能捕获 Exception 的派生词,因此这种方式没有真正的问题。 所以在这种情况下,您最终会遇到 SystemException。编译器可以解决这个问题,因此类型安全也不会受到威胁。 编译器可以,IDE 也可以,但是对于开发人员来说会有些模糊——他们应该对 ex 期望什么智能感知?不过,我可以看到类似的方法可以工作——比如 C#4 中的协/逆变支持 我现在可以看到它...catch (in SystemException)... 除非这正是已经发生的事情。也许catch(in<IOException, UnauthorizedAccessException, ...> SystemException)。可能很有趣。【参考方案5】:

同样重要的是要注意,当捕获多于一种类型的异常时,它们应该按大多数指定到最一般的顺序排列。异常将在列表中找到它匹配的第一个并抛出该错误,不会抛出任何其他错误。

【讨论】:

我在发布后意识到 IOException 是其中一些的基类,但它是上面列表中的第一个。无论如何,这段代码并不重要,但这是一个很好的观点。【参考方案6】:

为了完整起见:

在 VB 中,您可以使用条件异常处理:

Try
    …
Catch ex As Exception When TypeOf ex Is MyException OrElse _
                           TypeOf ex Is AnotherExecption
    …
End Try

这样的Catch 块只会在指定的异常情况下输入 - 与 C# 不同。

也许未来的 C# 版本会提供类似的功能(毕竟,该代码有特定的 IL 指令)。

MSDN: How to: Filter Errors in a Catch Block in Visual Basic

【讨论】:

【参考方案7】:

查看来自EntLib 的The Exception Handling Application Block。他们阐明了一种非常好的基于策略和配置的异常处理方法,可以避免大的条件逻辑块。

【讨论】:

【参考方案8】:

您可以捕获基类异常(所有异常都派生自SystemException):

try

  Directory.CreateDirectory(directory);
  return true;

catch (SystemException ex)

  lblBpsError.Text = ex.Message;

但是你最终可能会捕捉到你不想捕捉的异常。

【讨论】:

这里的问题是,ArgumentException 也是 SystemException ,我特别不想抓住那些,因为那是我的问题,而不是用户。 然后你只需在捕获异常的分支之前添加一个捕获 ArgumentExceptions 的捕获分支(或者编译器是否优化了 try catch?) 它按顺序运行它们。我相当确定至少没有得到优化。但随后你又回到了三个纯粹是 throw; 的 catch 语句【参考方案9】:

你可以的

ex.GetType()

见http://msdn.microsoft.com/en-us/library/system.exception.gettype.aspx

编辑

try            
                
    Directory.CreateDirectory(directory); 
    return true;           
            
catch (Exception ex)            
   switch(ex.GetType())
         case .....
         case ..........
    blBpsError.Text = ex.Message;            

【讨论】:

这需要捕获Exception,但是是的,这是一种解决方案。 做一个开关类型和默认就可以再扔一次? 你会捕获异常,但如果它不是你正在处理的类型,你总是可以再次抛出它。 对我来说最好的解决方案是拥有一个 ExceptionCustom 类并在那里做类型,这样它就不会混淆代码...... 您不能打开 ex.GetType():表达式必须隐式转换为以下类型之一:sbyte、byte、short、ushort、int、uint、long、ulong ,字符,字符串。这不是类型的情况。你可以使用switch (ex.GetType().Name),但另一方面你会失去类型安全......【参考方案10】:

我了解其中一些例外情况可能无法预见,但请尽可能尝试实施您自己的“先发制人”逻辑。例外是昂贵的,但在这种情况下可能不会破坏交易。

例如,使用 Directory.GetAccessControl(...) 而不是依赖于抛出 UnauthorizedAccessException。

【讨论】:

异常并没有那么昂贵以至于被人类注意到。你是对的,在这种情况下,它绝对不会破坏交易,而且很可能永远不会发生,如果它发生,我宁愿不要崩溃。【参考方案11】:

您可以使用委托,这将满足您的需求:

编辑:简化了一点

static void Main(string[] args)

    TryCatch(() =>  throw new NullReferenceException(); , 
        new []  typeof(AbandonedMutexException), typeof(ArgumentException), typeof(NullReferenceException) ,
        ex => Console.WriteLine(ex.Message));



public static void TryCatch(Action action, Type[] exceptions, Action<Exception> catchBlock)

    try
    
        action();
    
    catch (Exception ex)
    
         if(exceptions.Any(p => ex.GetType() == p))
         
             catchBlock(ex);
         
         else
         
             throw;
         
    

您的特定尝试/捕获将是:

bool ret;
TryCatch(
    () =>
        
            Directory.CreateDirectory(directory);
            ret = true;
        ,
    new[]
        
            typeof (IOException), typeof (UnauthorizedAccessException), typeof (PathTooLongException),
            typeof (DirectoryNotFoundException), typeof (NotSupportedException)
        ,
    ex => lblBpsError.Text = ex.Message
);

return ret;

【讨论】:

以上是关于你能在每个块中捕获不止一种类型的异常吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中是不是可以在同一个 catch 块中捕获两个异常? [复制]

如何在 Java 6 中捕获所有已检查的异常(在一个块中)?

你能在 CSS“内容”属性中使用 HTML 实体吗? [复制]

你能在 iOS 上的 PhoneGap 应用中显示搜索键盘吗? [复制]

你能在 javascript 中设置有序列表编号的样式吗? [复制]

你能在 Silverlight 的 SaveFileDialog 中设置初始目录吗