通过使用异常过滤器来比较旧方式与新方式的错误处理,使用过滤器的优势是什么?我何时应该使用它?有没有一个场景我可以很好地利用这个新功能?
我已经阅读了有关展开堆栈的信息,但我仍然没有得到我们无法按照旧方式处理的情况。请解释我喜欢的事。
try
{
Foo.DoSomethingThatMightFail(null);
}
catch (MyException ex) when (ex.Code == 42)
{
Console.WriteLine("Error 42 occurred");
}
VS
try
{
Foo.DoSomethingThatMightFail(null);
}
catch (MyException ex)
{
if (ex.Code == 42)
Console.WriteLine("Error 42 occurred");
else
throw;
}
我知道这个问题有其他版本,问题是,这个问题提到了我实际上无法找到的好处,例如。
异常过滤器比捕获和重新抛出更可取,因为 他们没有受到伤害。如果异常后来导致堆栈 要被倾倒,你可以看到它最初的来源,而不是 只是最后一个地方被重新抛出。
在做了一些测试后,我没有看到两者之间的区别,我仍然看到 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;
}
答案 0 :(得分:3)
我不明白保罗的回答。他可能是正确的,也可能不是。
我绝对不同意亚历山大的回答。它不仅仅是语法糖。纯语法糖意味着它只是一种更简单的写东西的方式,并且执行将保持不变。
然而,在这种情况下情况并非如此。作为Thomas Levesque points out in his blog,异常过滤器不会展开堆栈。因此,在调试程序时,如果在try块中抛出异常,使用异常过滤器,您将能够看到try块中值的状态。如果您没有使用异常过滤器,您的代码将进入catch块,您将丢失有关try块中变量状态的信息。
请注意,我不是在谈论堆栈跟踪(它是与堆栈不同但相关的概念)。堆栈跟踪将保持不变,除非您在throw exception;
是捕获的异常的catch块中明确地重新抛出异常,如exception
中那样。
因此,在某些情况下,您可以将其视为可能会或可能不会使您的代码更清晰的内容(取决于您对语法的看法), 会改变行为。
答案 1 :(得分:0)
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
,则不会被抓住。
答案 2 :(得分:-1)
UPD:正如Paulo Morgado的回答所指出的那样,该功能已经在CLR中使用了很长一段时间,而C#6.0只为其添加了语法支持。然而,我对它的理解仍然是一种语法糖,例如允许我以比以往更好的方式过滤异常的语法,无论前一个&#34;直截了当的&#34;方法在引擎盖下工作。
=====
根据我的理解,这是一个语法糖,可以让你更清楚地定义你的异常将被处理的块。
请考虑以下代码:
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
safeguard分隔的相应块中处理的,而一个全局catch-all块将仅捕获第三个异常。语法更清晰,可以捕获每个块中的所有异常,例如:
catch (ArgumentException ex) // local
{
if (ex.Source.StartsWith("One"))
{
Console.WriteLine("This error is handled locally");
}
else
{
throw;
}
}