tags:

views:

819

answers:

7

I've been helping a colleague debug some strange behavior in their code. The following sample illustrates this:

static void Main(string[] args)
{
    string answer = Sample();
    Console.WriteLine(answer);
}

public static string Sample()
{
    string returnValue = "abc";

    try 
    {
         return returnValue;
    }

    catch (Exception)
    {
         throw;
    }

    finally
    {
         returnValue = "def";
    }
}

What does this sample return?

You'd think that because of the finally block, it returns "def" but in fact, it returns "abc"? I've stepped through the code and confirmed that the finally block is in fact invoked.

The real answer is that you shouldn't write code like this in the first place but I'm still puzzled as to the behaviour.

Edit: To clarify the flow based on some of the answers.

When you step through the code, the finally is executed before the return.

Duplicate of: What really happens in a try { return x; } finally { x = null; } statement?

+10  A: 

Your "finally" block is assigning a value to returnValue and not actually returning a value. The "return" has already occurred before the finally block changes the value and therefore "abc" is returned.

Whilst the code is confusing as what you've done doesn't make sense, what it's doing is correct.

Robin Day
this is wrong..If the return statement is inside a try block, the finally block, if one exists, will be executed before control returns to the calling method.
TStamper
TStamper - the return value is evaluated in the try while the variable is still "abc", then the finally executes and changes the variable assignment but NOT what has already been cached to be returned.
Chuck
true,but the wording is wrong.from what he's stating the code doesnt continue to run in the Sample function
TStamper
A: 

I'm no expert, but I would have to guess that this function returns, and then invokes finally. Since return returnValue has already been executed, it doesn't really matter what value returnValue takes on in the finally block. This behaviour kind of makes sense, because it is supposed to execute the entire try block before the finally block, and the only way it can do that is if it returns from the function like its supposed to.

Mark
+3  A: 

The finally block effectively runs after the return statement. So you've already returned the old value of abc before you go into your finally block.

(this isn't exactly how works under the hood, but it's close enough for the point here)

Wilka
+2  A: 

Found this link some time ago that deals with this very question. He goes to the trouble of showing the IL code which drives home exactly what is happening.

lJohnson
A: 

If you're really curious as to what is going on, then you can download and install Reflector. It's a fantastic tool to put into your 'bag o tricks'. It will tell you what is going on underneath the hood.

Matt Brunell
A: 

At a guess I'd say that you are determining what will be returned (a reference to the string "abc") at the point where the return statement is.

So the fact that the finally later sets that reference to refer to a different string has no effect on the returned value.

GrahamS
+1  A: 

Yes, the finally block runs after the function returns, but this doesn't matter. Remember that the return value is passed by value, so a new temporary variable is created for it at return time, thus the finally block does nothing to affect the actual return value. If you want to support the desired behavior you could use an out parameter, like so:

static void Main(string[] args)
{
    string answer;
    Sample(out answer);
    Console.WriteLine(answer);
}

public static void Sample(out string answer)
{

    try
    {
        answer = "abc";
        return;
    }

    catch (Exception)
    {
        throw;
    }

    finally
    {
        answer = "def";
    }
}

Or, you could simply move the return statement outside of the try block, like so:

static void Main(string[] args)
{
    string answer = Sample();
    Console.WriteLine(answer);
}

public static string Sample()
{
    string returnValue;
    try
    {
        returnValue = "abc";
    }

    catch (Exception)
    {
        throw;
    }

    finally
    {
        returnValue = "def";
    }

    return returnValue;
}

However, given that the finally block will always override the return value, this is a questionable design.

Wedge