views:

2418

answers:

4

I saw this tip in another question and was wondering if someone could explain to me how on earth this works? I mean, does the finally clause really execute after the return statement? How thread-unsafe is this code? Can you think of any additional hackery that can be done w.r.t. this try-finally hack?

Update 1 well, it seems as though a temporary variable is created (even though we do not see it).

+7  A: 

The finally clause executes after the return statement but before actually returning from the function. It has little to do with thread safety, I think. It is not a hack - the finally is guaranteed to always run no matter what you do in your try block or your catch block.

Otávio Décio
+44  A: 

No - at the IL level you can't return from inside an exception-handled block. It essentially stores it in a variable and returns afterwards

i.e. similar to:

int tmp;
try {
  tmp = ...
} finally {
  ...
}
return tmp;

for example (using reflector):

static int Test() {
    try {
        return SomeNumber();
    } finally {
        Foo();
    }
}

compiles to:

.method private hidebysig static int32 Test() cil managed
{
    .maxstack 1
    .locals init (
        [0] int32 CS$1$0000)
    L_0000: call int32 Program::SomeNumber()
    L_0005: stloc.0 
    L_0006: leave.s L_000e
    L_0008: call void Program::Foo()
    L_000d: endfinally 
    L_000e: ldloc.0 
    L_000f: ret 
    .try L_0000 to L_0008 finally handler L_0008 to L_000e
}

This basically declares a local variable (CS$1$0000), places the value into the variable (inside the handled block), then after exiting the block loads the variable, then returns it. Reflector renders this as:

private static int Test()
{
    int CS$1$0000;
    try
    {
        CS$1$0000 = SomeNumber();
    }
    finally
    {
        Foo();
    }
    return CS$1$0000;
}
Marc Gravell
Isn't this exactly what ocdedio said: the finally is executed after the calculation of the return value and before really returning from the funczion???
rstevens
+2  A: 

If x is a local variable, I don't see the point, as x will be effectively set to null anyway when the method is exited and the value of the return value is not null (since it was placed in the register before the call to set x to null).

I can only see doing this happening if you want to guarantee the change of the value of a field upon return (and after the return value is determined).

casperOne
Unless the local variable is also captured by a delegate :)
Jon Skeet
Umm, what happens then?
Dmitri Nesteruk
Then there is a closure, and the object can't be garbage collected, because there is still a reference.
recursive
But I still don't see why you would use a local var in a delegate unless you intended to use its value.
recursive
+35  A: 

The finally statement is executed, but the return value isn't affected. The execution order is:

  • Code before return statement is executed
  • Expression in return statement is evaluated
  • finally block is executed
  • Result evaluated in step 2 is returned

Here's a short program to demonstrate:

using System;

class Test
{
    static string x;

    static void Main()
    {
        Console.WriteLine(Method());
        Console.WriteLine(x);
    }

    static string Method()
    {
        try
        {
            x = "try";
            return x;
        }
        finally
        {
            x = "finally";
        }
    }
}

This prints "try" (because that's what's returned) and then "finally" because that's the new value of x.

Of course, if we're returning a reference to a mutable object (e.g. a StringBuilder) then any changes made to the object in the finally block will be visible on return - this hasn't affected the return value itself (which is just a reference).

Jon Skeet