This project is read-only.

Exception filtering syntax

Topics: C# Language Design
Nov 20, 2014 at 9:39 PM
Edited Nov 20, 2014 at 9:42 PM
I'm a little late to the party (especially since the feature is marked done, if I recall), but I think the syntax for exception filtering has a pretty serious flaw.

Consider the following catch snippets:
catch (Exception ex)
{
    if (Condition(ex))
    {
        Console.WriteLine("match");
    }
}


catch (Exception ex)
    if (Condition(ex)) {
        Console.WriteLine("match");
    }
These obviously have very different behaviors, though to someone less familiar with the new features, they appear to be quite identical. The only difference appears to be a naked statement vs. the same statement enclosed in braces. Having two separate behaviors based only a difference in bracing is inconsistent with every other situation where bracing is optional in C#, making this situation a point of confusion/landmine for developers that are not fully aware of the new feature, and creating a very likely point of confusion when reading the code.

There is not enough differentiation in syntax to make the very significant difference in behavior apparent, and the need for close inspection here is inconsistent with everything else in C#.

My suggestion would be to move the conditional expression into the parenthesis of the catch statement:
catch (Exception ex, Condition(ex))
{
    Console.WriteLine("match");
}
Readers unfamiliar with exception filtering are unlikely to think "hey, I guess braces are optional on catch blocks now," instead of realizing that there is a language change; and more importantly, readers familiar with the feature are given a strong indication from the syntax that the condition is part of the filter and not part of the action.

Before this feature, the filter is entirely contained in the parenthesis (only the type of exception), and the action is entirely contained in the braces. Adding additional content between the filter and action is confusing. Adding the conditional portion of the filter within the parenthesis is more intuitive, because that is where the filter is already set and belongs.

In any case... thanks for reading. Hope its at least worthy of a brief discussion before someone gets to "but... the feature is marked as done." :)
Nov 20, 2014 at 9:44 PM
danwyand wrote:
I'm a little late to the party (especially since the feature is marked done, if I recall), but I think the syntax or exception filtering has a pretty serious flaw.

Consider the following catch snippets:
catch (Exception ex)
{
    if (Condition(ex))
    {
        Console.WriteLine("match");
    }
}


catch (Exception ex)
    if (Condition(ex))
        Console.WriteLine("match");

I believe the second option wouldn't be legal syntax. A block would be required after the condition, e.g.:
catch (Exception ex)
    if (Condition(ex)) {
        Console.WriteLine("match");
    }
    // uh oh, what happened to the exception?
I do get your point, though. Despite the fact that catch always requires a block enclosed in braces it may be initially expected that the if condition is the body of the catch block and not a separate condition.
Nov 20, 2014 at 9:52 PM
My understanding is that with 6.0 exception filtering, the following snippet (Condition(ex) changed to plain ol false):
catch (Exception ex)
    if (false) {
        Console.WriteLine("match");
    }
will result in the exception being unhandled. Very different from what would happen if the if statement had braces around it; there would be no message, but the exception would be handled. That is why I think the syntax needs some improvement to make the behaviors more distinct.
Nov 20, 2014 at 11:05 PM
danwyand wrote:
My understanding is that with 6.0 exception filtering, the following snippet (Condition(ex) changed to plain ol false):
catch (Exception ex)
    if (false) {
        Console.WriteLine("match");
    }
will result in the exception being unhandled. Very different from what would happen if the if statement had braces around it; there would be no message, but the exception would be handled. That is why I think the syntax needs some improvement to make the behaviors more distinct.
It would. My point only being that said code isn't valid C# 5.0 since catch blocks require braces. I think that you are right that it can be confusing for people who aren't very aware of that fact.

I'd probably prefer either changing if to when or moving the condition into the parenthesis, e.g.:
catch (Exception ex) when (SomeFunction(ex)) {
   ...
}

