如果 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 方法中较早抛出的异常。