So what is the output for following program?
public class SimpleTest
{
public static void Main(string[] args) {
Console.WriteLine(GetString());
Console.ReadLine();
}
private static string GetString() {
string str = "original string";
try {
return str;
} finally {
str = "changed in finally";
}
}
}
It looks very simple, but most of my developer friends and I all had the wrong answer. In the college, we were all told that finally block is guaranteed to be executed before a method returns, right? So it should display “changed in finally”, shouldn’t it? Unfortunately, this is a wrong answer. The program will actually print “original string”!
So, why? The answer is pretty simple. Though “return str;” is just one statement in C#, it is made of a few IL instructions. So if we have a look the complied IL code, the program first loads the “original string” and stores it in location0, then in the try block, it loads the value from lcoation0 and then store it into location1. In the finally block, it loads string “changed in finally” and store it to location0. And at the end, it loads the value from location1 onto stack for return value and exits the method.
So we can simply put, the string “original string” is prepared for return before the finally block gets executed, that is why the “original string” was returned from the method.
.method private hidebysig static string GetString() cil managed
{
// Code size 24 (0x18)
.maxstack 1
.locals init (string V_0,
string V_1)
IL_0000: nop
IL_0001: ldstr "original string"
IL_0006: stloc.0
.try
{
IL_0007: nop
IL_0008: ldloc.0
IL_0009: stloc.1
IL_000a: leave.s IL_0015
} // end .try
finally
{
IL_000c: nop
IL_000d: ldstr "changed in finally"
IL_0012: stloc.0
IL_0013: nop
IL_0014: endfinally
} // end handler
IL_0015: nop
IL_0016: ldloc.1
IL_0017: ret
} // end of method SimpleTest::GetString