catch (Exception ex if (SomeFunction (ex)) {
   ...
}
The issue with the former being that it introduces yet another new keyword but at least the context could not result in breaking existing code. Neither would the latter given that if can't be used as an identifier.

May be too late to raise these concerns, though.
Nov 20, 2014 at 11:27 PM
I concur with the concerns about the syntax. From a code-formatting standpoint, encouraging the convention that the condition should be on the same line as the catch would somewhat lessen the ambiguity, but either using a context-sensitive keyword like when instead of if, or else adding a context-sensitive keyword word like only before the if would seem like simple ways to avoid possible misunderstandings.

Additionally, it might be good to consider what pattern should be encouraged for use when a block never expects to resolve an exception, but should nonetheless have finally block code know about any exception that might have been thrown within the try? While the following has pretty good semantics, it's pretty ugly:
Exception thrownEx = null;
try
{
  ... do main try stuff
}
catch (Exception ex) if (CopyFirstArgumentToSecondAndReturnFalse(ex, out thrownEx))
{
}
finally
{
  try
  {
    ... do some cleanup
  }
  catch(Exception ex) if (thrownEx != null && ex.LogOriginalExceptionAndIndicateWhetherToFavorItOverNewOne(thrownEx)) // Extension method)
  {
  }
}
Note that the semantics of this have a slight flaw, which is that in some really weird scenarios it would be possible for thrownEx to be non-null even when the original try-block completes normally. Of greater interest in this example, however, is the fact that the first catch block has no hope of ever actually "catching" an exception, and even the second will (at least in theory) never result in the enclosing block exiting normally (it should only suppress the new exception if a more important exception would already be pending when the finally-block code completes).

I'm probably too late in suggesting this, but I wonder if it might be helpful to have some alternative keyword instead of catch for use in cases where a filter is needed to let code know that an exception occurred, even though the catch block will never actually execute (or, perhaps, that as in the second example it will only execute to avoid stifling another exception).
Nov 20, 2014 at 11:28 PM
Halo_Four wrote:
catch (Exception ex if (SomeFunction (ex)) {
Hadn't thought of that one, but I like it.
Nov 21, 2014 at 8:39 AM
Should we also change
if (bool)
{
}
else if (bool)
{
}
to
if (bool)
{
}
elseif (bool)
{
}
because someone could write
if (bool)
{
}
else
    if (bool)
    {
    }
or
if (bool)
{
}
else
{
    if (bool)
    {
    }
}
Imho the current Syntax is totally fine and i would require folks to pay a little attention when writing code and doing it not drunk ;)
Nov 21, 2014 at 12:29 PM
Suchiman wrote:
Should we also change
if (bool)
{
}
else if (bool)
{
}
to
if (bool)
{
}
elseif (bool)
{
}
because someone could write
if (bool)
{
}
else
    if (bool)
    {
    }
or
if (bool)
{
}
else
{
    if (bool)
    {
    }
}
Imho the current Syntax is totally fine and i would require folks to pay a little attention when writing code and doing it not drunk ;)
Bad analogy, your samples work identically and despite the lack of braces that is considered proper syntax for else if. In the above examples the two blocks of code are semantically different and only distinguishable if you are aware of the fact that catch blocks require braces.
Nov 21, 2014 at 4:10 PM
Suchiman wrote:
Should we also change
else if (bool)
to
elseif (bool)
That would be appropriate if the semantic or binding behavior of the two constructs differed. Note that in C# the behavior of folloowing a #else by an #if does differ from the behavor of #elif, and thus a separate keyword is used in the latter case.
Nov 24, 2014 at 8:12 AM
Halo_Four wrote:
```cs
catch (Exception ex) when (SomeFunction(ex)) {
...
}
The issue with the former being that it introduces yet another new keyword but at least the context could not result in breaking existing code.
Why not reuse the existing where keyword?
catch (Exception ex) 
    where ex is CommunicationException ||
          ex is TimeoutException {
        client.Abort()
}
Nov 24, 2014 at 11:28 AM
madrian wrote:
Halo_Four wrote:
```cs
catch (Exception ex) when (SomeFunction(ex)) {
...
}
The issue with the former being that it introduces yet another new keyword but at least the context could not result in breaking existing code.
Why not reuse the existing where keyword?
catch (Exception ex) 
    where ex is CommunicationException ||
          ex is TimeoutException {
        client.Abort()
}
Because if makes more sense?
Nov 24, 2014 at 4:01 PM
madrian wrote:
Why not reuse the existing where keyword?
catch (Exception ex) 
    where ex is CommunicationException ||
          ex is TimeoutException {
        client.Abort()
}
I really like this. It does a good job of distinguishing between the behaviors, since the braces aren't the most important part of the syntax anymore. Bonus points for being something that (maybe) only requires some minor changes to existing code, since the exception filtering is already implemented (at least I think it might... is where a proper keyword or a contextual keyword?).



PauloMorgado wrote:
Because if makes more sense?
How does "if" make more sense? Need a bit info than that for this to be good feedback.
Nov 24, 2014 at 5:40 PM
danwyand wrote:
Why not reuse the existing where keyword?
I really like this. It does a good job of distinguishing between the behaviors, since the braces aren't the most important part of the syntax anymore. Bonus points for being something that (maybe) only requires some minor changes to existing code, since the exception filtering is already implemented (at least I think it might... is where a proper keyword or a contextual keyword?).
How much difference is there between having one sequence of letters serve as a context-sensitive keyword in two unrelated contexts, versus having different sequences of letters for the different contexts? In cases where it will syntactically necessary to use a reserved word [e.g. the yield return construct includes a reserved word (return) to ensure that the existence of an identifier called yield won't create ambiguity. If the construct had been simply yield foo;, and at class scope there had been a type named yield and a field named foo, then yield foo could either mean "use the contents of class field foo as the next item in the sequence" or "create a new local variable named foo of type yield].

If a new language construct will need to include a reserved word, it's MUCH better to use an existing reserved word than add a new one to the language. On the other hand, I don't think adding context-sensitive keywords to a language will clutter up any sort of identifier space. In cases where a letter sequence that serves as an existing context-sensitive keyword would seem the most natural choice for a feature, it may as well be used, but if some other letter sequence would be semantically clearer, I'd see no reason to favor the pre-existing sequence.

VB.NET uses "When", which (aside from being PascalCase) seems more semantically accurate than where. Would using the latter have any compiler-implementation advantages I don't know about?
Nov 24, 2014 at 5:50 PM
To me, the difference between "where" and "if" is that "if" has two different behaviors in very similar syntax. There would be no misunderstanding with a "where" replacing an "if" in the exception filter clause... the "where" is clearer.

For "when," I think its best to avoid adding new keywords whenever possible, and I'm not sure "when" has any inherent advantage over "where".

My comment on this suggestion possibly being easy to implement (as a change to the existing syntax) probably was not altogether constructive... it was just meant as ease of implementation in the compiler, not anything that made it a better solution. It was just the assumption that switching out a keyword in mostly the same syntax may be simpler than going for a whole new syntax. Also, not sure that holds water since "if" is a proper reserved word and "where" is a contextual keyword.

In any case, I think the change from "if" to "where" is good because it is a minimal change, it is clear, and it eliminates the ambiguity that comes from the "if" keyword.
Nov 24, 2014 at 10:06 PM
danwyand wrote:
For "when," I think its best to avoid adding new keywords whenever possible, and I'm not sure "when" has any inherent advantage over "where".
I guess I tend to subscribe to the opposite philosophy: it's best to avoid overloading keywords for unrelated purposes absent a compelling need to do so. If exception filters used the formerly-unused keyword "when", then a question like "what kinds of expressions can be used with when" would have a clear meaning. By contrast, if filters and LINQ both use where, then such a question would be ambiguous.
My comment on this suggestion possibly being easy to implement (as a change to the existing syntax) probably was not altogether constructive... it was just meant as ease of implementation in the compiler, not anything that made it a better solution. It was just the assumption that switching out a keyword in mostly the same syntax may be simpler than going for a whole new syntax. Also, not sure that holds water since "if" is a proper reserved word and "where" is a contextual keyword.
I agree that changing the if to either when or where may be easier than changing the sequence of tokens. I don't have any particularly strong preference beyond the fact that it should be something which doesn't look like any pre-existing construct. From a human-language-linguistic standpoint, I think where would imply that acceptance or non-acceptance would be based upon features of the exception itself; when would imply that a catch would at times be active and at times inactive. Depending upon what kind of condition was being tested, either term could make more sense. For example, one may wish to only catch exceptions which implement some particular interface (such exceptions might not have a clear hierarchical relationship among each other), or only catch exceptions at times when there's already a pending exception working its way up the stack outside the immediate context (and the outer exception would be lost if the immediate exception weren't caught). Using "where" to test a condition which has nothing whatsoever to do with the present exception being thrown would feel a little odd, but not horrible. Still an improvement over if.