Else blocks on try statements

Topics: C# Language Design
Sep 23, 2014 at 5:55 PM
Edited Sep 23, 2014 at 6:03 PM
I'm somewhat new here so I'm not sure if something similar to this has been discussed before.

Basically try statements could have an 'else' block which executes when an exception doesn't get thrown in the main try block.

Rough example off the top of my head:
try {
  using (var f = File.Open("file", FileMode.Open)) {
    ...
  };
} catch (Exception) {
  WriteLine("An error occurred");
} else {
  WriteLine("File opened successfully");
}
Here's how it looks with the other blocks of a try statement:
try {
  WriteLine("Something that may throw an exception");
} catch (IOException) {
  WriteLine("An IOException was thrown");
} catch (Exception) {
  WriteLine("Some other Exception was thrown");
} else {
  WriteLine("No exceptions thrown");
} finally {
  WriteLine("This will happen regardless");
}
Sep 23, 2014 at 8:46 PM
I don't think I like else as a syntax, but I do wish there were an easy way for a finally block to know whether an exception occurred; a simple but very useful syntax for that would be to allow finally to specify a parameter of type ex which would be null if no exception occurred or else indicate an exception. Such an approach would allow for a finally block to perform in any sequence those operations which should happen always, those which should only happen in the "exception" case, and those which should only happen in the "no exceptions" case. It would also make it possible for any exceptions which occur within a finally block to wrap any exception which had occurred in the try block; at present, there's no clean mechanism by which finally code can throw an exception to a method's caller without obliterating without a trace any exception that might be pending. This creates some very awkward situations which could be resolved easily if finally block could know the status of the associated try.
Sep 23, 2014 at 11:34 PM
Yuioppe:

In what is this:
try {
  using (var f = File.Open("file", FileMode.Open)) {
    ...
  };
} catch (Exception) {
  WriteLine("An error occurred");
} else {
  WriteLine("File opened successfully");
}
try {
  WriteLine("Something that may throw an exception");
} catch (IOException) {
  WriteLine("An IOException was thrown");
} catch (Exception) {
  WriteLine("Some other Exception was thrown");
} else {
  WriteLine("No exceptions thrown");
} finally {
  WriteLine("This will happen regardless");
}
any different from this?
try
{
  using (var f = File.Open("file", FileMode.Open)) {
    ...
  };
  WriteLine("File opened successfully");
}
catch (Exception)
{
  WriteLine("An error occurred");
}
try
{
  WriteLine("Something that may throw an exception");
  WriteLine("No exceptions thrown");
}
catch (IOException)
{
  WriteLine("An IOException was thrown");
}
catch (Exception)
{
  WriteLine("Some other Exception was thrown");
}
finally
{
  WriteLine("This will happen regardless");
}

Sep 24, 2014 at 12:23 AM
Edited Sep 24, 2014 at 8:00 PM
I think like PauloMorgado...

Edit: to be more constructive, I don't think adding an else (or any "noexception" block would add that more value. I guess there might be patterns that would actually need it, but I don't see where I would need that. It would be nice if you had an actual real case where you would need it so I/we could see it would really be worth it...
Sep 24, 2014 at 9:20 AM
I think that exceptions thrown in the else clause wouldn't get caught in the catch(Exception) clause.

In your second case, any exception thrown on the
  WriteLine("No exceptions thrown");
line would get caught in
catch (Exception)
{
  WriteLine("Some other Exception was thrown");
}
But in the else case it would get propagated upwards.
Sep 24, 2014 at 4:08 PM
brunomlopes wrote:
I think that exceptions thrown in the else clause wouldn't get caught in the catch(Exception) clause.

In your second case, any exception thrown on the
  WriteLine("No exceptions thrown");
line would get caught in
catch (Exception)
{
  WriteLine("Some other Exception was thrown");
}
But in the else case it would get propagated upwards.
Like his?
try
{
    try
    {
        WriteLine("Something that may throw an exception");
    }
    catch (IOException)
    {
        WriteLine("An IOException was thrown");
    }
    catch (Exception)
    {
        WriteLine("Some other Exception was thrown");
    }
}
catch
{
    WriteLine("No exceptions thrown");
}
finally
{
  WriteLine("This will happen regardless");
}
I get your point.

