2

According to MSDN and this accepted answer,

using (T resource = expression)
    embedded-statement

is translated by the compiler as the following code:

{
    T resource = expression;//Shouldn't this statement be moved inside the try block?
    try
    {
        embedded-statement
    }
    finally
    {
        if (resource != null)
             ((IDisposable)resource).Dispose();
    }
}

My question is: Why is there an extra {} around the try block? Shouldn't the first statement be moved inside the try block?

MSDN explains:

The code example earlier expands to the following code at compile time (note the extra curly braces to create the limited scope for the object):

But according to another MSDN page,

By using a finally block, you can clean up any resources that are allocated in a try block

Updated: If variable visibility is the reason, then how about we declare the variable first and assign it null, then initialize it inside the try block? Is this better than the original code?

{
    T resource = null;//Now it is visible in the try block
    try
    {
        resource =expression;// in case an exception is thrown here
        embedded-statement
    }
    finally
    {
        if (resource != null)
             ((IDisposable)resource).Dispose();
    }
}
James King
  • 1,574
  • 4
  • 19
  • 28

2 Answers2

8

Why is there an extra {} around the try block?

Because the using statement is not equivalent to:

T resource = expression;
try
{
    embedded-statement
}
finally
{
    if (resource != null)
         ((IDisposable)resource).Dispose();
}

// resource is still in scope here!

resource is still in scope after the above code, but it should not be in scope after the using statement.

Imagine if resource is also the name of a field, and you have put Console.WriteLine(resource); after the using statement. Normally, it would have printed the field, but if the using statement were replaced by the code above, then you would have printed the resource you just allocated instead!

Therefore, to ensure that the semantics remain the same, the extra {} is needed.

Shouldn't the first statement be moved inside the try block?

No. The using statement is designed to throw an exception if the initialisation of the resource fails.

By using a finally block, you can clean up any resources that are allocated in a try block

Yes, but that does not mean you have to put every resource in a try block, nor does that mean putting resources in a try block is the only way to clean them up.

If the initialisation of resource throws an exception, the resource will not be initialised, so there is no resource to be disposed of anyway - the finally block does not need to run in this case, so it is totally fine to put that line outside of the try.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

If the resource was declared in the try-block, it would not be visible in the finally block, because the scope of a variable is always limited to the actual block (and blocks nested more deeply).

The surrounding block limits the scope of the resource to this try-finally construction + declaration. Just as the scope of a resource declared in the using statement is limited to this using statement.

The documentation for try-finally (C# Reference) says:

By using a finally block, you can clean up any resources that are allocated in a try block, and you can run code even if an exception occurs in the try block. Typically, the statements of a finally block run when control leaves a try statement. The transfer of control can occur as a result of normal execution, of execution of a break, continue, goto, or return statement, or of propagation of an exception out of the try statement.

But let's make a test to see what happens. With this class

class Resource : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("disposing");
    }

    public override string ToString() => "not null";
}

and this method:

private static Resource Throw()
{
    throw new NotImplementedException();
}

let's write this test:

Resource resource = new();
try {
    using (resource = Throw()) {
        Console.WriteLine("inside using block");
    }
    Console.WriteLine("after using block");
} catch {
    Console.WriteLine($"catch: resource is {resource}");
}

The idea is that we assign a resource before the using statement and throw an exception in the declaration section of the using statement. If the hidden finally-part of the using statement was executed, then disposing should be printed. But instead, this is printed to the console:

catch: resource is not null

This shows that the using-statement does in fact not execute the finally-block, if an exception is thrown in the declaration section.

If a constructor throws an exception, then there is nothing to be disposed anyway.

A factory method should dispose any resources already allocated if an exception is thrown before returning, as this is not the caller's responsibility. The method can then either return null or throw an exception.

If things are done right, then it does not matter whether a resource is created before or inside the try part of a try-finally-statement, since there will be nothing to be disposed if exception should occur.

A try-finally-statement is often used when nothing has to be disposed (because then a using-statement could be used instead) but some state has to be reset. An example is settings the wait-cursor for a long-running operation. Wrapping the long-running operation in a try block and restoring the normal cursor in the finally block ensures that the wait cursor does not persist.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • But if an exception is thrown during the `T resource = expression;`,`(var reader = new StringReader(manyLines);) from MSDN example`, will the `finally` block still be executed? – James King Nov 29 '22 at 18:36
  • 1
    No, and that's by design. If an exception is thrown at initialization, there's nothing to dispose because nothing was assigned to the variable. – madreflection Nov 29 '22 at 18:36
  • The purpose of the finally block is to be always evaluated. Even when an exception occurs or when the try-block is left with `return`, `break`, `continue`, or `goto`. The doc for [try-finally (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally) says: *"By using a finally block, you can clean up any resources that are allocated in a try block, and you can run code even if an exception occurs in the try block."* – Olivier Jacot-Descombes Nov 29 '22 at 18:38
  • If variable visibility is the reason , then we can declare the variable first and assign it `null`, then initialize it inside the try block? Is this better than the original code? – James King Nov 29 '22 at 18:42
  • There's no point in doing that. Again, if the constructor throws, the variable will still be `null`, so the finally block has nothing to do. Why branch to code that does nothing? – madreflection Nov 29 '22 at 18:44
  • @JamesKing, I would say that it depends whether the initialization can throw or not. Constructors rarely throw. E.g. a connection will throw when you try to open it. – Olivier Jacot-Descombes Nov 29 '22 at 18:48