ChatGPT解决这个技术问题 Extra ChatGPT

What happens if a finally block throws an exception?

If a finally block throws an exception, what exactly happens?

Specifically, what happens if the exception is thrown midway through a finally block. Do the rest of statements (after) in this block get invoked?

I am aware that exceptions will propagate upwards.

Why not just try it? But on this sort of things, the one I like the most is return before the finally and then return something else from the finally block. :)
All statements in a finally block must execute. It can't have a return. msdn.microsoft.com/en-us/library/0hbbzekw(VS.80).aspx

H
Henk Holterman

If a finally block throws an exception what exactly happens ?

That exception propagates out and up, and will (can) be handled at a higher level.

Your finally block will not be completed beyond the point where the exception is thrown.

If the finally block was executing during the handling of an earlier exception then that first exception is lost.

C# 4 Language Specification § 8.9.5: If the finally block throws another exception, processing of the current exception is terminated.


Unless it is a ThreadAbortException, then the whole finally block will be finished first, as it is a critical section.
@Shedal - you're right but that applies only to "certain asynchronous exceptions", ie ThreadAbortException. For normal 1-thread code my answer holds.
"First exception is lost" - thats actually very disapointing, accasionally I find IDisposable objects that throw exception in Dispose(), which result in exception being lost inside "using" clause.
"I find IDisposable objects that throw exception in Dispose()" - that's weird to say the least. Read on MSDN: AVOID throwing an exception from within Dispose(bool) except under ...
@HenkHolterman: Disk-full errors aren't very common on a directly-connected primary hard disk, but programs sometimes write files to removable or networked disks; problems can be much more common with those. If someone yanks out a USB stick before a file has been fully written, it would be better to tell them immediately than wait until they get where they're going and find the file is corrupt. Yielding to the earlier error when there is one may be sensible behavior, but when there is no earlier error it would be better to report the problem than leave it unreported.
D
Dirk Vollmar

For questions like these I usually open up an empty console application project in Visual Studio and write a small sample program:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Inner catch block handling {0}.", ex.Message);
                throw;
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

When you run the program you will see the exact order in which catch and finally blocks are executed. Please note that code in the finally block after the exception is being thrown will not be executed (in fact, in this sample program Visual Studio will even warn you that it has detected unreachable code):

Inner catch block handling exception thrown from try block.
Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block

Additional Remark

As Michael Damatov pointed out, an exception from the try block will be "eaten" if you don't handle it in an (inner) catch block. In fact, in the example above the re-thrown exception does not appear in the outer catch block. To make that even more clear look at the following slightly modified sample:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

As you can see from the output the inner exception is "lost" (i.e. ignored):

Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block

