【问题标题】:What is the advantage of using Exception filters and when should I use them?使用异常过滤器有什么好处,我应该什么时候使用它们?
【发布时间】:2015-12-13 17:09:26
【问题描述】:

比较旧的错误处理方式和新的错误处理方式,通过使用异常过滤器,使用过滤器对我来说究竟有什么优势,我应该在什么时候使用它?有没有一种情况可以让我充分利用这项新功能?

我已经阅读了有关展开堆栈的信息,但我仍然没有得到我们无法以旧方式处理它的情况。请像我 5 岁一样解释一下。

try
{
    Foo.DoSomethingThatMightFail(null);
}
catch (MyException ex) when (ex.Code == 42)
{
    Console.WriteLine("Error 42 occurred");
}

try
{
    Foo.DoSomethingThatMightFail(null);
}
catch (MyException ex)
{
    if (ex.Code == 42)
        Console.WriteLine("Error 42 occurred");
    else
        throw;
}

我知道这个问题还有其他版本,问题是,问题提到了我实际上找不到的好处。

异常过滤器比捕获和重新抛出更可取,因为 他们让堆栈安然无恙。如果后来异常导致堆栈 要被倾倒,你可以看到它最初来自哪里,而不是 只是它被重新抛出的最后一个地方。

在做了一些测试之后,我没有看到两者之间的区别,我仍然从它被重新抛出的地方看到异常。所以,或者信息没有得到确认,我不理解异常过滤器(这就是我问的原因),或者我做错了(如果我错了也请纠正我)。

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();
   //throw new Exception("Weird exception");
   //int a = Int32.Parse("fail");
}
catch (specialException e) when(e.code == 0)
        {
            WriteLine("E.code 0");
            throw;
            //throw e;
        }
catch (FormatException e) 
        {
            if (cond1)
            {
                WriteLine("cond1 " + e.GetBaseException().Message+" - "+e.StackTrace);
                throw;
            }
            throw;
        }
catch (Exception e) //when (cond2)
        {
            Console.WriteLine("cond2! " + e.Message);
            throw;
        }

【问题讨论】:

  • 您在哪里找到“例外过滤器更可取...”这句话?
  • 在他们说我重复的问题中。 (stackoverflow.com/questions/27082069/…)
  • Thomas Levesque 写得很好here。你读过它吗?他提出了一些很好的观点,因为我在脑海中交替使用堆栈和堆栈跟踪,他的帖子为我清除了这一点。
  • 是的,非常详细,谢谢!我认为这回答了我的问题。
  • 你可能想问他是否可以在这里复制他的答案,或者邀请他这样做。

标签: c# .net exception-handling c#-6.0 .net-4.6


【解决方案1】:

我不明白保罗的回答。他可能是正确的,也可能不是。

我绝对不同意亚历山大的回答。它不仅仅是语法糖。纯语法糖意味着它只是一种更简单的编写方式,并且执行将保持不变。

但是,在这种情况下并非如此。正如 Thomas Levesque points out in his blog 一样,异常过滤器不会展开堆栈。因此,在调试程序时,如果在 try 块中抛出异常,使用异常过滤器,您将能够看到 try 块中值的状态。如果您没有使用异常过滤器,您的代码将进入 catch 块,您将丢失有关 try 块中变量状态的信息。

请注意,我不是在谈论堆栈跟踪(它是与堆栈不同但相关的概念)。堆栈跟踪将保持不变,除非您在 exception 是捕获的异常的 catch 块中明确重新抛出异常,如 throw exception;

因此,虽然在某些情况下,您可以将其视为可能会或可能不会使您的代码更清晰的东西(取决于您对语法的看法),但它确实会改变行为。

