Roslyn: Proposal: finally (Exception ex)

Created on 31 Oct 2016  路  8Comments  路  Source: dotnet/roslyn

I frequently want to write finally code that does something slightly different if there was an exception thrown. Or put another way, I frequently want to write similar, but not identical, code in a catch block and a finally block. I end up having to do something such as:

bool finished = false;
try {
    // do stuff
    finished = true;
} finally {
    // do stuff
    if (!finished)
        // do more stuff
}

Or such as:

Exception exception = null;
try {
    // do stuff
} catch (Exception ex) {
    exception = ex;
} finally {
    // do stuff
    if (exception != null)
        // do more stuff
}

It would be nice if I could instead write:

try {
    // do stuff
} finally (Exception ex) {
    // ex is not null if and only if an exception was thrown
}

I would also be able to write:

try {
    // do stuff
} finally (IOException ex) {
    // ex is not null if and only if an IOException was thrown.
    // ex is null if an exception other than IOException was thrown.
}
0 - Backlog Area-Language Design Feature Request

Most helpful comment

Can we just add a fault clause like the CLR supports? fault is like finally but only executes if a try block does not exit normally. It evaluates after the catch block, but I do not know if you can reference exceptions caught in earlier blocks.

All 8 comments

Can we just add a fault clause like the CLR supports? fault is like finally but only executes if a try block does not exit normally. It evaluates after the catch block, but I do not know if you can reference exceptions caught in earlier blocks.

What are the use cases when you would find this useful?

bool finished = false;
try {
    // do stuff
    finished = true;
} finally {
    // do stuff
    if (!finished)
        // do more stuff
}

why not

try {
    // do stuff
    // yep, it's finished
    // do more stuff
} catch (...) {
    // damn, it's not finished
} finally {
    // well, anyways
}

if you want to know that the control reached a certain point perhaps you can use multiple/nested try catches.

I think its best to consider a method with many catch handlers...

try{
...
}
catch(ExceptionTypeA exa){
   ...
}
catch(ExceptionTypeB exb){
   ...
}
catch(ExceptionTypeC exc){
   ...
}
finally{
...
}

Now let's say I need to add logging to eventviewer on failure stating that the method just finished abnormally. I can either wire that logging code in each block, or do it as a set of exception filters but that's still me repeating the same code three times, I could use a bool finished, but that's ugly.

This is the case for a fault block is for.

{
 try { ... }
 catch(Exception ex) when (Log(ex)) { throw; }
} 
catch (Exception1) { ... }
catch (Exception2) { ... }
etc

OR

try { ... }
catch (Exception ex) when (Log(ex))
{ 
  switch(ex)
  {
    case Exception1 ex1: ...;
    case Exception2 ex2: ...;
    default: throw;
  }
}

That's still pretty ugly and introduces additional nesting. The CLR supports it, and actually can generate it in the case of using statements in asyc and iterator blocks.
Here is the case of how it would generate in an iterator block:
http://stackoverflow.com/a/16930005/468672

@mburbea

Depends. fault handlers have no access to the thrown Exception so their usefulness in a lot of those scenarios might be pretty limited.

For common exception handling scenarios I think that Java's approach might be cleaner:

try { ... }
catch (Exception1 | Exception2 ex) {
    // common cleanup here
}

That doesn't really solve the problem that this proposal is trying to solve, but I don't think any out-of-the-box tools do. Having the finally block accept an Exception is novel as shorthand for the pattern demonstrated, but I think that the syntax accepting a derived exception would be confusing as it might imply that the finally block doesn't execute unless that exception is thrown, or that you can have multiple finally blocks like you can catch blocks.

@svick

With regard to use cases, I have ran into 3:

1. Finally code that cleans up slightly different when there's an exception

I've written many UI auto tests using Coded UI and a common design pattern is something like:

ShowDialog();
bool finished = false;
try {
    // test code
    finished = true;
} finally {
    // cleanup code
    if (finished)
        ClickOK();
    else
        ClickCancel();
    // more cleanup code
}

With this feature I could avoid the finished variable. While it's not a huge deal to do occasionally, with Coded UI at least you have to write such code a lot. I could see needing to do this in other situations where you frequently need to write complex finally code.

2. To deal with an exception that occurs within a finally block

By default an exception in a finally block would overwrite an existing exception (if there is one) which would virtually never be what you want.
Instead of having to do:

Exception ex1 = null;
try {
    // do stuff
} catch (Exception ex) {
    ex1 = ex;
} finally {
    try {
        // do more stuff
    } catch (Exception ex2) {
        if (ex1 == null)
            rethrow;
        else
            throw new AggregateException(ex1, ex2);
    }
}

With this proposed feature you could deal with this more cleanly by doing:

try {
    // do stuff
} finally (Exception ex1) {
    try {
        // do more stuff
    } catch (Exception ex2) {
        if (ex1 == null)
            rethrow;
        else
            throw new AggregateException(ex1, ex2);
    }
}

3. Avoid having to rethrow an exception

To avoid the first shortcoming described in #14999, I frequently write code such as:

bool finished = false;
try {
    // do stuff
    finished = true;
} finally {
    if (!finished)
        // exception code (provided exception code doesn't reference the exception; exception will be rethrown)
}

to avoid having to do the following which can lose some call stack info:

try {
    // do stuff
} catch (Exception) {
    // exception code
    throw;
}

With the proposed feature I could do the following, not lose any call stack info, and not have to have a finished variable:

try {
    // do stuff
} finally (Exception ex) {
    if (ex != null)
        // exception code
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Pilchie picture Pilchie  路  113Comments

stephentoub picture stephentoub  路  259Comments

mattwar picture mattwar  路  190Comments

MgSam picture MgSam  路  152Comments

davidroth picture davidroth  路  158Comments