9

What is the difference between code like this:

string path = @"c:\users\public\test.txt";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
    file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
    Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}

finally
{
    if (file != null)
    {
        file.Close();
    }
}

and this:

string path = @"c:\users\public\test.txt";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
    file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
    Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}
if (file != null)
{
    file.Close();
}

Is really finally block necessary in this construction. Why Microsoft provided such construction? It seems to be redundant. Isn't it?

Kuba Matjanowski
  • 350
  • 3
  • 14
  • 3
    The finally block is guaranteed to run, whatever messed up in the rest of the try construction. Say Console.WriteLine raised an exception - the Close would not be run. – Andrew Morton Nov 05 '13 at 22:02
  • 1
    Rather than try to manage an instance of `StreamReader` yourself, why not wrap it in a `using` block? – 48klocs Nov 05 '13 at 22:03
  • 'using' block should be used to avoid questions like this one. This code should not have been written your way since it is overcomplicated. – Ilya Palkin Nov 05 '13 at 22:18
  • 2
    @48klocs @Ilya Palkin This code is taken from c# Reference (http://msdn.microsoft.com/en-us/library/vstudio/dszsf989.aspx). My purpose was to understand the construction. I agree that `using` block would be the better solution in this situation. – Kuba Matjanowski Nov 05 '13 at 22:27

5 Answers5

10

Imagine if some other exception occurred that you haven't handled, e.g. an ArgumentOutOfRangeException, or if you want to rethrow the exception or throw a wrapped exception from your catch block:

  1. The first block would ensure that the file is closed regardless of whether or not an exception occurred.

  2. The second block would only close the file if either no exception occurred or an IOException occurred. It does not handle any other cases.

N.B. The first block also ensures the file is closed if an exception is thrown from inside the catch block itself.

MemeDeveloper
  • 6,457
  • 2
  • 42
  • 58
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
7

The first block will close the file even if there is an uncaught exception.

The second block will close the file only if there are no exceptions, or any thrown exceptions are caught.

The first will also ensure that the file is closed if the try has a break, goto, return, continue, or any other jump construct that would cause the execution to move outside of the try block. The second doesn't, and as such it could result in the resource not being closed.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • +1 for mentioning the jumps. Personally, I tend to avoid having any such constructs within a try block, but that's good point. – p.s.w.g Nov 05 '13 at 22:09
  • @p.s.w.g It's not uncommon at all for a `try/finally` or `using` to cover the whole scope of a method. It often makes sense to have a `return` at the end to return the value. In most cases this will even be at the end of the try/using block, where the reader would expect it to be, but the expression for what is returned may use the disposable resource, so extracting it to after the end of the block really adds more complexity than it saves. – Servy Nov 05 '13 at 22:12
  • That's true, I frequently have returns inside usings, but I can usually avoid it inside a try/catch. Nothing really wrong with it, just personal preference I suppose. (although goto is bad enough to warrant it's own criticism regardless of what's around it) – p.s.w.g Nov 05 '13 at 22:15
2

In your example, if your code throws an exception other than System.IO.IOException, your cleanup code is not guaranteed to run. With the finally block, the code within it will run no matter what type of exception is thrown.

JimEvans
  • 27,201
  • 7
  • 83
  • 108
1

Imagine there was an exception inside catch{}, code inside finally would still run but if (file != null){} block will not.

coderguy123
  • 1,955
  • 1
  • 15
  • 15
1

In that case it's redundant.

It's usefull if you for example will rethrow an exception and still want some code to run after the block:

try {
  // do something dangerous
} catch(...) {
  // log the error or something
  throw; // let the exception bubble up to the caller
} finally {
  // this always runs
}
// this only runs if there was no exception

Another example is if the catch may throw an exception for a different reason:

try {
  // do something dangerous
} catch(...) {
  // handle the error
  // log the error, which may cause a different exception
} finally {
  // this runs even if the catch crashed
}
// this only runs if there was no exception, or the code in the catch worked

Simply, as code might crash for plenty of reasons you might not even know about, it's useful to put the cleanup in a finally block just to be sure that it runs whatever happens.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • What is more, if you use `return`, `break`, `goto`, `continue` in `catch` block, finally block executes anyway. – Kuba Matjanowski Nov 05 '13 at 22:33
  • @Matjanos it executes on a `goto` even? – Scott Chamberlain Nov 05 '13 at 23:04
  • 1
    @ScottChamberlain: Yes, if you step out of the `try` or `catch` block with a `goto`, the `finally` block still executes. I wasn't sure about this, but I tested it, and it does. – Guffa Nov 05 '13 at 23:36
  • I wonder, what about the reverse, what about stepping into a `try` block with a `goto`? Does the call stack get set up on steping over the `try` or would entering mid method still execute the `finally` (and the `catch` for that matter) – Scott Chamberlain Nov 05 '13 at 23:43
  • 2
    @ScottChamberlain: You can't do that. A label inside a `try` structure is not within the scope of a `goto` that is outside it. – Guffa Nov 05 '13 at 23:48
  • Why the downvote? If you don't explain what it is that you think is wrong, it can't improve the answer. – Guffa Nov 06 '13 at 18:07
  • This should be the accepted answer, since it's the only one that actually answers both the question that is asked, and also presents the reasoning behind the finally block. All other answers have the word "if" in the first sentence which isn't what was asked. – TTT Mar 30 '20 at 20:26