使用Exception过滤器的优点是什么,什么时候应该使用它们
本文关键字:什么时候 过滤器 Exception 是什么 使用 | 更新日期: 2023-09-27 18:13:01
通过使用Exception过滤器比较旧的错误处理方式和新的错误处理方式,对我来说使用过滤器的优势到底是什么,我应该什么时候使用它?我是否可以从这个新功能中得到好处?
我读过关于unwind堆栈的内容,但我仍然不明白我们不能在旧方式下处理它的情况。请像我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;
}
我知道这个问题还有其他版本,问题是,这个问题提到的好处我实际上找不到,例如。
在做了一些测试之后,我没有看到两者之间的区别,我仍然看到异常从重新抛出的地方。所以,或者信息没有得到确认,我不理解Exception过滤器(这就是我问的原因),或者我做错了(如果我错了,也请纠正我)。异常过滤器比捕获和重新抛出更可取,因为他们离开堆栈毫发无损。如果稍后异常导致堆栈要被倾倒,你可以看到它最初是从哪里来的,而不是
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;
}
我不明白保罗的回答。他可能是对的,也可能不是。
我绝对不同意亚历山大的回答。这不仅仅是语法糖。纯语法糖意味着它仅仅是一种更简单的编写方式,而执行将不会改变。然而,在这种情况下情况并非如此。正如Thomas Levesque在他的博客中指出的那样,异常过滤器不会展开堆栈。因此,在调试程序时,如果在try块中抛出异常,使用异常过滤器,您将能够看到try块中值的状态。如果您没有使用异常过滤器,您的代码将进入catch块,并且您将丢失有关try块中变量状态的信息。
请注意,我不是在谈论堆栈跟踪(它是与堆栈不同但相关的概念)。除非在catch块中显式地重新抛出异常(如throw exception;
),否则堆栈跟踪将保持不变,其中exception
是捕获的异常。
因此,虽然在某些情况下,您可以认为它可能会或可能不会使您的代码更干净(取决于您对语法的看法),但它确实改变了行为。
异常过滤器已经被添加到 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
,则不会被捕获。
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 (ArgumentException ex) // local
{
if (ex.Source.StartsWith("One"))
{
Console.WriteLine("This error is handled locally");
}
else
{
throw;
}
}