为什么异常过滤器比捕获和重新抛出更可取?

Why exception filters are preferable to catching and rethrowing?

基于 this question(新的 Exception filter 功能有什么好处?)。

声明:

Exception filters are preferable to catching and rethrowing because they leave the stack unharmed. If the exception later causes the stack to be dumped, you can see where it originally came from, rather than just the last place it was rethrown.

在做了一些测试后,我没有看到新旧两者之间的区别,我仍然可以从重新抛出的地方看到异常。所以,或者信息没有得到确认,我不明白异常过滤器(这就是我问的原因),或者我做错了。你能解释一下为什么这个动作过滤器是一个优势吗?

class specialException : Exception
{
   public DateTime sentDateTime { get; } = DateTime.Now;
   public int code { get; } = 0;
   public string emailsToAlert { get; } = "email@domain.com";
}

然后:

        try
        {
            throw new specialException(); //line 16
            throw new Exception("Weird exception");
            //int a = Int32.Parse("fail");
        }
        catch (specialException e) when(e.code == 0)
        {
            WriteLine("E.code 0");
            throw; // <-Line 23
        }
        catch (FormatException e) 
        {
                WriteLine("cond1 " + e.GetBaseException().Message+" "+e.StackTrace);
                throw;
        }
        catch (Exception e) //when (cond2)
        {
            Console.WriteLine("cond2! " + e.Message);
                throw;
        }

结果:

异常过滤的优势更多地体现在过滤器不匹配时,而不是匹配时。如果您删除除第一个之外的所有 catch 块,并将第一个 catch 块上的过滤器更改为 when(e.code != 0),则异常的调用堆栈将指示它被抛出第 16 行。

旧的实现方式如下:

    try
    {
        throw new specialException(); //line 16
        throw new Exception("Weird exception");
        //int a = Int32.Parse("fail");
    }
    catch (specialException e)
    {
        if(e.code != 0)
        {
            WriteLine("E.code isn't 0");
            return;
        }

        throw;
    }

在这种情况下,调用堆栈将指示异常是在 throw 语句处抛出的,而不是在第 16 行。

我会给你一个很好的现实世界的例子,我用它来做:死锁重试循环。

有些 API 很好,并且有特定的 DeadlockException 之类的东西 -- 其他的,比如 SOAP 代理,则不完全是。当您没有时,异常过滤器可以很好地避免需要重新抛出。

int retryCount = 0;

while(true)
{
    try
    {
        // do stuff.
        break;
    }
    catch(Exception ex) when(ex.Message == "Deadlock" && ++retryCount < 10)
    {
        // retry up to 10 times.
        continue;
    }
}

如果发生非死锁异常,或者达到重试限制,这使您不必抛出包装器异常。