关于C#:每个块可以捕获多个异常类型吗?

Can you catch more than one type of exception with each block?

本问题已经有最佳答案,请猛点这里访问。

这个问题与我想做的很接近,但并不完全是这样。

是否有方法简化以下代码?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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个不同的捕获块。我是缺少什么东西,还是公然反对代码重用?

我只是想变得(太)懒惰吗?


你可以使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}


如果异常共享一个公共的超类,那么您只需捕获该超类。


是的,你想变得懒惰,但是懒惰是程序员的优点之一,所以这很好。

至于你的问题:我没有意识到,但也有一些解决办法:

  • 给出例外的共同祖先。我认为这对你来说是不可能的,因为他们似乎是在建造。
  • 捕获最一般的异常。
  • 将处理代码移到它自己的函数中,并从每个catch块调用该函数。


为了完整起见:

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

1
2
3
4
5
6
Try
    …
Catch ex As Exception When TypeOf ex Is MyException OrElse _
                           TypeOf ex Is AnotherExecption
    …
End Try

这样的Catch块只会针对指定的异常而被输入,与c不同。

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

msdn:如何:在Visual Basic中筛选catch块中的错误


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


这很烦人,其他答案都建议了很好的解决方法(我会使用@lotfi's)。

然而,考虑到C的类型安全性,这种行为是一项要求。

假设您可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
    Directory.CreateDirectory(directory);
    return true;
}
catch (IOException,
    UnauthorizedAccessException,
    PathTooLongException,
    DirectoryNotFoundException,
    NotSupportedException ex)
{
    lblBpsError.Text = ex.Message;
}

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


从entlib中签出异常处理应用程序块。它们阐明了一种非常好的基于策略和配置的异常处理方法,避免了大型条件逻辑块。


我理解其中一些例外可能是不可预见的,但如果可能,尝试实现自己的"先发制人"逻辑。例外是昂贵的,尽管在这种情况下可能不会破坏交易。

例如,使用directory.getAccessControl(…)而不是依赖于要引发的未经授权的AccessException。


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

编辑:简化了一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
         }
    }
}

您的特别尝试/捕捉将是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;

您可以捕获一个基类异常(您的所有异常都是从SystemException派生的):

1
2
3
4
5
6
7
8
9
try
{
  Directory.CreateDirectory(directory);
  return true;
}
catch (SystemException ex)
{
  lblBpsError.Text = ex.Message;
}

但最后你可能会发现你不想捕捉的异常。


你可以做到

GETType()

请参阅http://msdn.microsoft.com/en-us/library/system.exception.gettype.aspx

编辑

1
2
3
4
5
6
7
8
9
10
11
try            
{                
    Directory.CreateDirectory(directory);
    return true;          
}            
catch (Exception ex)            
{   switch(ex.GetType())
         case .....
         case ..........
    blBpsError.Text = ex.Message;            
}