【讨论】:

    【解决方案2】:

    异常过滤器已添加到 C# 中,因为它们在 Visual Basic 中,“Roslyn”团队发现它们在开发“Roslyn”时很有用。

    请注意,过滤器在throw 的上下文中运行,而不是在catch 的上下文中。

    无论如何,一种用途可能是这样的:

    try
    {
        //...
    }
    catch (SqlException ex) when (ex.Number == 2)
    {
        // ...
    }
    catch (SqlException ex)
    {
        // ...
    }
    

    已编辑:

    有人可能认为这只是语法糖:

    try
    {
        //...
    }
    catch (SqlException ex) when (ex.Number == 2)
    {
        // ...
    }
    catch (SqlException ex)
    {
       if (ex.Number == 2)
       {
           // ...
       }
       else
       {
           // ...
       }
    }
    

    但是如果我们为此更改代码:

    try
    {
        //...
    }
    catch (SqlException ex) when (ex.Number == 2)
    {
        // ...
    }
    

    会更像这样:

    try
    {
        //...
    }
    catch (SqlException ex) when (ex.Number == 2)
    {
        // ...
    }
    catch (SqlException ex)
    {
       if (ex.Number == 2)
       {
           // ...
       }
       else
       {
           throw
       }
    }
    

    但有一个根本区别。如果ex.Number 不是2,则不会捕获并重新抛出异常。如果ex.Number 不是2,则不会被捕获。

    【讨论】:

    • 你是什么意思“在投掷的上下文中运行”?这对我来说似乎不是很清楚。
    • 抛出的代码可能在模拟用户下运行,或者具有不同的 CAS 权限,例如。
    【解决方案3】:

    UPD: 正如 Paulo Morgado 在回答中指出的那样,该功能已经在 CLR 中使用了相当长的一段时间,而 C# 6.0 仅为其添加了语法支持。然而,我对它的理解仍然是一种语法糖,例如允许我以比以前更好的方式过滤异常的语法,无论以前的“直截了​​当”的方法如何在后台工作。

    =====

    据我了解,这是一种语法糖,可让您更清楚地定义要处理的异常块。

    考虑以下代码:

    try
    {
        try
        {
            throw new ArgumentException() { Source = "One" };
            throw new ArgumentException() { Source = "Two" };
            throw new ArgumentException() { Source = "Three" };
        }
        catch (ArgumentException ex) when (ex.Source.StartsWith("One")) // local
        {
            Console.WriteLine("This error is handled locally");
        }
        catch (ArgumentException ex) when (ex.Source.StartsWith("Two")) // separate
        {
            Console.WriteLine("This error is handled locally");
        }
    
    }
    catch (ArgumentException ex) // global all-catcher
    {
        Console.WriteLine("This error is handled globally");
    }
    

    在这里您可以清楚地看到,第一个和第二个异常是在使用when 安全措施分隔的各个块中处理的,而一个全局 catch-all 块将仅捕获第三个异常。捕获每个块中的所有异常的语法更清晰,例如:

        catch (ArgumentException ex)  // local
        {
                        if  (ex.Source.StartsWith("One")) 
                        {
                            Console.WriteLine("This error is handled locally");
                        } 
                        else 
                        {
                             throw;
                        }
    
        }
    

    【讨论】:

    • 你的第一段是错误的。这是 C# 语言现在支持的 CLR 功能。
    • @PauloMorgado 嗯?为什么第一段错了?他没有说它是 CLR 功能还是 C# 功能。
    • 你对“合成糖”的理解是什么?对我来说,它使做一些可以用另一种方式完成的事情变得容易。在 C# 中,您无法以任何其他方式执行异常过滤器所做的事情。
    • @PauloMorgado 感谢您向 CLR 指出!然而,用户并没有问这个功能是如何实现的,而是他应该在什么时候使用它。而且,根据维基百科,“语法糖”的定义非常适合这里,因为它没有说明实现,只说明用于表达某些概念的语法。
    • @AlexanderGalkin,我建议您了解一下该功能是什么以及它是如何工作的,以便就可能的用例提供建议。无论维基百科怎么说,您对这个答案的肯定和代码示例实际上都是错误的。
    猜你喜欢
    • 2011-05-05
    • 2021-09-07
    • 2010-12-19
    • 2011-02-21
    • 2023-04-02
    • 1970-01-01
    • 2011-05-25
    • 2014-09-29
    • 1970-01-01
    相关资源
    最近更新 更多