Not sure if I would like to use this often....
Sep 24, 2014 at 4:54 PM
brunomlopes wrote:
I think that exceptions thrown in the else clause wouldn't get caught in the catch(Exception) clause.
If a term like success were used rather than else, another difference might be the behavior in case there's a return from within the catch; that's not so clear with else, though. On the other hand, a returning block might be a helpful adjunct to else to handle cases where code doesn't throw an exception but won't flow out past the catch block. Of course, there are some cases where a return within a catch is normal, and some where it might break a program's expected invariants. I wonder if there would be a usefulness to having a try all {} block whose interior would be forbidden from containing any interior return statements [the "all" implying that if there's no exception the whole try block should be executed].
Sep 24, 2014 at 7:05 PM
PauloMorgado wrote:
Yuioppe:

In what is this:
try {
  using (var f = File.Open("file", FileMode.Open)) {
    ...
  };
} catch (Exception) {
  WriteLine("An error occurred");
} else {
  WriteLine("File opened successfully");
}
try {
  WriteLine("Something that may throw an exception");
} catch (IOException) {
  WriteLine("An IOException was thrown");
} catch (Exception) {
  WriteLine("Some other Exception was thrown");
} else {
  WriteLine("No exceptions thrown");
} finally {
  WriteLine("This will happen regardless");
}
any different from this?
try
{
  using (var f = File.Open("file", FileMode.Open)) {
    ...
  };
  WriteLine("File opened successfully");
}
catch (Exception)
{
  WriteLine("An error occurred");
}
try
{
  WriteLine("Something that may throw an exception");
  WriteLine("No exceptions thrown");
}
catch (IOException)
{
  WriteLine("An IOException was thrown");
}
catch (Exception)
{
  WriteLine("Some other Exception was thrown");
}
finally
{
  WriteLine("This will happen regardless");
}

The whole point of this is just to have a 'designated area' that is guaranteed to be executed when an exception doesn't occur, in exactly the same way as a finally block. With my example it is functionally the same as what you put, but there may be better use cases for this.
Sep 24, 2014 at 7:07 PM
supercat wrote:
brunomlopes wrote:
I think that exceptions thrown in the else clause wouldn't get caught in the catch(Exception) clause.
If a term like success were used rather than else, another difference might be the behavior in case there's a return from within the catch; that's not so clear with else, though. On the other hand, a returning block might be a helpful adjunct to else to handle cases where code doesn't throw an exception but won't flow out past the catch block. Of course, there are some cases where a return within a catch is normal, and some where it might break a program's expected invariants. I wonder if there would be a usefulness to having a try all {} block whose interior would be forbidden from containing any interior return statements [the "all" implying that if there's no exception the whole try block should be executed].
Else may not be the best choice, but I wanted to resist just adding a new keyword.
Sep 24, 2014 at 7:11 PM
brunomlopes wrote:
I think that exceptions thrown in the else clause wouldn't get caught in the catch(Exception) clause.

In your second case, any exception thrown on the
  WriteLine("No exceptions thrown");
line would get caught in
catch (Exception)
{
  WriteLine("Some other Exception was thrown");
}
But in the else case it would get propagated upwards.
I imagined that an exception thrown in the else block would work in exactly the same way as if one were thrown in the catch block - it would rise up to the next try statement (if any) as usual, since it's outside of the 'safety' of the try block.
Sep 24, 2014 at 9:47 PM
I can see it being useful although I don't see else as being the same as success.

For me, else would mean any other condition: success or a not caught exception. Is that what you are proposing?
Sep 24, 2014 at 11:16 PM
Yuioppe wrote:
Else may not be the best choice, but I wanted to resist just adding a new keyword.
In considering a language feature, it's worthwhile to examine places that no identifier may legally appear. Defining a new context-sensitive keyword for such purposes will not cause compatibility problems with existing code, because no existing code can use an identifier in that context. For example, yield is not a reserved word in C#; the yield return statement may be recognized because there are few things that can precede return, and prior to the introduction of iterators the word yield wasn't one of them.

In general, try/catch/whatever blocks can only accept a few tokens between a close-brace and an open-brace. Adding new keywords that would be usable only in those contexts would not create any ambiguity.