38

Is it possible to determine if code is currently executing in the context of a finally handler as a result of an exception being thrown? I'm rather fond of using the IDisposable pattern to implement entry/exit scoping functionality, but one concern with this pattern is that you might not necessarily want the end-of-scope behavior to occur if an exception occurs in the body of the using. I'd be looking for something like this:

public static class MyClass
{
    public static void MyMethod()
    {
        using (var scope = MyScopedBehavior.Begin())
        {
            //Do stuff with scope here
        }
    }
}

public sealed class MyScopedBehavior : IDisposable
{
    private MyScopedBehavior()
    {
        //Start of scope behavior
    }

    public void Dispose()
    {
        //I only want to execute the following if we're not unwinding
        //through finally due to an exception:
        //...End of scope behavior    
    }

    public static MyScopedBehavior Begin()
    {
        return new MyScopedBehavior();
    }
}

There are other ways I can accomplish this (pass a delegate to a function that surrounds the call with particular behavior), but I'm curious if it's possible to do it using the IDisposable pattern.


Actually, this has apparently been asked and answered before here. It's possible to detect in a very hackish sort of way. I wouldn't actually use that technique, but it's interesting to know that it's possible.

Community
  • 1
  • 1
Dan Bryant
  • 27,329
  • 4
  • 56
  • 102

8 Answers8

18

The means of accomplishing this that I've seen require an extra method:

public static void MyMethod()
{
    using (var scope = MyScopedBehavior.Begin())
    {
        //Do stuff with scope here
        scope.Complete(); // Tells the scope that it's good
    }
}

