如果 Exception 过滤器的过滤器抛出异常会发生什么
What happens if the filter of an Exception filter throws an exception
我还没有使用过 C# 6,但我想知道....
正如标题所说"What happens if the filter of an Exception filter throws an exception?"。我想真正的答案是 "The filter should be written in such a way that it never throws an exception.",但可以这么说。会不会好像异常发生在catch本身?
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
WriteLine("Filtered handler 1");
}
catch (Exception ex)
{
WriteLine("Filtered handler 2");
}
或
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
WriteLine("Filtered handler 1");
}
编辑:有趣的例子
由于该示例所基于的涉嫌 volatileread 中存在错误,因此删除了该部分。需要进一步调查
你可以试试here。
正如@Habib 正确指出的那样,过滤器只是被跳过了,就好像它从未存在过一样。从那时起,catch 子句将像往常一样工作。上面的例子证明了这一点。
但是,如果您将第二个 catch 子句更改为无法捕获从您的方法中抛出的任何内容的类型,您的程序将因未处理的异常而崩溃。
棘手的细节(错误):如果您通过反射调用包含 try-catch 的方法并且 when
子句抛出异常,那么这个异常将被视为未处理,而不是原始异常。更多信息 here.
编辑:这个问题似乎是由 volatileread 中的错误引起的。请参考poke的回答。以下实验不可信
所以我 运行 进行了一些实验,得出了一些有趣的结果,以阐明这个问题。
使用 http://volatileread.com/utilitylibrary/snippetcompiler?id=7632
检查
public void Main()
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
catch (Exception ex)
{
Console.WriteLine("Filtered handler 2");
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception();
}
Prints out "Filtered handler 2"
public void Main()
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception("MethodThatThrowsAnException");
}
打印出来:
Unhandled Expecption: System.Exception: MethodThatThrowsAnException
at Program.MethodThatThrowsAnException() at Program.Main()
的另一个有趣输出
public void Main()
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when(MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
catch (Exception ex) when(MethodThatThrowsAnException2())
{
Console.WriteLine("Filtered handler 2");
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception("MethodThatThrowsAnException");
}
private bool MethodThatThrowsAnException2()
{
throw new Exception("MethodThatThrowsAnException2");
}
Unhandled Expecption: System.Exception: MethodThatThrowsAnException2
at Program.MethodThatThrowsAnException2() at Program.Main()
所以它似乎试图评估第一个捕获,如果它抛出异常它继续下一个捕获。第一个没有失败并匹配所有条件的捕获然后处理异常(顺便说一句,一个最初在 try 中抛出的类型的异常)。但是,如果属于抛出错误类型的最后一个捕获也在过滤器部分中抛出异常,则将抛出过滤器中类型的未处理异常。
编辑:
注:
public void Main()
{
try
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
}
catch (Exception ex)
{
Console.WriteLine("Caught");
Console.WriteLine(ex);
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception("MethodThatThrowsAnException");
}
输出:
Caught
System.Exception: Forced Exception at Program.Main()
如果你将它与第二个输出进行比较...这怎么可能???
在第二个示例中抛出 MethodThatThrowsAnException 但在最后一个示例中 "Forced Exception" 被捕获
如果过滤器中抛出异常,那么该异常将被静静地吞下,过滤器就会失败。这会导致原始异常在 catch
个案例中下降或最终被向上重新提出。
因此调用过滤器的代码无法知道您的过滤器方法中实际上存在异常。因此,重要的是要避免可能引发异常的情况,以确保过滤器不会因此而失败。
您可以在 volatileread.com’s C# 6 beta interpreter 上使用以下代码验证这一点:
public void Main ()
{
try
{
try
{
throw new Exception("Original exception");
}
catch (Exception ex)
when (Test()) // `if (Test())` in older previews
{
Console.WriteLine("Caught the exception");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public static bool Test ()
{
throw new Exception("Exception in filter condition");
}
这导致 “原始异常” 出现在外部 try/catch 块中。
更新
由于在不使用外部 try/catch 块时我不理解 volatileread 编译器的输出,因此我自己安装了 MS Build Tools 2015(截至本回答时仍使用 if
,而不是 when
) 并试了一下。事实证明,当不使用外层try/catch时,“原始异常”仍然是导致程序崩溃的异常。所以这不是过滤器异常。这似乎是 volatile 编译器的错误。
异常过滤器中抛出的异常将被忽略并导致过滤器失败。
Exception filters 是 CLR 自 v1.0 以来的功能。早些时候他们可以使用 VB.Net F#。 就过滤器方法中的 swallowing/ignoring 异常而言,这也是一种定义的行为,近期不太可能改变。
原始异常将向下移动到其他 catch 块,或者在未被任何捕获的情况下保持未处理状态。
考虑以下代码示例:
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) if (MethodThatThrowsAnException())
{
WriteLine("Filtered handler 1");
}
catch (IndexOutOfRangeException ex)
{
WriteLine("Index Out of Range");
}
和方法:
static bool MethodThatThrowsAnException()
{
throw new IndexOutOfRangeException("Index Out of Range");
}
尽管该方法抛出 IndexOutOfRangeException
并且调用者代码中有一个 catch
块用于该特定异常,但该方法的异常永远不会到达调用者。来自过滤器方法的 IndexOutOfRangeException
将被忽略,并且由于第一行的原始异常 throw new Exception("Forced Exception");
未在任何地方处理,程序将因未处理的异常 "Forced Exception" 而崩溃。
在您的第一个代码片段中,由于您有基 Exception
的 catch 块,因此第一行 throw new Exception("Forced Exception");
中的原始异常将在那里被捕获和处理。您不会注意到 filter 方法中较早抛出的异常。
我还没有使用过 C# 6,但我想知道....
正如标题所说"What happens if the filter of an Exception filter throws an exception?"。我想真正的答案是 "The filter should be written in such a way that it never throws an exception.",但可以这么说。会不会好像异常发生在catch本身?
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
WriteLine("Filtered handler 1");
}
catch (Exception ex)
{
WriteLine("Filtered handler 2");
}
或
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
WriteLine("Filtered handler 1");
}
编辑:有趣的例子 由于该示例所基于的涉嫌 volatileread 中存在错误,因此删除了该部分。需要进一步调查
你可以试试here。
正如@Habib 正确指出的那样,过滤器只是被跳过了,就好像它从未存在过一样。从那时起,catch 子句将像往常一样工作。上面的例子证明了这一点。
但是,如果您将第二个 catch 子句更改为无法捕获从您的方法中抛出的任何内容的类型,您的程序将因未处理的异常而崩溃。
棘手的细节(错误):如果您通过反射调用包含 try-catch 的方法并且 when
子句抛出异常,那么这个异常将被视为未处理,而不是原始异常。更多信息 here.
编辑:这个问题似乎是由 volatileread 中的错误引起的。请参考poke的回答。以下实验不可信
所以我 运行 进行了一些实验,得出了一些有趣的结果,以阐明这个问题。
使用 http://volatileread.com/utilitylibrary/snippetcompiler?id=7632
检查public void Main()
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
catch (Exception ex)
{
Console.WriteLine("Filtered handler 2");
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception();
}
Prints out "Filtered handler 2"
public void Main()
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception("MethodThatThrowsAnException");
}
打印出来:
的另一个有趣输出Unhandled Expecption: System.Exception: MethodThatThrowsAnException
at Program.MethodThatThrowsAnException() at Program.Main()
public void Main()
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when(MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
catch (Exception ex) when(MethodThatThrowsAnException2())
{
Console.WriteLine("Filtered handler 2");
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception("MethodThatThrowsAnException");
}
private bool MethodThatThrowsAnException2()
{
throw new Exception("MethodThatThrowsAnException2");
}
Unhandled Expecption: System.Exception: MethodThatThrowsAnException2 at Program.MethodThatThrowsAnException2() at Program.Main()
所以它似乎试图评估第一个捕获,如果它抛出异常它继续下一个捕获。第一个没有失败并匹配所有条件的捕获然后处理异常(顺便说一句,一个最初在 try 中抛出的类型的异常)。但是,如果属于抛出错误类型的最后一个捕获也在过滤器部分中抛出异常,则将抛出过滤器中类型的未处理异常。
编辑: 注:
public void Main()
{
try
{
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) when (MethodThatThrowsAnException())
{
Console.WriteLine("Filtered handler 1");
}
}
catch (Exception ex)
{
Console.WriteLine("Caught");
Console.WriteLine(ex);
}
}
private bool MethodThatThrowsAnException()
{
throw new Exception("MethodThatThrowsAnException");
}
输出:
Caught
System.Exception: Forced Exception at Program.Main()
如果你将它与第二个输出进行比较...这怎么可能??? 在第二个示例中抛出 MethodThatThrowsAnException 但在最后一个示例中 "Forced Exception" 被捕获
如果过滤器中抛出异常,那么该异常将被静静地吞下,过滤器就会失败。这会导致原始异常在 catch
个案例中下降或最终被向上重新提出。
因此调用过滤器的代码无法知道您的过滤器方法中实际上存在异常。因此,重要的是要避免可能引发异常的情况,以确保过滤器不会因此而失败。
您可以在 volatileread.com’s C# 6 beta interpreter 上使用以下代码验证这一点:
public void Main ()
{
try
{
try
{
throw new Exception("Original exception");
}
catch (Exception ex)
when (Test()) // `if (Test())` in older previews
{
Console.WriteLine("Caught the exception");
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public static bool Test ()
{
throw new Exception("Exception in filter condition");
}
这导致 “原始异常” 出现在外部 try/catch 块中。
更新
由于在不使用外部 try/catch 块时我不理解 volatileread 编译器的输出,因此我自己安装了 MS Build Tools 2015(截至本回答时仍使用 if
,而不是 when
) 并试了一下。事实证明,当不使用外层try/catch时,“原始异常”仍然是导致程序崩溃的异常。所以这不是过滤器异常。这似乎是 volatile 编译器的错误。
异常过滤器中抛出的异常将被忽略并导致过滤器失败。
Exception filters 是 CLR 自 v1.0 以来的功能。早些时候他们可以使用 VB.Net F#。 就过滤器方法中的 swallowing/ignoring 异常而言,这也是一种定义的行为,近期不太可能改变。
原始异常将向下移动到其他 catch 块,或者在未被任何捕获的情况下保持未处理状态。
考虑以下代码示例:
try
{
throw new Exception("Forced Exception");
}
catch (Exception ex) if (MethodThatThrowsAnException())
{
WriteLine("Filtered handler 1");
}
catch (IndexOutOfRangeException ex)
{
WriteLine("Index Out of Range");
}
和方法:
static bool MethodThatThrowsAnException()
{
throw new IndexOutOfRangeException("Index Out of Range");
}
尽管该方法抛出 IndexOutOfRangeException
并且调用者代码中有一个 catch
块用于该特定异常,但该方法的异常永远不会到达调用者。来自过滤器方法的 IndexOutOfRangeException
将被忽略,并且由于第一行的原始异常 throw new Exception("Forced Exception");
未在任何地方处理,程序将因未处理的异常 "Forced Exception" 而崩溃。
在您的第一个代码片段中,由于您有基 Exception
的 catch 块,因此第一行 throw new Exception("Forced Exception");
中的原始异常将在那里被捕获和处理。您不会注意到 filter 方法中较早抛出的异常。