Because you THROW the Exception in your inner catch, 'Inner finally block' will never be reached in this example
@Theofanis Pantelides: No, a finally block will (almost) always be executed, this holds also in this case for the inner finally block (just try the sample program yourself (A finally block will not be executed in the case of a non-recoverable exception, e.g. an EngineExecutionException, but in such a case your program will terminate immediately anyway).
I do not see what is the role for the throw in the first catch of your first piece of code, though. I tried with and without it with a console application, no differece found.
@johnpan: The point was to show that the finally block always executes, even if both try and catch block throw an exception. There is indeed no difference in the console output.
M
Mudassir Hasan

If there is an exception pending (when the try block has a finally but no catch), the new exception replaces that one.

If there is no exception pending, it works just as throwing an exception outside the finally block.


An exception might also be pending if there is a matching catch block that (re-)throws an exception.
l
lxa

Quick (and rather obvious) snippet to save "original exception" (thrown in try block) and sacrifice "finally exception" (thrown in finally block), in case original one is more important for you:

try
{
    throw new Exception("Original Exception");
}
finally
{
    try
    {
        throw new Exception("Finally Exception");
    }
    catch
    { }
}

When code above is executed, "Original Exception" propagates up the call stack, and "Finally Exception" is lost.


D
Darin Dimitrov

The exception is propagated.


@bitbonk: from the inside out, as usual.
D
Doug Coburn

Throwing an exception while another exception is active will result in the first exception getting replaced by the second (later) exception.

Here is some code that illustrates what happens:

    public static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("first exception");
            }
            finally
            {
                //try
                {
                    throw new Exception("second exception");
                }
                //catch (Exception)
                {
                    //throw;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

Run the code and you will see "second exception"

Uncomment the try and catch statements and you will see "first exception"

Also uncomment the throw; statement and you will see "second exception" again.


It's worth noting that it's possible for the cleanup of a "severe" exception which would only be caught outside a particular code block to throw an exception which is caught and handled within it. Using exception filters (available in vb.net, though not C#) it's possible to detect this condition. There's not a whole lot that code can do to "handle" it, though if one is using any sort of logging framework it's almost certainly worth logging. The C++ approach of having exceptions that occur within cleanup trigger a system meltdown is ugly, but having exceptions disappear is IMHO hideous.
n
nawfal

Some months ago i also faced something like this,

    private  void RaiseException(String errorMessage)
    {
        throw new Exception(errorMessage);
    }

    private  void DoTaskForFinally()
    {
        RaiseException("Error for finally");
    }

    private  void DoTaskForCatch()
    {
        RaiseException("Error for catch");
    }

    private  void DoTaskForTry()
    {
        RaiseException("Error for try");
    }


        try
        {
            /*lacks the exception*/
            DoTaskForTry();
        }
        catch (Exception exception)
        {
            /*lacks the exception*/
            DoTaskForCatch();
        }
        finally
        {
            /*the result exception*/
            DoTaskForFinally();
        }

To solve such problem i made a utility class like

class ProcessHandler : Exception
{
    private enum ProcessType
    {
        Try,
        Catch,
        Finally,
    }

    private Boolean _hasException;
    private Boolean _hasTryException;
    private Boolean _hasCatchException;
    private Boolean _hasFinnallyException;

    public Boolean HasException { get { return _hasException; } }
    public Boolean HasTryException { get { return _hasTryException; } }
    public Boolean HasCatchException { get { return _hasCatchException; } }
    public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
    public Dictionary<String, Exception> Exceptions { get; private set; } 

    public readonly Action TryAction;
    public readonly Action CatchAction;
    public readonly Action FinallyAction;

    public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
    {

        TryAction = tryAction;
        CatchAction = catchAction;
        FinallyAction = finallyAction;

        _hasException = false;
        _hasTryException = false;
        _hasCatchException = false;
        _hasFinnallyException = false;
        Exceptions = new Dictionary<string, Exception>();
    }


    private void Invoke(Action action, ref Boolean isError, ProcessType processType)
    {
        try
        {
            action.Invoke();
        }
        catch (Exception exception)
        {
            _hasException = true;
            isError = true;
            Exceptions.Add(processType.ToString(), exception);
        }
    }

    private void InvokeTryAction()
    {
        if (TryAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasTryException, ProcessType.Try);
    }

    private void InvokeCatchAction()
    {
        if (CatchAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
    }

    private void InvokeFinallyAction()
    {
        if (FinallyAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
    }

    public void InvokeActions()
    {
        InvokeTryAction();
        if (HasTryException)
        {
            InvokeCatchAction();
        }
        InvokeFinallyAction();

        if (HasException)
        {
            throw this;
        }
    }
}

And used like this

try
{
    ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
    handler.InvokeActions();
}
catch (Exception exception)
{
    var processError = exception as ProcessHandler;
    /*this exception contains all exceptions*/
    throw new Exception("Error to Process Actions", exception);
}

but if you want to use paramaters and return types that's an other story


R
Raj Baral

The exception propagates up, and should be handled at a higher level. If the exception is not handled at the higher level, the application crashes. The "finally" block execution stops at the point where the exception is thrown.

Irrespective of whether there is an exception or not "finally" block is guaranteed to execute.

If the "finally" block is being executed after an exception has occurred in the try block, and if that exception is not handled and if the finally block throws an exception

Then the original exception that occurred in the try block is lost.

public class Exception
{
    public static void Main()
    {
        try
        {
            SomeMethod();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static void SomeMethod()
    {
        try
        {
            // This exception will be lost
            throw new Exception("Exception in try block");
        }
        finally
        {
            throw new Exception("Exception in finally block");
        }
    }
} 

Great article for Details


M
Massimiliano Kraus

I had to do this for catching an error trying to close a stream that was never opened because of an exception.

errorMessage = string.Empty;

try
{
    byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);

    webRequest = WebRequest.Create(url);
    webRequest.Method = "POST";
    webRequest.ContentType = "text/xml;charset=utf-8";
    webRequest.ContentLength = requestBytes.Length;

    //send the request
    using (var sw = webRequest.GetRequestStream()) 
    {
        sw.Write(requestBytes, 0, requestBytes.Length);
    }

    //get the response
    webResponse = webRequest.GetResponse();
    using (var sr = new StreamReader(webResponse.GetResponseStream()))
    {
        returnVal = sr.ReadToEnd();
        sr.Close();
    }
}
catch (Exception ex)
{
    errorMessage = ex.ToString();
}
finally
{
    try
    {
        if (webRequest.GetRequestStream() != null)
            webRequest.GetRequestStream().Close();
        if (webResponse.GetResponseStream() != null)
            webResponse.GetResponseStream().Close();
    }
    catch (Exception exw)
    {
        errorMessage = exw.ToString();
    }
}

if the webRequest was created but a connection error happened during the

using (var sw = webRequest.GetRequestStream())

then the finally would catch an exception trying to close up connections it thought was open because the webRequest had been created.

If the finally didnt have a try-catch inside, this code would cause an unhandled exception while cleaning up the webRequest

if (webRequest.GetRequestStream() != null) 

from there the code would exit without properly handling the error that happened and therefore causing issues for the calling method.

Hope this helps as an example


P
Patrick Hofman
public void MyMethod()
{
   try
   {
   }
   catch{}
   finally
   {
      CodeA
   }
   CodeB
}

The way the exceptions thrown by CodeA and CodeB are handled is the same.

An exception thrown in a finally block has nothing special, treat it as the exception throw by code B.


Could you elaborate? What do you mean with the exceptions are the same?
J
JHollanti

It throws an exception ;) You can catch that exception in some other catch clause.