Reputation: 1232
I was reading up on filter expressions using catch(..) when (..)
in C# and I couldn't find any mention of what happens if an exception is thrown while evaluating the expression. I tried going through the docs and spec but either I couldn't find them or they are not mentioned. So, I made up a tiny program to check what happens when I throw from when(..)
. I put it up on https://dotnetfiddle.net/R7oJNp
try {
Console.WriteLine("Test(): Inside Test()");
throw new Exception("thrown from Test()");
} catch (Exception) when (AlwaysThrow()) {
Console.WriteLine("Test(): caught exception in Test()");
} catch (Exception e) {
Console.WriteLine("Test(): totally skipped last catch clause, any inner exception: " + (e.InnerException?.Message ?? "nothing"));
throw;
}
What I'm noticing is that the entire catch(..) when (..)
block is skipped if an exception is thrown inside when(..)
. It skips to the next matching catch
block and upon inspecting the exception object caught, there's no trace of the exception that originated from when(..)
.
I wanted to know if this is the expected behavior and if there's a reference to the docs/spec regarding what's supposed to happen in this scenario. It seems odd to skip the entire block and throw away the exception because that'd make it very hard to debug.
**Edit: ** Okay, this is the behavior according to .NET docs but is there any way to trace these? I'm onboard with the idea that exceptions must not happen and filters should be simple enough but we make mistakes. Also, isn't this behavior supposed to be mentioned in C# spec?
Upvotes: 7
Views: 407
Reputation: 113342
The exception gets swallowed and the filter doesn't match (as if it returned false):
void Main()
{
UseFilterThenCatch();
UseFilterOnly();
}
public static bool BadFilter() => throw new Exception("This will be thrown by the filter");
public static void UseFilterThenCatch()
{
try
{
throw new Exception("The exception to catch.");
}
catch(Exception) when (BadFilter())
{
Console.WriteLine("Filtered");
}
catch(Exception e)
{
Console.WriteLine("Unfiltered"); // This line gets hit.
Console.WriteLine(e.Message); // This proves it's the first exception thrown.
}
}
public static void UseFilterOnly()
{
try
{
try
{
throw new Exception("The exception to catch.");
}
catch (Exception) when (BadFilter())
{
Console.WriteLine("Filtered");
}
}
catch(Exception e)
{
Console.WriteLine("Outer catch");
Console.WriteLine(e.Message); // This proves it's the first exception thrown.
}
}
While this is language-specific, and a .NET language could conceivably do something else, it's part and parcel to how filters work in the CLR so it's what the most natural corresponding CIL would do. The same thing also happens with VB.NET and with Linq expressions (which took a bit of deliberate work in the case of interpreted expressions, but happened easily enough with compiled expressions, since again it's just what the most natural corresponding CIL does).
Upvotes: 0
Reputation: 11871
Although a C# 6 feature, exception filters have been supported by the CLR since .NET Framework 1.1.
Exceptions in the expression are handled as follows:
If an exception occurs during execution of the user-filtered expression, that exception overwrites the current exception. In this case, the common language runtime abandons the search for a handler for the current exception, the finally block of the try construct executes, and a search begins outside the current try construct for a handler of the new exception.
Upvotes: 0
Reputation: 11514
VB.Net has supported this long before C# so you may be searching too narrowly:
If an exception occurs during execution of the user-filtered expression, that exception is discarded and the filter expression is considered to have evaluated to false.
https://learn.microsoft.com/en-us/dotnet/standard/exceptions/using-user-filtered-exception-handlers
Upvotes: 5