Mark A. Donohoe
Mark A. Donohoe

Reputation: 30378

Do you need a catch block if all you're doing is throwing the caught exception?

I found some code where they want to propagate an exception, but they want to run some clean-up code beforehand, so naturally it uses Try/Catch/Finally. But... they aren't actually doing anything with the exception, only forwarding it on. It was my understanding in those cases the catch block isn't needed, but some say it actually is. I'm not sure those who do are correct.

Specifically, Microsoft's documentation on Try-Catch says the following...

Within a handled exception, the associated finally block is guaranteed to be run. However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. That, in turn, is dependent on how your computer is set up.

What does the computer have to do with it? With the exception (no pun intended) of using FailFast, wouldn't a further-up try-catch block which called it properly catch this exception? And if that's what they mean, man that was an awkward way of saying it!

Still, I think it proves the catch{ throw; } isn't needed, correct?

Consider the following code...

public static BitmapImage MakeBitmapImage(byte[] bytes){

    var ms = new MemoryStream(bytes);

    try{
        var bitmapImage = new BitmapImage();

        bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.StreamSource = ms;
        bitmapImage.EndInit();

        return bitmapImage;
    }
    catch{
        throw;
    }
    finally{
        ms.Close();
        ms.Dispose();
    }
}

Couldn't it just be re-written like this (with no catch block) to propagate if the bitmap can't be loaded?

public static BitmapImage MakeBitmapImage(byte[] bytes){

    var ms = new MemoryStream(bytes);

    try{
        var bitmapImage = new BitmapImage();

        bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.StreamSource = ms;
        bitmapImage.EndInit();

        return bitmapImage;
    }
    finally{
        ms.Close();
        ms.Dispose();
    }
}

Upvotes: 1

Views: 140

Answers (2)

Jeremy Lakeman
Jeremy Lakeman

Reputation: 11110

I was going to say that this is a useless noop. But after reading your linked documentation in more detail that's not quite true.

There are some situations where the runtime may terminate your process without unwinding the thread stack and running any finally blocks. Perhaps if (and only if) there is no catch handler at all. Your program is going to crash anyway, or could crash due to processing in another thread, you probably don't care about that distinction.

In dotnet core, for some kinds of failures, the runtime may call Environment.FailFast and just crash immediately. Though I thought this was only for failures that didn't have any call stack, like an exception that occurs while restoring the execution context. And again, other threads probably won't be unwound.

But for any finally block that is disposing managed resources, this is absolutely pointless. I'd only worry about a finally block that performed other external operations, like a file rename. And then you're going to need to test what the actual behaviour is anyway.

Upvotes: 2

Goigle
Goigle

Reputation: 148

I am not fully sure what the author meant, but my educated guess is that "how your computer is set up." really means "how your architecture handles unwinding." This can be different in ARM and x86.

https://github.com/dotnet/runtime/blob/master/docs/design/coreclr/botr/clr-abi.md#general-unwindframe-layout

The catch block isn't needed and neither is the try block: if they're trying to guarantee cleanup they could just use a using statement as that will work even if there is an exception.

Upvotes: 2

Related Questions