C # "finally", which only works on exceptions

Edit: I looked at the response code: NO of them do what I want (I checked). It would seem that there is no way to do what I want in my native C #. I think this is not just a shame given that .NET supports it (see Accepted Answer).

Thanks to everyone.


I have C # code (the part of the test environment that will never be run except in the debugger), like this, who points to this to avoid exceptions, as this makes debugging the code in the unwound part of the stack a real pain.

Bool bad = true; try { MightThrow(); bad = false; } finally { if(bad) DoSomeLoggingOnFailure(); //// Does not catch!!!! //// exception continues to unwind stack. //// Note that re throwing the exception is NOT //// the same as not catching it in the first place } 

is the best way to do this?

The solution should behave the same as with the debugger with respect to excepted exceptions. This would result in a single exception from the first chance and the debugger breaks at the point when the exception was originally thrown, and not in the catch block.

In particular, I need a debugger for thrown exceptions to stop the lateral MightThrow.

The following does not work because it has no debugging interrupt in the right place

 try { ... } catch { throw; } 

And this does not work, because it loses information about the stack (and also breaks in the wrong place).

 try { ... } catch(Excption e) { throw e; } 

I knew that in D I could use scope(failure)

+7
c # exception-handling finally
source share
16 answers

So, in .NET, what you ask is theoretically possible, but it will not be easy.

CIL actually defines five types of exception handling blocks! try , catch and finally ones you used in C #, and two others:

  • filter - like a catch , but can run arbitrary code to determine if it wants to handle the error, and not just match by type. This block has access to the exception object and has the same effect on the trace of the exception stack as the catch .

  • fault - It looks like a finally block, but it only starts when an exception occurs. This block does not have access to the exception object and does not affect the trace of the exception stack (like the finally block).

filter is available in some .NET languages ​​(for example, VB.NET, C ++ / CLI), but is not available in C #, unfortunately. However, I do not know any language other than CIL, which allows you to express a fault block.

Because it can be done in IL, but not all is lost. Theoretically, you can use Reflection.Emit to dynamically emit a function with a fault block, and then pass the code you want to run as lambda expressions (i.e. one for the try part, one for the error part, etc.) ), however (a) it’s not easy, and (b) I’m not convinced that this will actually give you a more useful stack trace than you currently get.

Sorry, the answer is not “here how to do it,” but at least now you know! What you are doing now is probably IMHO's best approach.


Pay attention to those who say that the approach used in the question is “bad practice”, in fact this is not so. When you implement the catch , you say: “I need to do something with the exception object when an exception occurs”, and when you implement finally , you say: “I do not need the exception object, but I need to do something to the end functions. "

If what you are actually trying to say is: “I do not need an exception object, but I need to do something when an exception occurs,” then you are halfway between them, i.e. want a fault block. Since this is not available in C #, you do not have an ideal option, so you can also choose an option that is less likely to cause errors, forgetting to flip, and which will not damage the stack trace.

+33
source share

