使用异常过滤器有什么好处,我应该在什么时候使用它们?
What is the advantage of using Exception filters and when should I use them?
比较旧的错误处理方式和新的错误处理方式,通过使用异常过滤器,使用过滤器对我来说到底有什么优势,我应该在什么时候使用它?是否存在可以充分利用此新功能的场景?
我已经阅读了有关展开堆栈的信息,但我仍然不明白我们无法在旧方法下处理它的情况。请像我 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 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();
//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;
}
UPD: 正如 Paulo Morgado 在回答中指出的那样,该功能已经在 CLR 中存在了相当长的一段时间,而 C# 6.0 仅为其添加了语法支持。然而,我对它的理解仍然是一种语法糖,例如允许我以比以前更好的方式过滤异常的语法,无论以前的 "straightforward" 方法如何在幕后工作。
=====
据我了解,这是一种语法糖,可让您更清楚地定义要处理异常的块。
考虑以下代码:
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#,因为它们在 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
.
就不会被捕获
我不明白保罗的回答。他可能是对的,也可能不是。
我绝对不同意亚历山大的回答。它不仅仅是语法糖。纯粹的语法糖意味着它只是一种更简单的编写方式,并且执行将保持不变。
然而,在这种情况下情况并非如此。正如 Thomas Levesque points out in his blog,异常过滤器不会展开堆栈。因此,在调试程序时,如果在 try 块中抛出异常,使用异常过滤器,您将能够看到 try 块中值的状态。如果您没有使用异常过滤器,您的代码将进入 catch 块,您将丢失有关 try 块中变量状态的信息。
请注意,我不是在谈论堆栈跟踪(它是与堆栈不同但相关的概念)。除非您明确地在 throw exception;
中重新抛出异常,否则堆栈跟踪将保持不变,其中 exception
是捕获的异常。
因此,虽然在某些情况下您可以认为它可能会使您的代码更清晰,也可能不会(取决于您对语法的看法),但它确实改变了行为。
比较旧的错误处理方式和新的错误处理方式,通过使用异常过滤器,使用过滤器对我来说到底有什么优势,我应该在什么时候使用它?是否存在可以充分利用此新功能的场景?
我已经阅读了有关展开堆栈的信息,但我仍然不明白我们无法在旧方法下处理它的情况。请像我 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 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();
//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;
}
UPD: 正如 Paulo Morgado 在回答中指出的那样,该功能已经在 CLR 中存在了相当长的一段时间,而 C# 6.0 仅为其添加了语法支持。然而,我对它的理解仍然是一种语法糖,例如允许我以比以前更好的方式过滤异常的语法,无论以前的 "straightforward" 方法如何在幕后工作。
=====
据我了解,这是一种语法糖,可让您更清楚地定义要处理异常的块。
考虑以下代码:
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#,因为它们在 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
.
我不明白保罗的回答。他可能是对的,也可能不是。
我绝对不同意亚历山大的回答。它不仅仅是语法糖。纯粹的语法糖意味着它只是一种更简单的编写方式,并且执行将保持不变。
然而,在这种情况下情况并非如此。正如 Thomas Levesque points out in his blog,异常过滤器不会展开堆栈。因此,在调试程序时,如果在 try 块中抛出异常,使用异常过滤器,您将能够看到 try 块中值的状态。如果您没有使用异常过滤器,您的代码将进入 catch 块,您将丢失有关 try 块中变量状态的信息。
请注意,我不是在谈论堆栈跟踪(它是与堆栈不同但相关的概念)。除非您明确地在 throw exception;
中重新抛出异常,否则堆栈跟踪将保持不变,其中 exception
是捕获的异常。
因此,虽然在某些情况下您可以认为它可能会使您的代码更清晰,也可能不会(取决于您对语法的看法),但它确实改变了行为。