By doing this, your scope object can track whether it's disposing because of an error, or a successful operation. This is the approach taken by TransactionScope, for example (see TransactionScope.Complete).

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Yeah, I think I like yours (optimistic) better than mine (pessimistic). – Brian Genisio Jul 21 '10 at 16:29
  • @Brian: Both work, but this requires less code by the user of your class :) – Reed Copsey Jul 21 '10 at 16:29
  • This is pretty clean and it's more explicit that something different will happen if you don't reach the Complete. I prefer this style to the 'trick' I'm asking for, but I'm definitely still curious if it's technically possible. – Dan Bryant Jul 21 '10 at 16:38
  • @Dan: This has become the standard way of handling factored types in .NET, as far as I can tell (ie: it's used in the BCL). I don't know of a better way to handle this, as Dispose() isn't given any information about the current exception state. – Reed Copsey Jul 21 '10 at 16:40
  • Useless for nested scopes, and doesn't actually detect whether an exception triggered the finally block. For example, if this kind of scope could be stacked like using(new Scope) using (new Scope) using (new Scope) { call "Complete" on all three scopes), then all scopes would THINK no error occurred, but that's not the case, because by the time Dispose is called on outermost scope, an error could have occurred in the first two Dispose calls. So besides being a very strong argument for Dispose to never throw exceptions, there's still no way to 'detect' why the finally block was entered. – Triynko Aug 10 '18 at 15:14
15

As a side point, IL allows you to specify SEH fault blocks that are similar to finally but are entered only when an exception is thrown - you can see an example here, about 2/3rds down the page. Unfortunately, C# doesn't expose this functionality.

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 1
    +1 for being very interesting - do you know of any languages that expose this for the CLR? It'd be interesting to use that for constructing factored types (or even, potentially, a base class that could expose this to C#!) – Reed Copsey Jul 21 '10 at 16:47
  • 2
    Not that I know of. VB.NET exposes `filter` (also mentioned in the post), but C# doesn't. It's times like this that I wish I could extend the C# compiler to add this little bit of extra syntax :/ – thecoop Jul 21 '10 at 16:54
  • 1
    Unfortunately, even if C# did expose this behavior, it wouldn't solve the OP's problem :( – Reed Copsey Jul 21 '10 at 17:37
  • Boo has support for fault handlers. – Nick Cox Dec 19 '10 at 11:59
  • 1
    Nowadays C# exposes exception filters via `when`. – Drew Noakes Sep 03 '18 at 12:45
7

I was looking for something similar for unit testing - I have a helper class I use to clean up objects after a test run and I want to keep the nice, clean 'using' syntax. I also wanted the option of not cleanup up if the test failed. What I came up with is to call Marshal.GetExceptionCode(). I don't know if this is appropriate for all cases, but for test code it seems to work fine.

dmo
  • 3,993
  • 6
  • 35
  • 39
  • My main use case was also for unit testing; I wanted to validate certain scope exit conditions via Asserts, but I wanted to skip the validation if an AssertionException had already been thrown (so as not to suppress it.) – Dan Bryant Oct 07 '10 at 18:22
5

The best I can come up with would be:

using (var scope = MyScopedBehavior.Begin())
{
  try
  {
    //Do stuff with scope here
  }
  catch(Exception)
  {
    scope.Cancel();
    throw;
  }
}

Of course, scope.Cancel() would make sure nothing happens in Dispose()

Brian Genisio
  • 47,787
  • 16
  • 124
  • 167
  • I personally like the "Complete" approach taken by TransactionScope more than a Cancel() approach. It doesn't require you to write a try/catch to work. (See my answer for details...) – Reed Copsey Jul 21 '10 at 16:29
  • @Reed Copsey: Ha! Yeah, I think we wrote our answers, and then responded to each other at the same time. I agree. Yours is better. Not pulling mine down, just to show another approach. – Brian Genisio Jul 21 '10 at 16:31
  • 1
    One advantage to scope.Cancel() would be that it could take an Exception argument and then make it available as an inner exception if the dispose/cleanup fails. For example, if a transaction cleanup fails, it may be reasonable for that to throw an exception that's "more severe" than the exception which started the chain of events, but it would be annoying to lose altogether the stack trace and other information related to the original exception. – supercat Jul 21 '10 at 17:28
  • Still doesn't achieve what's required. If you had multiple using statements stacked like using (new Scope()) using (new Scope()) using (new Scope()) { mark all three complete }, they'd all think no error has occurred, but that's simply not the case if an error is thrown from either of the inner scope's finally/dispose calls. The "using" block is essentially a handicapped try/catch/finally block that doesn't support the "catch" block and the finally block is forced to be a simple Dispose call, lol. Too bad. – Triynko Aug 10 '18 at 15:19
4

The following pattern avoids the problem with API misuse i.e. a scope completion method not being called i.e. omitted completely, or not being called because of a logical condition. I think this answers your question more closely and is even less code for the API user.

Edit

Even more straightforward after Dan's comment:

public class Bling
{
    public static void DoBling()
    {
        MyScopedBehavior.Begin(() =>
        {
            //Do something.
        }) ;
    }   
}

public static class MyScopedBehavior
{
    public static void Begin(Action action)
    {
        try
        {
            action();

            //Do additonal scoped stuff as there is no exception.
        }
        catch (Exception ex)
        {
            //Clean up...
            throw;
        }
    }
}   
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • 1
    If going down the route of using a delegate to scope the behavior, I forgo the IDisposable entirely and create a static method `MyScope.With(Action action)` and then call it like `MyScope.With(myScope => { ...delegate contents...})` – Dan Bryant Jul 21 '10 at 16:56
  • @Dan That might be a better solution given the API misuse issues. – Tim Lloyd Jul 21 '10 at 17:01
  • 1
    If the scope wrapper were written in VB.net, it could accept a MethodInvoker for the main method and an Action(Of Exception) for the finally clause which would be given the exception that occurred, if any, for the main action. Unlike C#, VB.net allows one to determine what exception occurred without having to catch and rethrow (meaning, e.g., that a debugger trap will show where an ultimately-unhandled exception occurs, rather than the lowest level at which it was rethrown). – supercat Dec 07 '10 at 18:39
1

It would be (IMHO very) helpful if there were a variant of IDisposable whose Dispose method accepted a parameter to indicate what exception, if any, was pending when it was run. Among other things, in the event that Dispose is unable to perform the expected cleanup, it would be able to throw an exception which includes information about the earlier exception. It would also allow a Dispose method to throw an exception if code "forgets" to do something that it was supposed to do within a using block, but not overwrite any other exception that might cause the using block to exit prematurely. Unfortunately, no such feature exists as of yet.

There are numerous articles which suggest means of using API functions to find out whether a pending exception exists. One major problem with such approaches is that it is possible that code may be running in a finally block for a try which completed successfully, but that may be nested in a finally block whose try exited prematurely. Even if a Dispose method could identify that such a situation existed, it would have no way of knowing which try block it "belonged" to. One could formulate examples where either situation applies.

As it is, the best approach is probably to have an explicit "success" method and assume failure if it's not called, and figure that the consequences of forgetting to call the "success" method should be obvious even if no exception is thrown. One thing that may be helpful as a simple utility method would be something like

T Success<T>(T returnValue)
{
  Success();
  return T;
}

thus allowing code like:

return scopeGuard.Success(thingThatMightThrow());

rather than

var result = thingThatMightThrow();
scopeGuard.Success();
return result;
supercat
  • 77,689
  • 9
  • 166
  • 211
1

I think the best way is to use write out try/catch/finally clause manually. Study an item from the first 'Effective c#" book. A good C# hacker should know exactly what using expands to. It has changed a bit since .Net 1.1 - you can now have several using one under another. So, use reflector, and study the un-sugared code.

Then, when you write your own code - either use the using or write your own stuff. It is not terribly hard, and a good thing to know.

You could get fancy with other tricks, but it feels too heavy, and even not efficient. Let me include a code sample.

LAZY WAY:

using (SqlConnection cn = new SqlConnection(connectionString))
using (SqlCommand cm = new SqlCommand(commandString, cn))
{
    cn.Open();
    cm.ExecuteNonQuery();
}

MANUAL WAY:

bool sawMyEx = false;
SqlConnection cn =  null;
SqlCommand cm = null;

try
{
    cn = new SqlConnection(connectionString);
    cm = new SqlCommand(commandString, cn);
    cn.Open();
    cm.ExecuteNonQuery();
}
catch (MyException myEx)
{
    sawMyEx = true; // I better not tell my wife.
    // Do some stuff here maybe?
}
finally
{
    if (sawMyEx)
    {
        // Piss my pants.
    }

    if (null != cm);
    {
        cm.Dispose();
    }
    if (null != cn)
    {
        cn.Dispose();
    }
}
Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147
  • What I'm really after here is the clarity of the 'using' syntax as a way to express the intention of the code. For the case I'm describing, I wouldn't actually need the try/finally at all if writing it out manually, but the 'using' construct imposes the finally. I'm mainly curious about how far I can make the construct bend. – Dan Bryant Jul 21 '10 at 16:31
  • Good luck, it better be readable at the end. Please post a complete working code sample once you do have it. – Hamish Grubijan Jul 21 '10 at 16:36
  • You have missed Dan's point, but provided a quite hilarious sample. Made me smile anyway. :) – Tim Lloyd Jul 21 '10 at 17:38
  • The first +1 since this is actually the solution I employed as it's all that's needed for my particular scenario and works just fine. – ouflak Jan 17 '18 at 14:30
0

Why not simply dispose from inside a try { } block at the very end, and not use a finally at all? This seems to be the behavior you're looking for.

This also seems more realistic in terms of how others might use your class. Are you sure that everybody who ever uses it will never want to dispose in the case of an exception? Or should this behavior be handled by the consumer of the class?

drharris
  • 11,194
  • 5
  • 43
  • 56
  • The Dispose in this case is not really a Dispose in the sense of disposing resources, but rather a syntactic trick to add begin/end code around a particular block of code via the 'using' construct. See the TransactionScope that Reed Copsey mentioned for an example of the pattern. – Dan Bryant Jul 21 '10 at 16:44