How about this:

 try { MightThrow(); } catch { DoSomethingOnFailure(); throw; // added based on new information in the original question } 

Indeed, all that you have done. Finally, for things to be done regardless of whether an exception has occurred.

[ Edit: Clarification]

Based on the comments you mentioned, you want the exception to continue to throw without changing its original stack trace. In this case, you want to use the extra cast that I added. This will eliminate the continuation of the stack and still allow you to handle part of the exception. Typical cases may be the closure of network connections or files.

[ Second edit: Regarding your explanation]

In particular, I need a debugger excepted exceptions to stop at the starting point of the cast (in MightThrow) not in the catch block.

I would object to ever violating best practice (and yes, it is best practice to partially handle exceptions) to add a little value to your debugging. You can easily check the exception to determine where the exception is thrown.

[ Final editing: You have an answer]

kronoz thoughtfully provided you with the answer you were looking for. Do not break best practices - use Visual Studio correctly! You can configure Visual Studio to break exactly when an exception is thrown. Here is the official information on the topic .

I really did not know about this function, so give it an acceptable answer. But please don't try to handle exceptions in a funky way, just to give yourself a hand debug. All you do is open yourself up to more mistakes.

+27
source share

If you are interested in the debugger, just stopping exactly where the exception occurred, did you consider the exceptions with the first chance?

If you open "Tools | Exceptions", check the box "Exclude common languages", the debugger will stop at the exception point, regardless of any try / catch / finally blocks.

Update . You can specify the exact exception that you want to catch by expanding the [+] tree in the Exceptions dialog box. Although, of course, it will fire every time an exception of the specified type [s] occurs, you can turn it on and off as you wish even in the middle of a debugging session, so if you use breakpoints wisely, you can get it to make your bids. I used it successfully to get around the "target of the call that caused the exception" due to the use of reflection to create objects. A very useful tool in such conditions. Also, pay attention to the fact that the locales and stack trace should be firmly accessible, as far as I remember (I just did a quick test, and they are available), so there are no problems.

Of course, if you want to log something that goes beyond the IDE debugger; and in this case, exceptions from the first chance will not help you!

Give him at least a walk; I found them very useful, and they may be more suitable for your problem than you think.

+11
source share

What happened with:

 try { MightThrow(); } catch { DoSomthingOnFailure(); throw; } 
+7
source share

For code that should only work on exceptions, use the catch block:

 try { MightThrow(); } catch (Exception ex) { // this runs only when there was an exception DoSomthingOnFailure(); // pass exception on to caller throw; } finally { // this runs everytime Cleanup(); } 
+7
source share

Is this what you want. It will only call this method when an error occurs, and the throw statement re-throws the exception with the brake light intact.

 try { MightThrow(); } catch { DoSomthingOnFailure(); throw; } 
+4
source share

The finally block, which only works on failure, is called a catch (without parameters). :-)

Now there is a small caveat. If you want to have a specialized “catch” case for a particular type of exception and have a general “catch” that works for all exceptions, you will need to do some custom logic.

So I would do something like:

  try { MightThrow(); } catch(MyException ex) { // Runs on MyException MySpecificFailureHandler() // Since we have handled the exception and can't execute the generic // "catch" block below, we need to explicitly run the generic failure handler MyGenericFailureHandler() } catch { // Runs on any exception hot handled specifically before MyGenericFailureHandler() // If you want to mimic "finally" behavior and propagate the exception // up the call stack throw; } finally { // Runs on any failure or success MyGenericCleanupHandler(); } 
+3
source share

Each example so far loses the original StackTrace in accordance with my tests. Here is a solution that should work for you.

 private static void PreserveStackTrace(Exception exception) { MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic); preserveStackTrace.Invoke(exception, null); } try { MightThrow(); } catch (Exception ex) { DoSomethingOnFailure(); PreserveStackTrace(ex); throw; } 
+2
source share

How to get only the exception that "MightThrow" does not ?

 Bool bad = true; try { MightThrow(); bad = false; } catch (SomePrivateMadeUpException foo) { //empty } finally { if(bad) DoSomeLoggingOnFailure(); } 
+2
source share

Let me repeat your requirements as I understand them:

  • You want some code to run only when an exception is thrown in order to perform some logging.
  • You want to run the test environment under the debugger and break at the point at which the exception is thrown.

To meet the first requirement, you must write the code as it was proposed to everyone - using a carefree grab and throw.

To fulfill the second requirement when using capture without parameters, you can configure the debugger to break when an exception occurs, and not just when there is an unhandled exception. I suspect that you know how to do this, but I will put it here to complete the answer: in VS you can do this in Debug -> Exception -> Common Language Runtime Exceptions -> check the Thrown checkbox.

If you know that your application throws many handled exceptions, this may not be for you. At this point, your only choice left before your first requirement is to either write the code for final use for exception logging or study the direct route of emitting IL, as Greg Buck suggests.

However, whether the finally code will be executed depends on the debugger used. In particular, VS will be broken into a non-random exception before it is executed and will not allow you to continue. Thus, if you have not disconnected from the process at this moment, your registration code will never be executed. In other words, the second requirement will interfere with the fulfillment of the first requirement.

+2
source share

You can encapsulate your logic in a custom class, for example:

  public class Executor { private readonly Action mainActionDelegate; private readonly Action onFaultDelegate; public Executor(Action mainAction, Action onFault) { mainActionDelegate = mainAction; onFaultDelegate = onFault; } public void Run() { bool bad = true; try { mainActionDelegate(); bad = false; } finally { if(bad) { onFaultDelegate(); } } } } 

And use it like:

  new Executor(MightThrow, DoSomeLoggingOnFailure).Run(); 

Hope this helps.

+2
source share

Isn't it like this:

 try { MightThrow(); } catch (Exception e) { DoSomethingOnFailure(); throw e; } 

?

+1
source share

You can write or write someone for yourself a small assembly in VB.net that implements the TryFaultCatchFinally method (from T), which accepts four delegates:

  1. TryMethod - action (from T) to execute the "Try" block.
  2. FaultMethod - predicate (Of T, Exception), which, if an exception occurs, will be called before any "final" blocks; if it returns true, the Catch block will be executed, otherwise it will not.
  3. CatchMethod - an action (Of T, Exception) that should be executed if an exception occurs, and FaultMethod returns true; happens after the finally blocks are run.
  4. LastMethod - an action (OF T, Exception, Boolean), which should be executed as a block "Finally". The thrown event will be null if TryMethod has completed execution or will contain an exception that caused it to exit. The boolean value will be true if the exception was caught or false otherwise.

    Note that when FaultMethod is executed, you can check the state of the objects that caused the exception before this state is destroyed by the finally blocks. At the same time, some caution must be exercised (any locks that were saved when the exception was thrown will continue to be preserved), but the ability can sometimes be convenient, especially when debugging.

    I suggest that the routine look something like this:

         Shared Sub TryFaultCatchFinally (Of T) (ByVal TryProc As Action (Of T), _
                                               ByVal FaultProc As Func (Of T, Exception, Boolean), _
                                               ByVal CatchProc As Action (Of T, Exception), _
                                               ByVal FinallyProc As Action (Of T, Exception, Boolean), _
                                               ByVal Value As T)
             Dim theException As Exception = Nothing
             Dim exceptionCaught As Boolean = False
             Try
                 TryProc (Value)
                 theException = Nothing
                 exceptionCaught = False
             Catch Ex As Exception When CopyExceptionAndReturnFalse (Ex, theException) OrElse FaultProc (Value, Ex)
                 exceptionCaught = True
                 CatchProc (Value, Ex)
             Finally
                 FinallyProc (Value, theException, exceptionCaught)
             End try
         End sub
    
+1
source share

No, I think this is an ordinary idiom like yours.

EDIT To be clear, the catch and then retry strategies offer the same semantics at run time, but they change experience when connecting to the VS debugger. Equipment and maintenance are important; Debugging often requires you to “catch all exceptions at your first chance,” and if you end up with a lot of “false” exceptions from the first chance due to the catch and then re-throwing in your code, it will really hurt your debugging capabilities. This idiom is to interact well with the tools, as well as clearly express the intention (you do not want to “catch”, you cannot process and reconstruct, instead you just want to register that an exception has occurred, but let it pass by).

0
source share

Do you consider using the DebuggerStepThrough attribute? http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerstepthroughattribute.aspx

 [DebuggerStepThrough] internal void MyHelper(Action someCallback) { try { someCallback(); } catch(Exception ex) { // Debugger will not break here // because of the DebuggerStepThrough attribute DoSomething(ex); throw; } } 
0
source share
 try { MightThrow(); } catch { DoSomethingOnFailure(); } 
-one
source share

All Articles