Click here to Skip to main content
Click here to Skip to main content

Scope of Return Statement

, 7 Jun 2012
Rate this:
Please Sign up or sign in to vote.
Scope of Return Statement, Reachable Code and Behavior in Finally Block

Introduction

Few weeks ago, me and my friend were discussing about functions and we came up with one question about scope and reachable code in .NET and we started understanding reachable code and found very interesting results.

Background

We will evaluate the scope of return statement in function along with try-catch. Before that, it is important to understand what the compiler does for a function having signature and returning any value before ending function. When a function has a return statement in other blocks, the compiler adds a new variable and returns that value before returning from that function.

Using the Code

Everyone knows that in try-catch block, finally block must execute before returning from the method. Then what happened with variables declared in Finally block, when are they assigned and what is the use of them.

Guess what will be the return value when calling MyFunction method in the below code?

private int MyFunction()
        {
            int i = 0;
            try
            {
                i = 1;
                throw new Exception("Int Error");
            }
            catch
            {
                i = 2;
                return i;
            }
            finally
            {
                i = 3;
                Console.Write(i.ToString());
            }
        }

If you execute the above code just after setting value of i = 2, when executing a return statement, finally block should be called. And in finally block, the value of I has been set to 3, even Debug.Print should return 3 but when you get return value of function(return i), you will get answer 2.

Guess what happened.

Let me change the existing code, let me pass a reference parameter and we will see what happens to the reference variable.

private int MyFunction(ref int passInt)
{
    int i = 0;
    try
    {
        i = 1;
        passInt = 1;
        throw new Exception("Int Error");
    }
    catch
    {
        i = 2;
        passInt = 2;
        return i;
    }
    finally
    {
        i = 3;
        passInt = 3;
        Console.Write(i.ToString());
    }
} 
In the above code, the passed parameter value becomes 3 but the return value becomes 2.

Now try to understand what happened in the previous code.

"Finally statement is always calling before returning from function", that means return statement is executed but value is yet not returned. Then in that case, even after changing the value of I in finally block, why does the return value persist the previous value.

Now go back to C++ era, and think that every function is called by a pointer and address of that function is set to callee. Now you will understand what happened.

Below is code generated by the compiler before creating IL code.

private int MyFunction()
{
    int CS$1$0000;
    int i = 0;
    try{
        i = 1;
        throw new Exception("Int Error");
    }
    catch{
        i = 2;
        CS$1$0000 = i;
    }
    finally{
    Console.Write(i.ToString());
    }
return CS$1$0000;
} 

See that return statement has been reset and return statement has been placed at the end of function. Also one variable CS$1$0000 is added to set return value.

You will see that in Catch block, the value of i is set to CS$1$0000 variable and in turn CS$1$0000 gets return from method.

The IL version of the above code will give a better explanation with stack trace; you will also get clear when you will execute IL code with IL compiler.

.method private hidebysig instance int32 MyFunction() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 i,
        [1] int32 CS$1$0000)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: nop 
    L_0004: ldc.i4.1 
    L_0005: stloc.0 
    L_0006: ldstr "Int Error"
    L_000b: newobj instance void [mscorlib]System.Exception::.ctor(string)
    L_0010: throw 
    L_0011: pop 
    L_0012: nop 
    L_0013: ldc.i4.2 
    L_0014: stloc.0 
    L_0015: ldloc.0 
    L_0016: stloc.1 
    L_0017: leave.s L_002b
    L_0019: nop 
    L_001a: ldc.i4.3 
    L_001b: stloc.0 
    L_001c: ldloca.s i
    L_001e: call instance string [mscorlib]System.Int32::ToString()
    L_0023: call void [System]System.Diagnostics.Debug::Print(string)
    L_0028: nop 
    L_0029: nop 
    L_002a: endfinally 
    L_002b: nop 
    L_002c: ldloc.1 
    L_002d: ret 
    .try L_0003 to L_0011 catch object handler L_0011 to L_0019
    .try L_0003 to L_0019 finally handler L_0019 to L_002b
}    

Summary

The compiler setting the return value before returning a method and changing into the method level variable is not affecting return value.

I may extend this tip to understand the scope of other reachable code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Joshi, Rushikesh
Technical Lead Synechron
United States United States
Having 12 Years of Software Development experience.

Comments and Discussions

 
Question[My vote of 1] All this says is that the finally block is executed after return and this is already documented! PinmemberFatCatProgrammer8-Jun-12 6:15 
AnswerRe: [My vote of 1] All this says is that the finally block is executed after return and this is already documented! PinmemberJoshi, Rushikesh8-Jun-12 7:07 
AnswerYou just "discovered" the differernce between value type and reference type? PinmemberAndreas Gieriet8-Jun-12 4:35 
GeneralRe: You just "discovered" the differernce between value type and reference type? PinmemberJoshi, Rushikesh8-Jun-12 7:52 
GeneralRe: You just "discovered" the differernce between value type and reference type? PinmemberAndreas Gieriet10-Jun-12 5:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140821.2 | Last Updated 8 Jun 2012
Article Copyright 2012 by Joshi, Rushikesh
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid